Skip to content

[API Suggestion] WinForms Application Builder API Proposal #14082

@KlausLoeffelmann

Description

@KlausLoeffelmann

Executive Summary

This proposal outlines a comprehensive modernization of Windows Forms application development by introducing an Application Builder Pattern inspired by ASP.NET Core's proven infrastructure. The goal is to bring dependency injection, modern configuration, logging, and application lifetime management to WinForms applications while maintaining full Designer compatibility.

Rationale

Windows Forms remains a critical technology for enterprise desktop applications, yet it lacks the modern development infrastructure that has made ASP.NET Core successful. Developers transitioning from .NET Framework to .NET, or building new desktop applications, face several challenges:

  1. No built-in dependency injection - forcing reliance on anti-patterns like static singletons
  2. Legacy configuration systems - XML-based settings files incompatible with cloud deployment
  3. Inconsistent application frameworks - VB and C# have different, outdated bootstrapping approaches
  4. Designer compatibility issues - making modern patterns difficult to adopt
  5. Limited Blazor Hybrid support - no clear path to integrate web technologies
  6. Missing lifecycle management - no standardized hooks for startup/shutdown operations

This proposal addresses these gaps by introducing a familiar, proven pattern that:

  • Aligns WinForms with modern .NET practices used across all other workloads
  • Maintains 100% Designer compatibility through innovative service provider assignment
  • Enables seamless migration from .NET Framework while supporting greenfield development
  • Provides a foundation for cloud-native desktop applications
  • Unifies VB and C# development experiences

Document Overview

This proposal is organized into the following sections:

  1. Modernization Goals - Detailed rationale for each feature area
  2. Core API Surface - Complete API specifications with proposed namespaces
  3. Namespace Strategy - Discussion of assembly and namespace organization
  4. Configuration Support - JSON-based configuration approach
  5. Bootstrapping Examples - C# and VB.NET samples
  6. Implementation Requirements - Runtime integration points
  7. Project & Solution Templates - Modern template ecosystem
  8. Migration Path - Guidance for existing applications

Modernization Goals

1. Dependency Injection for Migration and Modernization

Modern .NET applications rely heavily on dependency injection (DI) for:

  • Testability: Components can be easily mocked and unit tested
  • Maintainability: Loose coupling between components
  • Flexibility: Easy to swap implementations without changing consuming code
  • Cloud-Native Patterns: Aligns with microservices and modern architectural patterns

WinForms has lacked first-class DI support, forcing developers to:

  • Use static singletons (anti-pattern)
  • Manually wire dependencies
  • Struggle with Designer compatibility

Solution: Introduce WinFormsApplicationBuilder that provides DI while maintaining Designer compatibility through IServiceProviderAssignable.


2. Blazor Hybrid Integration

Modern WinForms applications need to integrate web technologies:

  • Rich UI Components: Leverage Blazor components in desktop apps
  • Code Reuse: Share components between web and desktop
  • Modern Web APIs: Utilize JavaScript libraries and frameworks

Solution: Seamless integration with BlazorWebView through service registration and DI.


3. Designer Compatibility via IServiceProviderAssignable

The WinForms Designer requires parameterless constructors, which conflicts with constructor injection.

Solution: Implement IServiceProviderAssignable interface that allows Designer-compatible controls to receive the service provider after construction:

public interface IServiceProviderAssignable : IServiceProvider
{
    /// <summary>
    ///  Sets the service provider for this component.
    /// </summary>
    IServiceProvider SetServiceProvider(object serviceProvider);
}

This enables:

  • Designer continues to work with parameterless constructors
  • Runtime injection through property/method call
  • CodeDOM serialization compatibility

4. Modern Alternative to Legacy Settings Infrastructure

The current .settings file approach has limitations:

  • XML-based, not JSON
  • Limited type support
  • No hierarchical configuration
  • No environment-based overrides
  • Incompatible with modern cloud deployment

Solution: WinFormsUserSettingsService providing:

  • JSON-based storage
  • Type-safe generic API
  • Automatic serialization/deserialization
  • Integration with IConfiguration

5. Extensible Builder Pattern with C# 14

C# 14 introduces enhanced extension method capabilities that enable:

  • Fluent builder APIs
  • Better type inference
  • More expressive configuration
  • Third-party extensibility

Solution: Builder pattern with extension methods for:

  • Custom service registration
  • Third-party library integration
  • Domain-specific configuration

6. Unified VB and C# Application Framework

Currently:

  • VB has Application Framework (with ApplicationEvents.vb)
  • C# has Application Settings generators
  • Both are inconsistent and limited

Proposal: Obsolete both and provide unified:

  • WinFormsApplicationBuilder for both languages
  • Modern configuration via appsettings.json
  • Consistent lifetime events
  • Cross-language best practices

7. Unified Logging and Services Experience

Developers need consistent logging across:

  • Development (Debug output with timestamps)
  • Production (File-based logs)
  • Integration with Application Insights, Seq, etc.

Solution: Leverage Microsoft.Extensions.Logging with WinForms-specific providers:

  • AddTimeStampedDebug(): Development logging
  • AddWinFormsFileLogger(): Production file logging
  • Standard ILogger<T> interface

8. Application Lifetime Service

Desktop applications need lifecycle hooks for:

  • Startup initialization
  • Graceful shutdown
  • Resource cleanup
  • State persistence

Solution: WinFormsApplicationLifetime providing:

  • ApplicationStarted event
  • ApplicationStopping event
  • ApplicationStopped event
  • Integration with IHostApplicationLifetime

9. Additional Desktop-Specific Benefits from ASP.NET Infrastructure

By leveraging the existing IHost infrastructure, WinForms gains:

Configuration Management

  • Hierarchical configuration (appsettings.json, environment variables, command line)
  • Environment-specific settings (Development, Staging, Production)
  • User secrets for sensitive data
  • Hot reload support

Health Checks

  • Application health monitoring
  • Dependency health checks
  • Integration with monitoring tools

Hosted Services

  • Background task execution
  • Scheduled jobs
  • Long-running operations

Options Pattern

  • Strongly-typed configuration
  • Validation at startup
  • Change notifications

Middleware Concept (WinForms Adaptation)

  • Pre/post processing of form activation
  • Global exception handling
  • Logging interceptors
  • Authorization checks

Core API Surface

Namespace: Microsoft.Extensions.WinForms

WinFormsApplication Class

namespace Microsoft.Extensions.WinForms;

/// <summary>
///  Represents a configured Windows Forms application.
/// </summary>
public sealed class WinFormsApplication : IHost, IDisposable
{
    /// <summary>
    ///  Gets the current running instance of the WinFormsApplication.
    /// </summary>
    public static WinFormsApplication? Current { get; }
    
    /// <summary>
    ///  Gets the application's configured service provider.
    /// </summary>
    public IServiceProvider Services { get; }
    
    /// <summary>
    ///  Creates a new WinFormsApplicationBuilder with pre-configured defaults.
    /// </summary>
    public static WinFormsApplicationBuilder CreateBuilder();
    
    /// <summary>
    ///  Starts the host and runs the Windows Forms application with the configured startup form.
    /// </summary>
    public void Run();
    
    /// <summary>
    ///  Starts the host and runs the Windows Forms application with the specified form type.
    /// </summary>
    public void Run<TForm>() where TForm : Form;
    
    /// <summary>
    ///  Starts the host asynchronously.
    /// </summary>
    public Task StartAsync(CancellationToken cancellationToken = default);
    
    /// <summary>
    ///  Stops the host asynchronously.
    /// </summary>
    public Task StopAsync(CancellationToken cancellationToken = default);
}

WinFormsApplicationBuilder Class

namespace Microsoft.Extensions.WinForms;

/// <summary>
///  A builder for Windows Forms applications.
/// </summary>
public sealed class WinFormsApplicationBuilder : IHostApplicationBuilder
{
    /// <summary>
    ///  Gets the collection of services for the application.
    /// </summary>
    public IServiceCollection Services { get; }
    
    /// <summary>
    ///  Gets the application configuration.
    /// </summary>
    public ConfigurationManager Configuration { get; }
    
    /// <summary>
    ///  Gets the logging builder for the application.
    /// </summary>
    public ILoggingBuilder Logging { get; }
    
    /// <summary>
    ///  Gets the host environment for the application.
    /// </summary>
    public IHostEnvironment Environment { get; }
    
    /// <summary>
    ///  Sets the startup form for the application using a form type.
    /// </summary>
    public WinFormsApplicationBuilder UseStartupForm<TForm>() where TForm : Form, new();
    
    /// <summary>
    ///  Sets an existing form instance as the startup form.
    /// </summary>
    public WinFormsApplicationBuilder UseStartupForm(Form startupForm);
    
    /// <summary>
    ///  Configures the high DPI mode for the application.
    /// </summary>
    public WinFormsApplicationBuilder UseHighDpiMode(HighDpiMode highDpiMode);
    
    /// <summary>
    ///  Configures the color mode (Dark/Light/System) for the application.
    /// </summary>
    public WinFormsApplicationBuilder UseColorMode(SystemColorMode colorMode);
    
    /// <summary>
    ///  Configures the default font for the application.
    /// </summary>
    public WinFormsApplicationBuilder UseDefaultFont(Font defaultFont);
    
    /// <summary>
    ///  Configures whether to enable visual styles.
    /// </summary>
    public WinFormsApplicationBuilder UseVisualStyles(bool enable = true);
    
    /// <summary>
    ///  Configures whether to use GDI+ text rendering (v2).
    /// </summary>
    public WinFormsApplicationBuilder UseTextRenderingV2(bool enable = true);
    
    /// <summary>
    ///  Configures whether to allow WinForms settings from appsettings.json.
    /// </summary>
    public WinFormsApplicationBuilder AllowWinFormsJsonAppSettings(bool allowJson = true);
    
    /// <summary>
    ///  Configures the application to use settings from the project file.
    /// </summary>
    public WinFormsApplicationBuilder UseProjectSettings();
    
    /// <summary>
    ///  Adds WinForms-specific debug logging.
    /// </summary>
    public WinFormsApplicationBuilder UseWinFormsDebugLogger();
    
    /// <summary>
    ///  Adds WinForms-specific release logging.
    /// </summary>
    public WinFormsApplicationBuilder UseWinFormsReleaseLogger();
    
    /// <summary>
    ///  Builds the WinFormsApplication.
    /// </summary>
    public WinFormsApplication Build();
}

WinFormsApplicationLifetime Class

namespace Microsoft.Extensions.WinForms;

/// <summary>
///  Represents the lifetime of a Windows Forms application.
/// </summary>
public sealed class WinFormsApplicationLifetime
{
    /// <summary>
    ///  Occurs when the application has been fully started.
    /// </summary>
    public event EventHandler<EventArgs>? ApplicationStarted;
    
    /// <summary>
    ///  Occurs when the application is stopping.
    /// </summary>
    public event EventHandler<EventArgs>? ApplicationStopping;
    
    /// <summary>
    ///  Occurs when the application has completely stopped.
    /// </summary>
    public event EventHandler<EventArgs>? ApplicationStopped;
}

Namespace Strategy

Assembly Organization

The proposed implementation can be organized in two potential approaches:

Option 1: Single Assembly Extension (Recommended for .NET 10+)

Add to existing System.Windows.Forms.dll:

  • Core builder types in Microsoft.Extensions.WinForms namespace
  • Service integration in System.ComponentModel namespace

Benefits:

  • Single assembly reference
  • Tight integration with existing WinForms infrastructure
  • Natural discoverability

Option 2: Separate Assembly

Create new Microsoft.Extensions.Hosting.WindowsForms.dll:

  • Contains all builder and hosting infrastructure
  • References System.Windows.Forms
  • Similar to Microsoft.AspNetCore.Components.WebView.WindowsForms

Benefits:

  • Opt-in for applications that need it
  • Easier to ship updates independently
  • Clear separation of concerns

Proposed Namespace Structure

// Core hosting infrastructure
namespace Microsoft.Extensions.WinForms
{
    public sealed class WinFormsApplication { }
    public sealed class WinFormsApplicationBuilder { }
    public sealed class WinFormsApplicationLifetime { }
    internal sealed class WinFormsApplicationOptions { }
}

// Service provider integration for Designer compatibility
namespace System.ComponentModel
{
    public interface IServiceProviderAssignable : IServiceProvider { }
}

// Extension methods for service registration
namespace Microsoft.Extensions.DependencyInjection
{
    public static class WinFormsServiceCollectionExtensions
    {
        public static IServiceCollection AddWinFormsUserSettings(this IServiceCollection services);
        public static IServiceCollection AddWinFormsExceptionHandling(this IServiceCollection services);
        public static IServiceCollection AddWinFormsDialogs(this IServiceCollection services);
    }
}

// Logging extensions
namespace Microsoft.Extensions.Logging
{
    public static class WinFormsLoggingExtensions
    {
        public static ILoggingBuilder AddDebugLogger(this ILoggingBuilder builder);
        public static ILoggingBuilder AddFileLogger(this ILoggingBuilder builder);
    }
}

// Core services and interfaces
namespace Microsoft.Extensions.WinForms.Services
{
    public interface IUserSettingsService { }
    public interface IWinFormsExceptionService { }
    public interface IWinFormsDialogService { }
}

Third-Party Extensions

Third-party libraries can extend the builder pattern through standard extension methods:

// Example: Blazor Hybrid support
namespace Microsoft.AspNetCore.Components.WebView.WindowsForms
{
    public static class BlazorWebViewServiceCollectionExtensions
    {
        public static IServiceCollection AddWindowsFormsBlazorWebView(
            this IServiceCollection services);
    }
}

// Example: AI integration
namespace Microsoft.Extensions.AI.WinForms
{
    public static class AIServiceCollectionExtensions
    {
        public static IServiceCollection AddAIChatClient(
            this IServiceCollection services);
    }
}

Design Principles

  1. Alignment with .NET conventions: Use Microsoft.Extensions.* pattern established by ASP.NET Core
  2. Minimal surface area: Keep core types focused, allow extensions via standard patterns
  3. Clear ownership: Microsoft.Extensions.WinForms clearly indicates hosting infrastructure
  4. Future-proof: Namespace structure allows for additional desktop UI framework support

Namespace: System.ComponentModel

IServiceProviderAssignable Interface

namespace System.ComponentModel;

/// <summary>
///  Represents a component that can receive a service provider after construction.
///  This enables dependency injection while maintaining Windows Forms Designer compatibility.
/// </summary>
public interface IServiceProviderAssignable : IServiceProvider
{
    /// <summary>
    ///  Sets the service provider for this component.
    /// </summary>
    /// <param name="serviceProvider">The service provider to assign.</param>
    /// <returns>The service provider for chaining.</returns>
    IServiceProvider SetServiceProvider(object serviceProvider);
}

Namespace: Microsoft.Extensions.DependencyInjection

Service Extension Methods

namespace Microsoft.Extensions.DependencyInjection;

public static class WinFormsServiceCollectionExtensions
{
    /// <summary>
    ///  Adds the WinForms user settings service to the service collection.
    /// </summary>
    public static IServiceCollection AddWinFormsUserSettings(
        this IServiceCollection services);
    
    /// <summary>
    ///  Adds the WinForms exception handling service.
    /// </summary>
    public static IServiceCollection AddWinFormsExceptionHandling(
        this IServiceCollection services);
    
    /// <summary>
    ///  Adds the WinForms dialog service for showing dialogs.
    /// </summary>
    public static IServiceCollection AddWinFormsDialogs(
        this IServiceCollection services);
    
    /// <summary>
    ///  Gets a component from the service provider.
    /// </summary>
    public static TComponent? GetComponent<TComponent>(
        this IServiceProvider serviceProvider);
    
    /// <summary>
    ///  Gets a required component from the service provider.
    /// </summary>
    public static TComponent GetRequiredComponent<TComponent>(
        this IServiceProvider serviceProvider);
}

Namespace: Microsoft.Extensions.WinForms.Services

IUserSettingsService Interface

namespace Microsoft.Extensions.WinForms.Services;

/// <summary>
///  Provides a modern, JSON-based user settings service.
/// </summary>
public interface IUserSettingsService
{
    /// <summary>
    ///  Gets a setting value with a default fallback.
    /// </summary>
    T GetSetting<T>(string key, T defaultValue);
    
    /// <summary>
    ///  Saves a setting value.
    /// </summary>
    void SaveSetting<T>(string key, T value);
    
    /// <summary>
    ///  Loads settings from disk.
    /// </summary>
    void Load();
    
    /// <summary>
    ///  Saves settings to disk.
    /// </summary>
    void Save();
    
    /// <summary>
    ///  Removes a setting.
    /// </summary>
    void Remove(string key);
}

IWinFormsExceptionService Interface

namespace Microsoft.Extensions.WinForms.Services;

/// <summary>
///  Provides centralized exception handling for WinForms applications.
/// </summary>
public interface IWinFormsExceptionService
{
    /// <summary>
    ///  Handles an exception that occurred in the application.
    /// </summary>
    void HandleException(Exception exception, bool canContinue = true);
    
    /// <summary>
    ///  Configures global exception handlers.
    /// </summary>
    void ConfigureGlobalHandlers();
}

Namespace: Microsoft.Extensions.Logging

Logging Extension Methods

namespace Microsoft.Extensions.Logging;

public static class WinFormsLoggingExtensions
{
    /// <summary>
    ///  Adds a timestamped debug logger suitable for Windows Forms development.
    /// </summary>
    public static ILoggingBuilder AddDebugLogger(
        this ILoggingBuilder builder,
        string? timeStampFormat = null,
        bool minuteSeparator = true);
    
    /// <summary>
    ///  Adds a file logger for Windows Forms applications.
    /// </summary>
    public static ILoggingBuilder AddFileLogger(
        this ILoggingBuilder builder,
        string? applicationName = null,
        string? logDirectory = null);
}

Configuration Support

appsettings.json Structure

{
  "WinForms": {
    "HighDpiMode": "PerMonitorV2",
    "ColorMode": "System",
    "UseTextRenderingV2": true,
    "UseVisualStyles": true,
    "DefaultFont": {
      "Family": "Segoe UI",
      "Size": 9.0
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  },
  "UserSettings": {
    "StoragePath": "%APPDATA%/MyApp/settings.json"
  }
}

Bootstrapping Examples

C# Example - Basic Application

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.WinForms;
using Microsoft.Extensions.Logging;

namespace MyWinFormsApp;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        // Create the application builder
        WinFormsApplicationBuilder builder = WinFormsApplication.CreateBuilder();

        // Configure services
        builder.Services.AddWinFormsUserSettings();
        builder.Services.AddWinFormsExceptionHandling();
        builder.Services.AddWinFormsDialogs();

        // Register application forms and components
        builder.Services.AddScoped<MainForm>();
        builder.Services.AddScoped<SettingsForm>();
        
        // Configure logging
        builder.Logging.AddDebugLogger();
        builder.Logging.AddFileLogger();

        // Configure WinForms-specific options
        builder.UseStartupForm<MainForm>()
               .UseHighDpiMode(HighDpiMode.PerMonitorV2)
               .UseColorMode(SystemColorMode.System)
               .UseTextRenderingV2()
               .UseVisualStyles()
               .AllowWinFormsJsonAppSettings();

        // Build and run
        WinFormsApplication app = builder.Build();
        app.Run();
    }
}

C# Example - Advanced with Blazor Hybrid

using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.WinForms;
using Microsoft.Extensions.Logging;

namespace ChattyApp;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        WinFormsApplicationBuilder builder = WinFormsApplication.CreateBuilder();

        // Core services
        builder.Services.AddWinFormsUserSettings();
        builder.Services.AddWinFormsExceptionHandling();

        // Blazor Hybrid support
        builder.Services.AddWindowsFormsBlazorWebView();

        // Application components
        builder.Services.AddScoped<FrmMain>();
        builder.Services.AddScoped<ChatViewModel>();

        // Logging
        builder.Logging.AddDebugLogger();

        // WinForms configuration
        builder.UseStartupForm<FrmMain>()
               .UseHighDpiMode(HighDpiMode.SystemAware)
               .UseColorMode(SystemColorMode.System)
               .UseTextRenderingV2()
               .UseVisualStyles();

        // Build and run
        WinFormsApplication app = builder.Build();
        app.Run();
    }
}

C# Example - Form with Dependency Injection

using System.ComponentModel;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.WinForms.Services;

namespace MyWinFormsApp;

/// <summary>
///  Main application form with dependency injection support.
/// </summary>
public partial class MainForm : Form, IServiceProviderAssignable
{
    private readonly ILogger<MainForm> _logger;
    private readonly IUserSettingsService _settings;
    private IServiceProvider? _serviceProvider;

    // Designer-compatible parameterless constructor
    public MainForm()
    {
        InitializeComponent();
    }

    // Constructor for dependency injection
    public MainForm(
        ILogger<MainForm> logger,
        IUserSettingsService settings) : this()
    {
        _logger = logger;
        _settings = settings;
        
        _logger.LogInformation("MainForm initialized with DI");
    }

    // IServiceProviderAssignable implementation
    public IServiceProvider SetServiceProvider(object serviceProvider)
    {
        _serviceProvider = (IServiceProvider)serviceProvider;
        return _serviceProvider;
    }

    public object? GetService(Type serviceType)
        => _serviceProvider?.GetService(serviceType);

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        // Load window position from settings
        if (_settings.GetSetting("WindowMaximized", false))
        {
            WindowState = FormWindowState.Maximized;
        }
        else
        {
            Location = _settings.GetSetting("WindowLocation", Location);
            Size = _settings.GetSetting("WindowSize", Size);
        }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        // Save window position
        _settings.SaveSetting("WindowMaximized", 
            WindowState == FormWindowState.Maximized);
        
        if (WindowState == FormWindowState.Normal)
        {
            _settings.SaveSetting("WindowLocation", Location);
            _settings.SaveSetting("WindowSize", Size);
        }
        
        _settings.Save();

        base.OnFormClosing(e);
    }
}

VB.NET Example - Basic Application

Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.WinForms
Imports Microsoft.Extensions.Logging

Module Program
    ''' <summary>
    '''  The main entry point for the application.
    ''' </summary>
    <STAThread()>
    Sub Main()
        ' Create the application builder
        Dim builder As WinFormsApplicationBuilder = _
            WinFormsApplication.CreateBuilder()

        ' Configure services
        builder.Services.AddWinFormsUserSettings()
        builder.Services.AddWinFormsExceptionHandling()
        builder.Services.AddWinFormsDialogs()

        ' Register application forms
        builder.Services.AddScoped(Of MainForm)()
        builder.Services.AddScoped(Of SettingsForm)()

        ' Configure logging
        builder.Logging.AddDebugLogger()
        builder.Logging.AddFileLogger()

        ' Configure WinForms-specific options
        builder.UseStartupForm(Of MainForm)() _
               .UseHighDpiMode(HighDpiMode.PerMonitorV2) _
               .UseColorMode(SystemColorMode.System) _
               .UseTextRenderingV2() _
               .UseVisualStyles() _
               .AllowWinFormsJsonAppSettings()

        ' Build and run
        Dim app As WinFormsApplication = builder.Build()
        app.Run()
    End Sub
End Module

VB.NET Example - Form with Dependency Injection

Imports System.ComponentModel
Imports Microsoft.Extensions.Logging
Imports Microsoft.Extensions.WinForms.Services

''' <summary>
'''  Main application form with dependency injection support.
''' </summary>
Public Class MainForm
    Inherits Form
    Implements IServiceProviderAssignable

    Private ReadOnly _logger As ILogger(Of MainForm)
    Private ReadOnly _settings As IUserSettingsService
    Private _serviceProvider As IServiceProvider

    ' Designer-compatible parameterless constructor
    Public Sub New()
        InitializeComponent()
    End Sub

    ' Constructor for dependency injection
    Public Sub New(logger As ILogger(Of MainForm), 
                   settings As IUserSettingsService)
        Me.New()
        _logger = logger
        _settings = settings
        
        _logger.LogInformation("MainForm initialized with DI")
    End Sub

    ' IServiceProviderAssignable implementation
    Public Function SetServiceProvider(serviceProvider As Object) _
        As IServiceProvider _
        Implements IServiceProviderAssignable.SetServiceProvider
        
        _serviceProvider = DirectCast(serviceProvider, IServiceProvider)
        Return _serviceProvider
    End Function

    Public Function GetService(serviceType As Type) As Object _
        Implements IServiceProvider.GetService
        
        Return _serviceProvider?.GetService(serviceType)
    End Function

    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        ' Load window position from settings
        If _settings.GetSetting("WindowMaximized", False) Then
            WindowState = FormWindowState.Maximized
        Else
            Location = _settings.GetSetting("WindowLocation", Location)
            Size = _settings.GetSetting("WindowSize", Size)
        End If
    End Sub

    Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
        ' Save window position
        _settings.SaveSetting("WindowMaximized", 
            WindowState = FormWindowState.Maximized)
        
        If WindowState = FormWindowState.Normal Then
            _settings.SaveSetting("WindowLocation", Location)
            _settings.SaveSetting("WindowSize", Size)
        End If
        
        _settings.Save()

        MyBase.OnFormClosing(e)
    End Sub
End Class

Project and Solution Templates

Importance of Modern Templates

The success of the WinFormsApplicationBuilder pattern depends heavily on discoverability and ease of adoption. Modern templates are essential for:

  1. Lowering the barrier to entry - Developers can start with best practices immediately
  2. Showcasing capabilities - Templates demonstrate the full potential of the new infrastructure
  3. Accelerating development - Pre-configured solutions save hours of setup time
  4. Establishing patterns - Templates serve as reference implementations
  5. Supporting migrations - Multi-process templates help bridge .NET Framework to .NET transitions

Proposed Template Ecosystem

Project Templates

Template Name Description Key Features
WinForms App (.NET) Basic application with DI Builder pattern, logging, settings service
WinForms App with Blazor Hybrid web/desktop app BlazorWebView integration, shared Razor components
WinForms System Tray App Background app with tray icon NotifyIcon setup, context menus, minimize-to-tray
WinForms Windows Service Long-running service BackgroundService integration, service lifecycle
WinForms MVVM App MVVM architecture CommunityToolkit.Mvvm, data binding, commands

Solution Templates

Template Name Description Architecture
WinForms Enterprise App Full-stack application Frontend → ViewModels → Services → Data (EF Core)
WinForms with Azure Backend Cloud-connected app Desktop client → Azure Functions → Cosmos DB/SQL
Blazor Hybrid Solution Shared UI components WinForms + Blazor WebAssembly + Shared Razor Class Library
Migration Bridge Solution .NET Framework coexistence .NET 10 frontend ↔ IPC ↔ .NET Framework backend
Multi-Tier WinForms App Layered architecture Presentation → Business Logic → Data Access → Database

Template Details

1. WinForms App (.NET) - Basic Template

Project Structure:

MyWinFormsApp/
├── Program.cs                 // Application builder setup
├── appsettings.json           // Configuration
├── MainForm.cs               // Main form with DI
├── MainForm.Designer.cs
├── Services/
│   └── IMyService.cs         // Example service interface
└── Properties/
    └── launchSettings.json   // Development settings

Program.cs:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.WinForms;
using Microsoft.Extensions.Logging;

var builder = WinFormsApplication.CreateBuilder();

// Configure services
builder.Services.AddWinFormsUserSettings();
builder.Services.AddWinFormsExceptionHandling();
builder.Services.AddScoped<MainForm>();

// Configure logging
builder.Logging.AddDebugLogger();

// Configure WinForms
builder.UseStartupForm<MainForm>()
       .UseHighDpiMode(HighDpiMode.PerMonitorV2)
       .UseColorMode(SystemColorMode.System);

var app = builder.Build();
app.Run();

2. WinForms with Blazor Template

Project Structure:

MyBlazorHybridApp/
├── Program.cs
├── MainForm.cs               // Hosts BlazorWebView
├── Components/
│   ├── App.razor            // Blazor root component
│   ├── Routes.razor
│   └── Pages/
│       ├── Index.razor
│       └── Counter.razor
├── wwwroot/
│   ├── css/
│   └── index.html
└── appsettings.json

Key Configuration:

// In Program.cs
builder.Services.AddWindowsFormsBlazorWebView();
builder.Services.AddScoped<ChatViewModel>();

3. Migration Bridge Solution Template

Solution Structure:

MyMigrationSolution/
├── MyApp.Modern/             // .NET 10 WinForms
│   ├── Program.cs
│   ├── MainForm.cs
│   └── Services/
│       └── LegacyBridge.cs  // IPC to legacy code
├── MyApp.Legacy/             // .NET Framework 4.8
│   ├── LegacyService.cs     // Existing business logic
│   └── IPC/
│       └── NamedPipeServer.cs
└── MyApp.Shared/             // .NET Standard 2.0
    └── Contracts/
        └── ILegacyService.cs

Migration Strategy:

  • Modern UI in .NET 10 with full DI and builder pattern
  • Legacy business logic remains in .NET Framework
  • Communication via named pipes or gRPC
  • Gradual migration of business logic to .NET

4. WinForms Enterprise App Solution Template

Solution Structure:

MyEnterpriseApp/
├── MyApp.Desktop/            // WinForms frontend
│   ├── Program.cs
│   ├── Views/
│   └── ViewModels/
├── MyApp.Core/               // Business logic
│   ├── Services/
│   └── Models/
├── MyApp.Data/               // Data access
│   ├── DbContext.cs
│   └── Repositories/
└── MyApp.Tests/              // Unit tests
    ├── ViewModelTests/
    └── ServiceTests/

Dependency Flow:

Desktop → Core → Data
   ↓       ↓
   └─────→ Tests

5. WinForms with Azure Backend Template

Solution Structure:

MyCloudApp/
├── MyApp.Desktop/            // WinForms client
│   ├── Program.cs
│   ├── MainForm.cs
│   └── Services/
│       └── AzureApiClient.cs
├── MyApp.Functions/          // Azure Functions
│   ├── CustomerApi.cs
│   └── host.json
├── MyApp.Shared/             // DTOs and contracts
│   └── Models/
└── deployment/
    ├── bicep/               // Infrastructure as Code
    └── pipelines/           // CI/CD

Features:

  • REST API client with HttpClient factory
  • Azure AD authentication
  • Application Insights integration
  • Offline-first data synchronization
  • Deployment scripts for Azure

6. System Tray Application Template

Key Components:

public class TrayApplicationContext : ApplicationContext
{
    private readonly NotifyIcon _notifyIcon;
    private readonly IServiceProvider _services;

    public TrayApplicationContext(IServiceProvider services)
    {
        _services = services;
        _notifyIcon = new NotifyIcon
        {
            Icon = Resources.AppIcon,
            ContextMenuStrip = CreateContextMenu(),
            Visible = true
        };
    }
    
    private ContextMenuStrip CreateContextMenu()
    {
        var menu = new ContextMenuStrip();
        menu.Items.Add("Open", null, OnOpen);
        menu.Items.Add("Settings", null, OnSettings);
        menu.Items.Add("Exit", null, OnExit);
        return menu;
    }
}

Template Discovery and Documentation

Visual Studio Integration

Templates should appear in:

  1. New Project Dialog - Filtered by "WinForms" or "Desktop"
  2. Getting Started - Featured in welcome screen
  3. Template Search - Tagged with relevant keywords (WinForms, Desktop, Blazor, Azure, MVVM)

Online Documentation

Each template should include:

  • README.md - Project overview and quick start
  • Architecture.md - Design decisions and structure explanation
  • Migration-Guide.md (for bridge templates) - Step-by-step migration process
  • Deployment.md - How to build and deploy
  • Code comments - Inline explanations of key patterns

Template Metadata

<Template>
  <Name>WinForms App (.NET)</Name>
  <Description>A modern Windows Forms application with dependency injection, logging, and configuration</Description>
  <ProjectType>CSharp</ProjectType>
  <ProjectSubType>Windows</ProjectSubType>
  <SortOrder>1000</SortOrder>
  <CreateNewFolder>true</CreateNewFolder>
  <DefaultName>MyWinFormsApp</DefaultName>
  <ProvideDefaultName>true</ProvideDefaultName>
  <LocationField>Enabled</LocationField>
  <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
  <CreateInPlace>true</CreateInPlace>
  <Icon>WinFormsIcon.png</Icon>
  <PreviewImage>WinFormsPreview.png</PreviewImage>
  <Tags>
    <Tag>WinForms</Tag>
    <Tag>Desktop</Tag>
    <Tag>DependencyInjection</Tag>
    <Tag>Modern</Tag>
  </Tags>
</Template>

Benefits of Comprehensive Template Ecosystem

  1. Reduced Learning Curve - Developers don't need to configure DI, logging, etc. from scratch
  2. Best Practices by Default - Templates encode recommended patterns
  3. Faster Time to Market - Pre-wired solutions accelerate development
  4. Migration Support - Bridge templates provide clear migration paths
  5. Ecosystem Growth - Templates encourage adoption of modern WinForms development

Implementation Requirements for WinForms Runtime

To fully support this API surface in the .NET runtime, the following components need to be added to System.Windows.Forms:

1. Core Builder Types (Namespace: Microsoft.Extensions.WinForms)

  • WinFormsApplication class
  • WinFormsApplicationBuilder class
  • WinFormsApplicationOptions class
  • WinFormsApplicationLifetime class

2. Service Provider Integration

  • IServiceProviderAssignable interface in System.ComponentModel
  • Updates to Form base class to support optional service provider injection
  • Updates to Control base class to support IServiceProviderAssignable
  • Designer code generation support for service provider assignment

3. Configuration Support

  • Integration with Microsoft.Extensions.Configuration
  • appsettings.json support with WinForms section
  • Environment-specific configuration (Development/Production)
  • User secrets support for development

4. Logging Integration

  • Integration with Microsoft.Extensions.Logging
  • Default providers for Debug output and file logging
  • Structured logging support

5. Lifetime Management

  • Integration with IHostApplicationLifetime
  • Application startup/shutdown event hooks
  • Graceful shutdown support

6. Settings Service

  • Modern replacement for ApplicationSettings
  • JSON-based storage
  • Type-safe generic API
  • Automatic serialization

Migration Path from Legacy Approaches

From VB Application Framework

Before (VB Application Framework):

' ApplicationEvents.vb
Namespace My
    Partial Friend Class MyApplication
        Private Sub MyApplication_Startup(sender As Object, 
                                          e As StartupEventArgs) _
            Handles Me.Startup
            ' Initialization code
        End Sub
    End Class
End Namespace

After (WinFormsApplicationBuilder):

' Program.vb
Module Program
    <STAThread()>
    Sub Main()
        Dim builder = WinFormsApplication.CreateBuilder()
        
        ' Configure services and startup
        builder.Services.AddSingleton(Of IMyService, MyService)()
        builder.UseStartupForm(Of MainForm)()
        
        Dim app = builder.Build()
        
        ' Subscribe to lifetime events
        Dim lifetime = app.Services.GetService(Of WinFormsApplicationLifetime)()
        AddHandler lifetime.ApplicationStarted, AddressOf OnStartup
        
        app.Run()
    End Sub
    
    Private Sub OnStartup(sender As Object, e As EventArgs)
        ' Initialization code
    End Sub
End Module

From C# Application.Run

Before:

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }
}

After:

static class Program
{
    [STAThread]
    static void Main()
    {
        var builder = WinFormsApplication.CreateBuilder();
        
        builder.UseStartupForm<MainForm>()
               .UseHighDpiMode(HighDpiMode.SystemAware)
               .UseTextRenderingV2()
               .UseVisualStyles();
        
        var app = builder.Build();
        app.Run();
    }
}

From Settings Files

Before (.settings file):

<Settings>
  <Setting Name="WindowLocation" Type="System.Drawing.Point">
    <Value>100, 100</Value>
  </Setting>
</Settings>

After (IUserSettingsService):

// Save
_settings.SaveSetting("WindowLocation", this.Location);
_settings.Save();

// Load
Location = _settings.GetSetting("WindowLocation", new Point(100, 100));

Benefits Summary

For Developers

  • Modern Patterns: DI, logging, configuration aligned with ASP.NET Core
  • Better Testing: Easy to mock and unit test
  • Cloud-Ready: Supports cloud deployment patterns
  • Blazor Integration: Seamless hybrid web/desktop apps
  • Type Safety: Strongly-typed configuration and settings

For the Ecosystem

  • Consistency: Same patterns across all .NET workloads
  • Extensibility: Third-party libraries can provide builder extensions
  • Maintainability: Clear separation of concerns
  • Documentation: Familiar patterns from web development

For Migration

  • Incremental: Can adopt features gradually
  • Backward Compatible: Doesn't break existing code
  • Designer Compatible: Works with Visual Studio Designer
  • VB and C# Unified: Same API surface for both languages

Conclusion

The WinFormsApplicationBuilder pattern brings Windows Forms development into the modern .NET era by:

  1. Providing first-class dependency injection support
  2. Enabling Blazor Hybrid scenarios
  3. Offering modern configuration and logging
  4. Unifying VB and C# application frameworks
  5. Maintaining full Designer compatibility
  6. Paving the way for cloud-native desktop applications

This proposal leverages existing, proven infrastructure from ASP.NET Core while respecting the unique requirements of desktop application development. The result is a modernized development experience that makes WinForms competitive with contemporary UI frameworks while preserving its strengths: Designer support, performance, and Windows integration.

Metadata

Metadata

Labels

api-suggestion(1) Early API idea and discussion, it is NOT ready for implementationarea-Infrastructurearea-Templatesdesign-discussionOngoing discussion about design without consensusepicGroups multiple user stories. Can be grouped under a theme.needs-area-label

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions