EthicsComputerCourses
Join training +91 90532 10052

Desktop & Mobile: WPF, WinUI & .NET MAUI

C# · Lesson 41 of 43 Advanced 8 min read Updated

C# is not only for web APIs and background services. Billing software in shops, hospital front-desk systems, school ERPs and many internal tools in Indian companies are Windows desktop apps written in C#. With .NET MAUI the same language also builds Android and iOS apps.

There are several UI frameworks, and job adverts mention them by name, so this lesson gives you a map: what each one is for, the MVVM pattern they share, and a first .NET MAUI app built step by step. It is an overview; each framework deserves its own course.

The UI frameworks at a glance

FrameworkRuns onUI written inTypical use today
Windows FormsWindowsC# with a visual designerQuick internal tools, maintaining older business apps
WPFWindowsXAML + C#Rich line-of-business desktop apps
WinUI 3Windows 10 and 11XAML + C#New Windows apps with the modern Windows look
.NET MAUIAndroid, iOS, macOS, WindowsXAML or C#One code base for mobile and desktop
Blazor HybridInside MAUI, WPF or Windows FormsRazor components (HTML + C#)Reusing web UI skills and components in a native app

Some facts to remember:

  • Windows Forms and WPF run on modern .NET 10, not only on .NET Framework. They stay Windows-only; their projects target net10.0-windows. Create them with dotnet new winforms or dotnet new wpf.
  • WinUI 3 is part of the Windows App SDK, Microsoft's current platform for native Windows apps.
  • .NET MAUI (Multi-platform App UI) is the successor of Xamarin.Forms. Microsoft ended support for Xamarin in May 2024, so Xamarin apps are being moved to MAUI.
  • XAML (eXtensible Application Markup Language) is an XML language that describes the screen: buttons, text boxes, layouts. C# code supplies the behaviour.
  • Community frameworks such as Avalonia and Uno Platform also run C# XAML apps on Linux and the web. They are not made by Microsoft but are widely used.

MVVM: the pattern behind XAML apps

WPF, WinUI and MAUI all encourage MVVM (Model, View, ViewModel):

  • The Model is your data and business rules (an Invoice, a GST calculator).
  • The View is the XAML screen. It contains almost no code.
  • The ViewModel is a C# class that exposes properties for the View to show and commands for buttons to run.

The View connects to the ViewModel with data binding: Text="{Binding Amount}" keeps a text box and a property in sync. For that to work, the ViewModel must announce changes by implementing INotifyPropertyChanged. The field keyword from C# 14 keeps this short, and because a ViewModel is plain C#, you can run and test it in a console app with no UI at all:

C#
using System.ComponentModel;

var vm = new FeeViewModel();
vm.PropertyChanged += (_, e) => Console.WriteLine($"{e.PropertyName} changed");

vm.Amount = 1000m;
Console.WriteLine($"GST: {vm.Gst}");
vm.Amount = 1000m; // same value: no notification

public class FeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public decimal Amount
    {
        get;
        set
        {
            if (field == value) return;
            field = value;
            OnChanged(nameof(Amount));
            OnChanged(nameof(Gst)); // Gst depends on Amount
        }
    }

    public decimal Gst => Math.Round(Amount * 0.18m, 2); // 18% is an example rate

    private void OnChanged(string name) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
Output
Amount changed
Gst changed
GST: 180.00

When a bound control hears "Gst changed", it reads Gst again and redraws itself. Setting the same value twice raises nothing, which avoids needless redraws. In real projects many teams use the CommunityToolkit.Mvvm package, whose source generators write this notification code for you.

Walkthrough: your first .NET MAUI app

You can build MAUI apps on Windows (for Windows and Android), on a Mac (for Android, iOS and macOS) and, for Android only, on Linux. Building for iOS always needs a Mac with Xcode.

Step 1: install the MAUI workload

Terminal
dotnet workload install maui
dotnet workload list

A workload is an optional part of the SDK. On Linux the command is dotnet workload install maui-android. In Visual Studio, tick the ".NET Multi-platform App UI development" workload in the installer instead. In VS Code, install the .NET MAUI extension, which also brings C# Dev Kit. For Android you also need the Java JDK and the Android SDK; the MAUI extension and the Visual Studio installer can set them up for you.

Step 2: create the project

Terminal
dotnet new maui -n FeeCalc
cd FeeCalc

MAUI uses a single project: one .csproj that targets several platforms at once (its TargetFrameworks lists entries such as net10.0-android and net10.0-ios), with a Platforms folder for the rare platform-specific code and a Resources folder for shared images, fonts and the app icon.

Step 3: add the ViewModel

Copy the FeeViewModel class from the previous section into a new file FeeViewModel.cs, inside namespace FeeCalc;.

Step 4: describe the screen in XAML

Replace the contents of MainPage.xaml:

XML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:FeeCalc"
             x:Class="FeeCalc.MainPage"
             x:DataType="local:FeeViewModel">
    <VerticalStackLayout Padding="20" Spacing="12">
        <Label Text="Course fee (Rs)" FontSize="18" />
        <Entry Text="{Binding Amount}" Keyboard="Numeric" />
        <Label Text="{Binding Gst, StringFormat='GST: Rs {0:F2}'}" FontSize="18" />
    </VerticalStackLayout>
</ContentPage>

x:DataType tells the XAML compiler which type the bindings use, so it can check property names at build time and generate faster compiled bindings. In MainPage.xaml.cs, keep the generated InitializeComponent() call and set the binding source after it, then delete the template's counter button handler, which no longer has a button:

C#
namespace FeeCalc;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new FeeViewModel();
    }
}

Step 5: run it

Terminal
dotnet build -t:Run -f net10.0-android

This builds the Android version and starts it on a running emulator or connected phone. On Windows you can instead pick the Windows target in Visual Studio or VS Code and press F5. Type 2500 in the box and the label shows GST: Rs 450.00 immediately, with no button and no event handler: binding did the work.

Choosing a framework

  • An internal Windows tool that must be ready this week: Windows Forms or WPF.
  • A new Windows-only product with a modern look: WinUI 3 (or WPF if your team already knows it).
  • One app for Android and iOS, perhaps also desktop: .NET MAUI.
  • A team that already has Blazor web components: Blazor Hybrid inside MAUI or WPF.
  • Desktop apps that must also run on Linux: consider Avalonia or Uno Platform.

Whatever you choose, keep business rules in a separate class library (see Solutions, Projects & NuGet Packages) so the same code can serve a desktop app, a mobile app and a web API.

Common mistakes

  • Expecting WPF or Windows Forms to run on Linux or macOS. They are Windows-only, even on .NET 10.
  • Putting business logic in click handlers. Move it into ViewModels and services you can test.
  • Forgetting INotifyPropertyChanged. The screen shows the first value and never updates.
  • Blocking the UI thread. A slow .Result or Thread.Sleep in a handler freezes the window; use async/await.
  • Starting a new mobile app on Xamarin. It is out of support; use .NET MAUI.

Key points

  • Windows Forms and WPF run on modern .NET but only on Windows; WinUI 3 is the newest Windows UI platform.
  • .NET MAUI builds Android, iOS, macOS and Windows apps from a single project and replaces Xamarin.Forms.
  • XAML describes the screen; data binding connects it to C# properties.
  • MVVM separates Model, View and ViewModel; ViewModels implement INotifyPropertyChanged and can be tested without UI.
  • Install MAUI with dotnet workload install maui, create with dotnet new maui, and run with dotnet build -t:Run -f <target>.

Exercise

Extend FeeViewModel with a DiscountPercent property (default 0). Gst should now be calculated on the discounted amount, and a new read-only Total property should return the discounted amount plus GST. Raise change notifications correctly for every property that depends on the one that changed. Test it in a console app: set Amount to 2000 and DiscountPercent to 10, and print the names of changed properties and the final total.

Show solution
C#
using System.ComponentModel;

var vm = new FeeViewModel();
vm.PropertyChanged += (_, e) => Console.WriteLine($"{e.PropertyName} changed");
vm.Amount = 2000m;
vm.DiscountPercent = 10m;
Console.WriteLine($"Total: {vm.Total}");

public class FeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public decimal Amount
    {
        get;
        set { if (field != value) { field = value; RaiseAll(nameof(Amount)); } }
    }

    public decimal DiscountPercent
    {
        get;
        set { if (field != value) { field = value; RaiseAll(nameof(DiscountPercent)); } }
    }

    private decimal Discounted => Amount - Amount * DiscountPercent / 100;
    public decimal Gst => Math.Round(Discounted * 0.18m, 2);
    public decimal Total => Discounted + Gst;

    private void RaiseAll(string changed)
    {
        foreach (string name in new[] { changed, nameof(Gst), nameof(Total) })
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}
Output
Amount changed
Gst changed
Total changed
DiscountPercent changed
Gst changed
Total changed
Total: 2124.00

The discounted fee is 2000 - 200 = 1800, GST on it is 1800 x 0.18 = 324.00, so the total is 2124.00 (a decimal keeps the two decimal places that came from multiplying by 0.18m). Changing either input property raises notifications for itself and for both properties that depend on it, so every bound label on the screen stays correct.

Quick Quiz

1. Which UI framework builds Android and iOS apps from one C# project?

2. What must a ViewModel implement so that bound controls update when a property changes?

3. Which statement about WPF on .NET 10 is true?

4. What is the role of the View in MVVM?

Learn C# with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call