/wpf-winforms-maui-shared-codebase

A WPF and MAUI application with shared business logic and services.

Primary LanguageC#OtherNOASSERTION

WPF or WinForms & .NET MAUI - Share Code Between Desktop and Mobile Projects

This example uses a loosely coupled architecture to share codebase between desktop (WPF or WinForms) and mobile (.NET MAUI) UI clients. The application displays orders loaded from a Web service and allows users to generate a report. For more information, review the following blog posts:

Demo Video

Run Project

  1. Rebuild the solution.
  2. Start the WebApiService project without debugging.
  3. Start the WPFDesktopClient, WinFormsDesktopClient or MobileClient project.

Implementation Details

The following schema outlines application architecture:

Application Architecture

The application includes the following projects:

  • Client.Shared. Contains client-side services and common helpers.
  • DataModel. A model for database objects.
  • WPFDesktopClient - WPF application.
  • WinFormsDesktopClient - WinForms application.
  • MobileClient - .NET MAUI application.
  • WebApiService - a web service that handles access to a database.

Note: Although this example does not include authentication and role-based data access, you can use the free DevExpress Web API Service to generate a project with this capability.

Shared Client Classes

Desktop and mobile clients reuse the following classes:

  • OrderDataService. Incorporates logic to communicate with the Web API service.
  • ReportService. Generates a report based on the selected order.

We use Dependency Injection to introduce these services into desktop and mobile projects.

WPF

protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);
    var builder = new ContainerBuilder();
    builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
    builder.RegisterType<ReportService>().As<IReportService>().SingleInstance();
    builder.RegisterInstance<IOrderDataService>(new OrderDataService(new HttpClient() {
        BaseAddress = new Uri("https://localhost:7033/"),
        Timeout = new TimeSpan(0, 0, 10)
    }));
    IContainer container = builder.Build();
    DISource.Resolver = (type) =>
    {
        return container.Resolve(type);
    };
}

WinForms

static void Main() {
    //...
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
    builder.RegisterType<ReportService>().As<IReportService>().SingleInstance();
    builder.RegisterInstance<IOrderDataService>(new OrderDataService(new HttpClient() {
        BaseAddress = new Uri("https://localhost:7033/"),
        Timeout = new TimeSpan(0, 0, 10)
    }));
    container = builder.Build();
    MVVMContextCompositionRoot.ViewModelCreate += (s, e) =>
    {
        e.ViewModel = container.Resolve(e.RuntimeViewModelType);
    };
    //...
}

.NET MAUI

public static MauiAppBuilder RegisterViewModels(this MauiAppBuilder mauiAppBuilder) {
    mauiAppBuilder.Services.AddTransient<OrdersViewModel>();
    mauiAppBuilder.Services.AddTransient<InvoicePreviewViewModel>();
    return mauiAppBuilder;
}
public static MauiAppBuilder RegisterViews(this MauiAppBuilder mauiAppBuilder) {
    mauiAppBuilder.Services.AddTransient<OrdersPage>();
    mauiAppBuilder.Services.AddTransient<InvoiceReportPreviewPage>();
    return mauiAppBuilder;
}
public static MauiAppBuilder RegisterAppServices(this MauiAppBuilder mauiAppBuilder) {
    mauiAppBuilder.Services.AddTransient<IOrderDataService>(sp => new OrderDataService(new HttpClient(MyHttpMessageHandler.GetMessageHandler()) {
        //OS-specific URLs are used because Android and iOS emulators use different addresses to access the local machine: https://learn.microsoft.com/en-us/dotnet/maui/data-cloud/local-web-services?view=net-maui-7.0#local-machine-address
        BaseAddress = new Uri(ON.Platform(android: "https://10.0.2.2:7033/", iOS: "https://localhost:7033/")),
        Timeout = new TimeSpan(0, 0, 10)
    })); ;
    mauiAppBuilder.Services.AddTransient<IReportService, ReportService>();
    mauiAppBuilder.Services.AddTransient<INavigationService, NavigationService>();
    return mauiAppBuilder;
}

Shared Web API Service

The Web API service includes basic endpoints to retrieve orders from a database connected with Entity Framework Core:

public class OrdersController : ControllerBase {
    //...
    [HttpGet]
    public async Task<ActionResult<IEnumerable<Order>>> GetOrders() {
        return await _context.Orders.Include(order => order.Customer)
            .Include(order => order.Items)
            .ThenInclude(orderItem => orderItem.Product)
            .ToListAsync();
    }
}

Shared Model

Both client and server sides use the same model to work with business objects.

public class Customer {
    //...
}
public class Order {
    //...
}
public class Product {
    //...
}

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)