MVAESTECH
Guide .NET · 09 · Applications

Publié le · par Mathieu Vaes

Applications lourdes

Une appli de bureau a un ennemi unique : le thread UI bloqué. Tout le reste découle de la séparation entre affichage, état et services.

Chapitres du guide
En clair

Une appli de bureau a un seul thread qui dessine l'écran. S'il attend le réseau, la fenêtre se fige et Windows affiche « Ne répond pas ». Le pattern MVVM sépare ce qu'on voit (la View) de l'état et des actions (le ViewModel), qu'on peut tester sans écran.

Choisir la technologie Confirmé

TechnologiePlateformesPoints fortsÀ savoir
WinFormsWindowsRapide pour des outils internes, designerMVVM difficile, DPI à surveiller
WPFWindowsXAML, binding puissant, très matureRéférence pour le métier Windows en .NET 10
WinUI 3 (Windows App SDK)Windows 10/11Fluent Design natifÉcosystème plus jeune
.NET MAUIAndroid, iOS, Windows, macOSUn code pour mobile et bureauDesktop moins abouti que le mobile
Avalonia (tiers, open source)Windows, Linux, macOS, webXAML proche de WPF, vraiment multiplateformeHors Microsoft
Blazor HybridVia MAUI / WPF / WinFormsRéutilise des composants web RazorWebView embarquée

MVVM Junior

View (XAML)aucune logique métier ViewModelétat + commandes, testable Model / ServicesAPI, base locale, domaine commandesPropertyChanged appels async Binding bidirectionnel : la View observe le ViewModel, le ViewModel ignore la View
// CommunityToolkit.Mvvm : les source generators écrivent INotifyPropertyChanged et les commandes
public sealed partial class OrdersViewModel(IOrdersApi api) : ObservableObject
{
    [ObservableProperty] private bool _isBusy;
    [ObservableProperty] private string? _error;
    public ObservableCollection<OrderSummary> Orders { get; } = [];

    [RelayCommand(IncludeCancelCommand = true)]
    private async Task LoadAsync(CancellationToken ct)
    {
        IsBusy = true; Error = null;
        try
        {
            var items = await api.GetOrdersAsync(ct);   // le thread UI reste libre
            Orders.Clear();                              // on est revenu sur le thread UI
            foreach (var o in items) Orders.Add(o);
        }
        catch (OperationCanceledException) { }
        catch (HttpRequestException ex) { Error = $"Serveur injoignable : {ex.Message}"; }
        finally { IsBusy = false; }
    }
}

Le thread UI et le Dispatcher

Seul le thread qui a créé un contrôle peut le modifier. Après un await lancé depuis le thread UI, le SynchronizationContext fait reprendre la suite sur ce thread : on peut toucher l'interface directement. Depuis un autre thread (timer, socket, Task.Run), passez par Dispatcher.InvokeAsync (WPF) ou Control.Invoke (WinForms).

Hôte générique et DI dans WPF

public partial class App : Application
{
    private readonly IHost _host = Host.CreateDefaultBuilder()
        .ConfigureServices(s =>
        {
            s.AddHttpClient<IOrdersApi, OrdersApi>(c => c.BaseAddress = new("https://api.shop.fr/"))
             .AddStandardResilienceHandler();
            s.AddTransient<OrdersViewModel>();
            s.AddSingleton<MainWindow>();
        }).Build();

    protected override async void OnStartup(StartupEventArgs e)   // async void toléré : handler d'événement
    {
        await _host.StartAsync();
        _host.Services.GetRequiredService<MainWindow>().Show();
    }
}

Déploiement Confirmé

ModeMise à jourRemarque
MSIXAuto-update, Store ou intranetInstallation propre, sandbox partielle
ClickOnceAuto-update simpleSupporté sur .NET moderne, idéal en intranet
Velopack (tiers)Deltas, multiplateformeSuccesseur moderne de Squirrel
Self-contained + single-fileÀ la charge de l'appliPas de runtime à installer, binaire plus gros
Piège
  • .Result dans un handler de bouton : interblocage garanti avec le SynchronizationContext UI.
  • Modifier une ObservableCollection depuis un autre thread : exception. Revenir sur le thread UI ou utiliser BindingOperations.EnableCollectionSynchronization.
  • Fuites par événements : un ViewModel abonné à un service singleton n'est jamais collecté. Utiliser WeakReferenceMessenger.
  • Erreurs de binding silencieuses : surveillez la fenêtre Sortie de Visual Studio (Failures de binding XAML).

À retenir

  • WPF + MVVM + CommunityToolkit.Mvvm + hôte générique : socle robuste sous Windows.
  • Tout appel réseau ou disque en async, le thread UI ne doit jamais attendre.
  • Le client lourd consomme l'API comme n'importe quel client : résilience, auth OIDC (PKCE), gestion hors ligne.

Un projet .NET à cadrer ou à reprendre ?

API REST documentées et testées, applications ASP.NET MVC (y compris VB.NET), migration de services anciens vers une architecture claire : parlons de votre code.