dotnet-winforms-basics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dotnet-winforms-basics

dotnet-winforms-basics

WinForms on .NET 8+: updated project templates with Host builder and DI, high-DPI support with
PerMonitorV2
, dark mode via
Application.SetColorMode
(experimental in .NET 9, targeting finalization in .NET 11), when to use WinForms, modernization tips for migrating from .NET Framework, and common agent pitfalls.
Version assumptions: .NET 8.0+ baseline (current LTS). TFM
net8.0-windows
. .NET 9 features (dark mode experimental) explicitly marked. .NET 11 finalization targets noted.
Scope boundary: This skill owns WinForms on modern .NET patterns: project setup, high-DPI, dark mode, DI, when to use, modernization. Migration from .NET Framework to .NET 8+ is owned by [skill:dotnet-wpf-migration]. Desktop testing is owned by [skill:dotnet-ui-testing-core].
Out of scope: WinForms .NET Framework patterns (legacy) -- this skill covers .NET 8+ only. Migration guidance -- see [skill:dotnet-wpf-migration]. Desktop testing -- see [skill:dotnet-ui-testing-core]. General Native AOT patterns -- see [skill:dotnet-native-aot]. UI framework selection -- see [skill:dotnet-ui-chooser].
Cross-references: [skill:dotnet-ui-testing-core] for desktop testing, [skill:dotnet-wpf-modern] for WPF patterns, [skill:dotnet-winui] for WinUI 3 patterns, [skill:dotnet-wpf-migration] for migration guidance, [skill:dotnet-native-aot] for general AOT, [skill:dotnet-ui-chooser] for framework selection.

基于.NET 8+的WinForms开发:包含搭载Host构建器和DI的更新版项目模板、基于
PerMonitorV2
的高DPI支持、通过
Application.SetColorMode
实现的深色模式(.NET 9中为实验特性,预计在.NET 11中正式落地)、WinForms适用场景、从.NET Framework迁移的现代化技巧,以及常见的开发误区。
版本假设: 基准为.NET 8.0+(当前LTS版本),TFM为
net8.0-windows
。.NET 9特性(实验性深色模式)会明确标注,同时标注.NET 11的正式落地预期。
范围边界: 本指南覆盖现代.NET下的WinForms开发模式:项目搭建、高DPI适配、深色模式、DI、适用场景、现代化升级。从.NET Framework迁移到.NET 8+的内容归属[skill:dotnet-wpf-migration],桌面测试内容归属[skill:dotnet-ui-testing-core]。
超出范围: WinForms .NET Framework模式(遗留版本)——本指南仅覆盖.NET 8+。迁移指南参考[skill:dotnet-wpf-migration],桌面测试参考[skill:dotnet-ui-testing-core],通用Native AOT模式参考[skill:dotnet-native-aot],UI框架选型参考[skill:dotnet-ui-chooser]。
交叉引用:桌面测试参考[skill:dotnet-ui-testing-core],WPF模式参考[skill:dotnet-wpf-modern],WinUI 3模式参考[skill:dotnet-winui],迁移指南参考[skill:dotnet-wpf-migration],通用AOT参考[skill:dotnet-native-aot],框架选型参考[skill:dotnet-ui-chooser]。

.NET 8+ Differences

.NET 8+版本差异

WinForms on .NET 8+ is a significant modernization from .NET Framework WinForms, with an SDK-style project format, DI support, and updated APIs.
.NET 8+的WinForms相比.NET Framework的WinForms有非常大的现代化升级,支持SDK风格项目格式、DI、更新的API。

New Project Template

新的项目模板

xml
<!-- MyWinFormsApp.csproj (SDK-style) -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.*" />
  </ItemGroup>
</Project>
Key differences from .NET Framework WinForms:
  • SDK-style
    .csproj
    (no
    packages.config
    , no
    AssemblyInfo.cs
    )
  • Nullable reference types enabled by default
  • Implicit usings enabled
  • NuGet
    PackageReference
    format
  • Program.cs
    uses top-level statements
  • dotnet publish
    produces a single deployment artifact
  • Side-by-side .NET installation (no machine-wide framework dependency)
xml
<!-- MyWinFormsApp.csproj (SDK-style) -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.*" />
  </ItemGroup>
</Project>
和.NET Framework WinForms的核心差异:
  • SDK风格的
    .csproj
    (无
    packages.config
    、无
    AssemblyInfo.cs
  • 默认启用可空引用类型
  • 启用隐式引用
  • NuGet使用
    PackageReference
    格式
  • Program.cs
    使用顶级语句
  • dotnet publish
    生成单一部署产物
  • .NET版本并行安装(无全局框架依赖)

Host Builder Pattern

Host构建器模式

Modern WinForms apps use the generic host for dependency injection:
csharp
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

ApplicationConfiguration.Initialize();

var host = Host.CreateDefaultBuilder()
    .ConfigureServices((context, services) =>
    {
        // Services
        services.AddSingleton<IProductService, ProductService>();
        services.AddSingleton<ISettingsService, SettingsService>();

        // HTTP client
        services.AddHttpClient("api", client =>
        {
            client.BaseAddress = new Uri("https://api.example.com");
        });

        // Forms
        services.AddTransient<MainForm>();
        services.AddTransient<ProductDetailForm>();
    })
    .Build();

var mainForm = host.Services.GetRequiredService<MainForm>();
Application.Run(mainForm);
csharp
// MainForm.cs -- constructor injection
public partial class MainForm : Form
{
    private readonly IProductService _productService;
    private readonly IServiceProvider _serviceProvider;

    public MainForm(IProductService productService, IServiceProvider serviceProvider)
    {
        _productService = productService;
        _serviceProvider = serviceProvider;
        InitializeComponent();
    }

    private async void btnLoad_Click(object sender, EventArgs e)
    {
        var products = await _productService.GetProductsAsync();
        dataGridProducts.DataSource = products.ToList();
    }

    private void btnDetails_Click(object sender, EventArgs e)
    {
        var detailForm = _serviceProvider.GetRequiredService<ProductDetailForm>();
        detailForm.ShowDialog();
    }
}
现代WinForms应用使用通用主机实现依赖注入:
csharp
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

ApplicationConfiguration.Initialize();

var host = Host.CreateDefaultBuilder()
    .ConfigureServices((context, services) =>
    {
        // 服务注册
        services.AddSingleton<IProductService, ProductService>();
        services.AddSingleton<ISettingsService, SettingsService>();

        // HTTP客户端
        services.AddHttpClient("api", client =>
        {
            client.BaseAddress = new Uri("https://api.example.com");
        });

        // 窗体注册
        services.AddTransient<MainForm>();
        services.AddTransient<ProductDetailForm>();
    })
    .Build();

var mainForm = host.Services.GetRequiredService<MainForm>();
Application.Run(mainForm);
csharp
// MainForm.cs -- 构造函数注入
public partial class MainForm : Form
{
    private readonly IProductService _productService;
    private readonly IServiceProvider _serviceProvider;

    public MainForm(IProductService productService, IServiceProvider serviceProvider)
    {
        _productService = productService;
        _serviceProvider = serviceProvider;
        InitializeComponent();
    }

    private async void btnLoad_Click(object sender, EventArgs e)
    {
        var products = await _productService.GetProductsAsync();
        dataGridProducts.DataSource = products.ToList();
    }

    private void btnDetails_Click(object sender, EventArgs e)
    {
        var detailForm = _serviceProvider.GetRequiredService<ProductDetailForm>();
        detailForm.ShowDialog();
    }
}

ApplicationConfiguration.Initialize

ApplicationConfiguration.Initialize

.NET 8+ WinForms uses
ApplicationConfiguration.Initialize()
as the entry point, which consolidates multiple legacy configuration calls:
csharp
// ApplicationConfiguration.Initialize() is equivalent to:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.SystemAware);  // default; override below for PerMonitorV2

.NET 8+ WinForms使用
ApplicationConfiguration.Initialize()
作为入口,整合了多个遗留配置调用:
csharp
// ApplicationConfiguration.Initialize() 等价于以下代码:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetHighDpiMode(HighDpiMode.SystemAware);  // 默认值;可在下方覆盖为PerMonitorV2

High-DPI

高DPI适配

WinForms on .NET 8+ has significantly improved high-DPI support. The recommended mode is
PerMonitorV2
, which handles per-monitor DPI changes automatically.
.NET 8+的WinForms大幅提升了高DPI支持,推荐使用
PerMonitorV2
模式,可自动适配不同显示器的DPI变化。

Enabling PerMonitorV2

启用PerMonitorV2

csharp
// Program.cs -- set before ApplicationConfiguration.Initialize()
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
ApplicationConfiguration.Initialize();
// Note: SetHighDpiMode() called before Initialize() takes precedence
// over the default SystemAware mode set by Initialize().
Or configure via
runtimeconfig.json
:
json
{
  "runtimeOptions": {
    "configProperties": {
      "System.Windows.Forms.ApplicationHighDpiMode": 3
    }
  }
}
High-DPI modes:
ModeValueBehavior
DpiUnaware
0No scaling; system bitmap-stretches the window
SystemAware
1Scales to primary monitor DPI at startup (default in .NET 8)
PerMonitor
2Adjusts when moved between monitors (basic)
PerMonitorV2
3Full per-monitor scaling with non-client area support (recommended)
DpiUnawareGdiScaled
4DPI-unaware but GDI+ text renders at native resolution
csharp
// Program.cs -- 在ApplicationConfiguration.Initialize()之前设置
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
ApplicationConfiguration.Initialize();
// 注意:在Initialize()之前调用SetHighDpiMode()会覆盖
// Initialize()设置的默认SystemAware模式
也可以通过
runtimeconfig.json
配置:
json
{
  "runtimeOptions": {
    "configProperties": {
      "System.Windows.Forms.ApplicationHighDpiMode": 3
    }
  }
}
高DPI模式说明:
模式数值行为
DpiUnaware
0无缩放;系统会拉伸窗口位图
SystemAware
1启动时适配主显示器DPI(.NET 8默认)
PerMonitor
2移动到不同显示器时调整(基础版)
PerMonitorV2
3完整的多显示器缩放,支持非客户区适配 (推荐)
DpiUnawareGdiScaled
4无DPI感知,但GDI+文本以原生分辨率渲染

DPI-Unaware Designer Mode (.NET 9+)

无DPI感知设计器模式(.NET 9+)

.NET 9 introduces a DPI-unaware designer mode that prevents layout scaling issues in the Visual Studio WinForms designer. The designer renders at 96 DPI regardless of system DPI, preventing corrupted
.Designer.cs
files.
xml
<!-- .csproj: opt in to DPI-unaware designer (.NET 9+) -->
<PropertyGroup>
  <ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>
</PropertyGroup>
.NET 9引入了无DPI感知设计器模式,可避免Visual Studio WinForms设计器中的布局缩放问题,设计器固定以96 DPI渲染,避免
.Designer.cs
文件损坏。
xml
<!-- .csproj: 开启无DPI感知设计器(.NET 9+) -->
<PropertyGroup>
  <ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>
</PropertyGroup>

Scaling Gotchas

缩放注意事项

  • Do not use absolute pixel sizes for controls. Use
    AutoScaleMode.Dpi
    on forms and let the layout engine scale controls automatically.
  • Anchor and Dock layouts scale better than absolute positioning.
    TableLayoutPanel
    and
    FlowLayoutPanel
    handle DPI changes more reliably than fixed-position controls.
  • Custom drawing (OnPaint) must use DPI-aware coordinates. Scale drawing coordinates by
    DeviceDpi / 96.0f
    in
    OnPaint
    overrides.
  • Image resources need multiple resolutions. Provide 1x, 1.5x, and 2x versions of icons and images, or use SVG-based rendering.
csharp
// DPI-aware custom drawing
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    float scale = DeviceDpi / 96.0f;
    float fontSize = 12.0f * scale;
    using var font = new Font("Segoe UI", fontSize);
    e.Graphics.DrawString("Scaled text", font, Brushes.Black, 10 * scale, 10 * scale);
}

  • 不要为控件设置绝对像素尺寸。 窗体使用
    AutoScaleMode.Dpi
    ,让布局引擎自动缩放控件。
  • Anchor和Dock布局比绝对定位缩放效果更好。
    TableLayoutPanel
    FlowLayoutPanel
    处理DPI变化的可靠性远高于固定位置控件。
  • 自定义绘制(OnPaint)必须使用DPI感知坐标。
    OnPaint
    重写中通过
    DeviceDpi / 96.0f
    缩放绘制坐标。
  • 图片资源需要多分辨率版本。 提供1x、1.5x、2x版本的图标和图片,或者使用SVG渲染。
csharp
// 支持DPI感知的自定义绘制
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    float scale = DeviceDpi / 96.0f;
    float fontSize = 12.0f * scale;
    using var font = new Font("Segoe UI", fontSize);
    e.Graphics.DrawString("缩放后的文本", font, Brushes.Black, 10 * scale, 10 * scale);
}

Dark Mode

深色模式

WinForms dark mode is experimental in .NET 9 and is targeting finalization in .NET 11. It provides system-integrated dark mode for WinForms controls using the Windows dark mode APIs.
WinForms深色模式在** .NET 9中为实验特性 **,预计在.NET 11中正式落地,它通过Windows深色模式API为WinForms控件提供系统集成的深色模式。

Enabling Dark Mode (.NET 9+ Experimental)

启用深色模式(.NET 9+ 实验特性)

csharp
// Program.cs -- set before ApplicationConfiguration.Initialize()
Application.SetColorMode(SystemColorMode.Dark);
ApplicationConfiguration.Initialize();
Or follow system theme:
csharp
// Follow system light/dark preference
Application.SetColorMode(SystemColorMode.System);
SystemColorMode values:
ModeBehavior
Classic
Standard WinForms colors (no dark mode)
System
Follow Windows system light/dark theme setting
Dark
Force dark mode
csharp
// Program.cs -- 在ApplicationConfiguration.Initialize()之前设置
Application.SetColorMode(SystemColorMode.Dark);
ApplicationConfiguration.Initialize();
或者跟随系统主题:
csharp
// 跟随系统深浅色偏好
Application.SetColorMode(SystemColorMode.System);
SystemColorMode取值说明:
模式行为
Classic
标准WinForms配色(无深色模式)
System
跟随Windows系统深浅色主题设置
Dark
强制启用深色模式

Dark Mode Caveats

深色模式注意事项

  • Experimental status: The API surface may change before .NET 11 finalization. Do not depend on specific color values or rendering behavior in production.
  • Control coverage: Not all controls support dark mode in .NET 9. Standard controls (Button, TextBox, Label, ListBox, DataGridView) have dark mode support. Third-party and custom-drawn controls may not render correctly.
  • Owner-drawn controls: Controls using
    DrawMode.OwnerDrawFixed
    or custom
    OnPaint
    overrides must manually read
    SystemColors
    to respond to dark mode. They do not automatically inherit dark mode colors.
  • Windows version: Dark mode requires Windows 10 version 1809 (build 17763) or later.
  • .NET 11 target: Microsoft has indicated that WinForms visual styles (including dark mode) are targeting finalization in .NET 11. Plan for API stability after that release.
csharp
// Owner-drawn controls must use SystemColors for dark mode compatibility
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // Use SystemColors instead of hardcoded colors
    using var textBrush = new SolidBrush(SystemColors.ControlText);
    using var bgBrush = new SolidBrush(SystemColors.Control);
    e.Graphics.FillRectangle(bgBrush, ClientRectangle);
    e.Graphics.DrawString("Text", Font, textBrush, 10, 10);
}

  • 实验状态: API设计可能在.NET 11正式落地前变更,不要在生产环境依赖特定色值或渲染行为。
  • 控件覆盖范围: .NET 9中并非所有控件都支持深色模式,标准控件(Button、TextBox、Label、ListBox、DataGridView)已支持,第三方和自定义绘制控件可能渲染异常。
  • 自绘控件: 使用
    DrawMode.OwnerDrawFixed
    或自定义
    OnPaint
    重写的控件需要手动读取
    SystemColors
    来适配深色模式,不会自动继承深色模式配色。
  • Windows版本要求: 深色模式需要Windows 10 1809版本(内部版本17763)及以上。
  • .NET 11落地目标: 微软已明确WinForms视觉样式(包含深色模式)预计在.NET 11中正式落地,该版本发布后API才会稳定。
csharp
// 自绘控件必须使用SystemColors来兼容深色模式
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // 使用SystemColors而非硬编码颜色
    using var textBrush = new SolidBrush(SystemColors.ControlText);
    using var bgBrush = new SolidBrush(SystemColors.Control);
    e.Graphics.FillRectangle(bgBrush, ClientRectangle);
    e.Graphics.DrawString("文本", Font, textBrush, 10, 10);
}

When to Use

适用场景

WinForms is the right choice for specific scenarios. It is not a general-purpose UI framework for new customer-facing applications.
WinForms仅适合特定场景,不适合作为面向客户的新应用的通用UI框架。

Good Fit

适合场景

  • Rapid prototyping: Drag-and-drop designer for quick internal tools and proof-of-concept UIs
  • Internal enterprise tools: Line-of-business forms, data entry, CRUD applications with DataGridView
  • Simple Windows-only utilities: System tray apps, configuration tools, diagnostics dashboards
  • Existing WinForms maintenance: Modernizing existing .NET Framework WinForms apps to .NET 8+
  • Data-heavy tabular UIs: DataGridView with virtual mode handles millions of rows efficiently
  • 快速原型开发: 拖拽式设计器适合快速开发内部工具和概念验证UI
  • 内部企业工具: 业务线表单、数据录入、搭载DataGridView的CRUD应用
  • 简单的Windows专属工具: 系统托盘应用、配置工具、诊断仪表盘
  • 现有WinForms维护: 将已有的.NET Framework WinForms应用现代化升级到.NET 8+
  • 数据密集型表格UI: 开启虚拟模式的DataGridView可以高效处理数百万行数据

Not a Good Fit

不适合场景

  • New customer-facing applications: Use WPF (rich Windows desktop), WinUI 3 (modern Windows), MAUI (cross-platform), or Blazor (web)
  • Complex custom UI: WinForms controls are limited in styling; WPF or WinUI provide rich templating
  • Cross-platform requirements: WinForms is Windows-only; use MAUI or Uno Platform
  • Accessibility-first applications: WPF and WinUI have better accessibility APIs and screen reader support
  • Touch-optimized interfaces: WinForms was designed for mouse/keyboard; WinUI or MAUI handle touch better
  • 面向客户的新应用: 选择WPF(丰富的Windows桌面能力)、WinUI 3(现代Windows体验)、MAUI(跨平台)或Blazor(Web)
  • 复杂自定义UI: WinForms控件的样式能力有限,WPF或WinUI提供丰富的模板能力
  • 跨平台需求: WinForms仅支持Windows,选择MAUI或Uno Platform
  • 优先考虑无障碍的应用: WPF和WinUI有更完善的无障碍API和屏幕阅读器支持
  • 触摸优化界面: WinForms为鼠标/键盘操作设计,WinUI或MAUI的触摸适配更好

Decision Guidance

选型指南

ScenarioRecommended Framework
Quick internal toolWinForms
Data entry form (Windows)WinForms or WPF
Modern Windows desktop appWinUI 3 or WPF (.NET 9+ Fluent)
Cross-platform mobile + desktopMAUI or Uno Platform
Cross-platform + webUno Platform or Blazor
Existing WinForms modernizationWinForms on .NET 8+
For the full framework decision tree, see [skill:dotnet-ui-chooser].

场景推荐框架
快速开发内部工具WinForms
Windows端数据录入表单WinForms或WPF
现代Windows桌面应用WinUI 3或WPF(.NET 9+ Fluent风格)
跨平台移动端+桌面端MAUI或Uno Platform
跨平台+WebUno Platform或Blazor
现有WinForms应用现代化升级基于.NET 8+的WinForms
完整的框架选型决策树参考[skill:dotnet-ui-chooser]。

Modernization Tips

现代化升级技巧

Tips for modernizing existing .NET Framework WinForms applications to .NET 8+.
将现有.NET Framework WinForms应用升级到.NET 8+的技巧。

Add Dependency Injection

引入依赖注入

Replace static references and singletons with constructor injection via Host builder (see .NET 8+ Differences section above).
Before (legacy pattern):
csharp
// Anti-pattern: static service references
public partial class MainForm : Form
{
    private void btnLoad_Click(object sender, EventArgs e)
    {
        var products = ProductService.Instance.GetProducts();
        dataGridProducts.DataSource = products;
    }
}
After (modern pattern):
csharp
// Modern: constructor injection
public partial class MainForm : Form
{
    private readonly IProductService _productService;

    public MainForm(IProductService productService)
    {
        _productService = productService;
        InitializeComponent();
    }

    private async void btnLoad_Click(object sender, EventArgs e)
    {
        var products = await _productService.GetProductsAsync();
        dataGridProducts.DataSource = products.ToList();
    }
}
通过Host构建器将静态引用和单例替换为构造函数注入(参考上文.NET 8+版本差异章节)。
之前(遗留模式):
csharp
// 反模式:静态服务引用
public partial class MainForm : Form
{
    private void btnLoad_Click(object sender, EventArgs e)
    {
        var products = ProductService.Instance.GetProducts();
        dataGridProducts.DataSource = products;
    }
}
之后(现代模式):
csharp
// 现代模式:构造函数注入
public partial class MainForm : Form
{
    private readonly IProductService _productService;

    public MainForm(IProductService productService)
    {
        _productService = productService;
        InitializeComponent();
    }

    private async void btnLoad_Click(object sender, EventArgs e)
    {
        var products = await _productService.GetProductsAsync();
        dataGridProducts.DataSource = products.ToList();
    }
}

Use Async Patterns

使用异步模式

Replace synchronous blocking calls with async/await to keep the UI responsive:
csharp
// Before: blocks UI thread
private void btnSave_Click(object sender, EventArgs e)
{
    var client = new HttpClient();
    var result = client.PostAsync(url, content).Result; // BLOCKS UI
    MessageBox.Show("Saved!");
}

// After: async keeps UI responsive
private async void btnSave_Click(object sender, EventArgs e)
{
    btnSave.Enabled = false;
    try
    {
        var result = await _httpClient.PostAsync(url, content);
        result.EnsureSuccessStatusCode();
        MessageBox.Show("Saved!");
    }
    catch (HttpRequestException ex)
    {
        MessageBox.Show($"Error: {ex.Message}");
    }
    finally
    {
        btnSave.Enabled = true;
    }
}
将同步阻塞调用替换为async/await,保持UI响应:
csharp
// 之前:阻塞UI线程
private void btnSave_Click(object sender, EventArgs e)
{
    var client = new HttpClient();
    var result = client.PostAsync(url, content).Result; // 阻塞UI
    MessageBox.Show("保存成功!");
}

// 之后:异步保持UI响应
private async void btnSave_Click(object sender, EventArgs e)
{
    btnSave.Enabled = false;
    try
    {
        var result = await _httpClient.PostAsync(url, content);
        result.EnsureSuccessStatusCode();
        MessageBox.Show("保存成功!");
    }
    catch (HttpRequestException ex)
    {
        MessageBox.Show($"错误: {ex.Message}");
    }
    finally
    {
        btnSave.Enabled = true;
    }
}

Convert to .NET 8+

迁移到.NET 8+

Use the .NET Upgrade Assistant for automated migration:
bash
undefined
使用.NET升级助手实现自动化迁移:
bash
undefined

Install upgrade assistant

安装升级助手

dotnet tool install -g upgrade-assistant
dotnet tool install -g upgrade-assistant

Analyze the project

分析项目

upgrade-assistant analyze MyWinFormsApp.csproj
upgrade-assistant analyze MyWinFormsApp.csproj

Upgrade the project

升级项目

upgrade-assistant upgrade MyWinFormsApp.csproj

**Common migration issues:**
- `App.config` settings need manual migration to `appsettings.json` or Host builder configuration
- `My.Settings` (VB.NET) and `Settings.settings` need manual migration
- Third-party controls may not have .NET 8 compatible versions
- Designer-generated code in `.Designer.cs` files usually migrates cleanly
- COM interop (`System.Runtime.InteropServices`) syntax may differ
upgrade-assistant upgrade MyWinFormsApp.csproj

**常见迁移问题:**
- `App.config`设置需要手动迁移到`appsettings.json`或Host构建器配置
- `My.Settings`(VB.NET)和`Settings.settings`需要手动迁移
- 第三方控件可能没有兼容.NET 8的版本
- `.Designer.cs`中的设计器生成代码通常可以正常迁移
- COM互操作(`System.Runtime.InteropServices`)语法可能有差异

Adopt Modern C# Features

采用现代C#特性

csharp
// File-scoped namespaces
namespace MyApp.Forms;

// Null-conditional event invocation
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));

// Collection expressions
var columns = new[] { "Name", "Price", "Category" };

// Primary constructors for services (C# 12)
public class ProductService(HttpClient httpClient) : IProductService
{
    public async Task<List<Product>> GetProductsAsync()
        => await httpClient.GetFromJsonAsync<List<Product>>("/products") ?? [];
}

csharp
// 文件级命名空间
namespace MyApp.Forms;

// 空条件事件触发
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));

// 集合表达式
var columns = new[] { "名称", "价格", "分类" };

// 服务主构造函数(C# 12)
public class ProductService(HttpClient httpClient) : IProductService
{
    public async Task<List<Product>> GetProductsAsync()
        => await httpClient.GetFromJsonAsync<List<Product>>("/products") ?? [];
}

Agent Gotchas

常见开发误区

  1. Do not recommend WinForms for new customer-facing applications. WinForms is appropriate for internal tools, rapid prototyping, and data-centric utilities. For customer-facing apps, recommend WPF, WinUI 3, MAUI, or Blazor depending on requirements.
  2. Do not use deprecated WinForms APIs.
    Menu
    (use
    MenuStrip
    ),
    MainMenu
    (use
    MenuStrip
    ),
    ContextMenu
    (use
    ContextMenuStrip
    ),
    StatusBar
    (use
    StatusStrip
    ),
    ToolBar
    (use
    ToolStrip
    ),
    DataGrid
    (use
    DataGridView
    ).
  3. Do not assume dark mode is production-ready. Dark mode via
    Application.SetColorMode
    is experimental in .NET 9 and targeting finalization in .NET 11. API surface and rendering may change.
  4. Do not use
    HighDpiMode.SystemAware
    without testing multi-monitor scenarios.
    PerMonitorV2
    is recommended for apps used on multi-monitor setups with different DPI settings.
  5. Do not block the UI thread with synchronous calls. Use
    async void
    for event handlers and
    async Task
    for all other async methods. Never use
    .Result
    or
    .Wait()
    on the UI thread.
  6. Do not use
    Control.Invoke
    when
    await
    suffices.
    In .NET 8+ WinForms,
    await
    automatically marshals back to the UI thread via
    SynchronizationContext
    . Manual
    Invoke
    /
    BeginInvoke
    is only needed when called from non-async code (timers, COM callbacks).
  7. Do not hardcode colors when dark mode is enabled. Use
    SystemColors
    properties (e.g.,
    SystemColors.ControlText
    ,
    SystemColors.Control
    ) in custom drawing and owner-drawn controls to respond correctly to theme changes.
  8. Do not forget to call
    ApplicationConfiguration.Initialize()
    before
    Application.Run
    .
    Omitting it disables visual styles and high-DPI configuration.

  1. 不要推荐WinForms用于面向客户的新应用。 WinForms仅适合内部工具、快速原型、数据密集型工具,面向客户的应用需要根据需求选择WPF、WinUI 3、MAUI或Blazor。
  2. 不要使用已废弃的WinForms API。 废弃API包括
    Menu
    (用
    MenuStrip
    代替)、
    MainMenu
    (用
    MenuStrip
    代替)、
    ContextMenu
    (用
    ContextMenuStrip
    代替)、
    StatusBar
    (用
    StatusStrip
    代替)、
    ToolBar
    (用
    ToolStrip
    代替)、
    DataGrid
    (用
    DataGridView
    代替)。
  3. 不要认为深色模式已可用于生产环境。 通过
    Application.SetColorMode
    实现的深色模式在.NET 9中为实验特性,预计在.NET 11中正式落地,API和渲染逻辑可能变更。
  4. 未测试多显示器场景不要使用
    HighDpiMode.SystemAware
    用于不同DPI多显示器环境的应用推荐使用
    PerMonitorV2
  5. 不要用同步调用阻塞UI线程。 事件处理器使用
    async void
    ,其他异步方法使用
    async Task
    ,UI线程中绝对不要使用
    .Result
    .Wait()
  6. 能用
    await
    就不要用
    Control.Invoke
    在.NET 8+ WinForms中,
    await
    会通过
    SynchronizationContext
    自动切回UI线程,仅在非异步代码(定时器、COM回调)中才需要手动调用
    Invoke
    /
    BeginInvoke
  7. 开启深色模式时不要硬编码颜色。 自定义绘制和自绘控件使用
    SystemColors
    属性(比如
    SystemColors.ControlText
    SystemColors.Control
    )来正确响应主题变化。
  8. 不要忘记在
    Application.Run
    之前调用
    ApplicationConfiguration.Initialize()
    省略会导致视觉样式和高DPI配置失效。

Prerequisites

前置要求

  • .NET 8.0+ with Windows desktop workload
  • TFM:
    net8.0-windows
    (no Windows SDK version needed for WinForms)
  • Visual Studio 2022+ with Windows desktop workload (for designer support)
  • For dark mode: .NET 9+ (experimental), Windows 10 version 1809+
  • For DPI-unaware designer: .NET 9+

  • .NET 8.0+及Windows桌面工作负载
  • TFM:
    net8.0-windows
    (WinForms不需要指定Windows SDK版本)
  • Visual Studio 2022+及Windows桌面工作负载(支持设计器)
  • 使用深色模式:.NET 9+(实验特性)、Windows 10 1809版本及以上
  • 使用无DPI感知设计器:.NET 9+

References

参考资料