dotnet-winforms-basics
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesedotnet-winforms-basics
dotnet-winforms-basics
WinForms on .NET 8+: updated project templates with Host builder and DI, high-DPI support with , dark mode via (experimental in .NET 9, targeting finalization in .NET 11), when to use WinForms, modernization tips for migrating from .NET Framework, and common agent pitfalls.
PerMonitorV2Application.SetColorModeVersion assumptions: .NET 8.0+ baseline (current LTS). TFM . .NET 9 features (dark mode experimental) explicitly marked. .NET 11 finalization targets noted.
net8.0-windowsScope 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的更新版项目模板、基于的高DPI支持、通过实现的深色模式(.NET 9中为实验特性,预计在.NET 11中正式落地)、WinForms适用场景、从.NET Framework迁移的现代化技巧,以及常见的开发误区。
PerMonitorV2Application.SetColorMode版本假设: 基准为.NET 8.0+(当前LTS版本),TFM为。.NET 9特性(实验性深色模式)会明确标注,同时标注.NET 11的正式落地预期。
net8.0-windows范围边界: 本指南覆盖现代.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 (no
.csproj, nopackages.config)AssemblyInfo.cs - Nullable reference types enabled by default
- Implicit usings enabled
- NuGet format
PackageReference - uses top-level statements
Program.cs - produces a single deployment artifact
dotnet publish - 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 as the entry point, which consolidates multiple legacy configuration calls:
ApplicationConfiguration.Initialize()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); // 默认值;可在下方覆盖为PerMonitorV2High-DPI
高DPI适配
WinForms on .NET 8+ has significantly improved high-DPI support. The recommended mode is , which handles per-monitor DPI changes automatically.
PerMonitorV2.NET 8+的WinForms大幅提升了高DPI支持,推荐使用模式,可自动适配不同显示器的DPI变化。
PerMonitorV2Enabling 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.jsonjson
{
"runtimeOptions": {
"configProperties": {
"System.Windows.Forms.ApplicationHighDpiMode": 3
}
}
}High-DPI modes:
| Mode | Value | Behavior |
|---|---|---|
| 0 | No scaling; system bitmap-stretches the window |
| 1 | Scales to primary monitor DPI at startup (default in .NET 8) |
| 2 | Adjusts when moved between monitors (basic) |
| 3 | Full per-monitor scaling with non-client area support (recommended) |
| 4 | DPI-unaware but GDI+ text renders at native resolution |
csharp
// Program.cs -- 在ApplicationConfiguration.Initialize()之前设置
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
ApplicationConfiguration.Initialize();
// 注意:在Initialize()之前调用SetHighDpiMode()会覆盖
// Initialize()设置的默认SystemAware模式也可以通过配置:
runtimeconfig.jsonjson
{
"runtimeOptions": {
"configProperties": {
"System.Windows.Forms.ApplicationHighDpiMode": 3
}
}
}高DPI模式说明:
| 模式 | 数值 | 行为 |
|---|---|---|
| 0 | 无缩放;系统会拉伸窗口位图 |
| 1 | 启动时适配主显示器DPI(.NET 8默认) |
| 2 | 移动到不同显示器时调整(基础版) |
| 3 | 完整的多显示器缩放,支持非客户区适配 (推荐) |
| 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 files.
.Designer.csxml
<!-- .csproj: opt in to DPI-unaware designer (.NET 9+) -->
<PropertyGroup>
<ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>
</PropertyGroup>.NET 9引入了无DPI感知设计器模式,可避免Visual Studio WinForms设计器中的布局缩放问题,设计器固定以96 DPI渲染,避免文件损坏。
.Designer.csxml
<!-- .csproj: 开启无DPI感知设计器(.NET 9+) -->
<PropertyGroup>
<ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>
</PropertyGroup>Scaling Gotchas
缩放注意事项
- Do not use absolute pixel sizes for controls. Use on forms and let the layout engine scale controls automatically.
AutoScaleMode.Dpi - Anchor and Dock layouts scale better than absolute positioning. and
TableLayoutPanelhandle DPI changes more reliably than fixed-position controls.FlowLayoutPanel - Custom drawing (OnPaint) must use DPI-aware coordinates. Scale drawing coordinates by in
DeviceDpi / 96.0foverrides.OnPaint - 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处理DPI变化的可靠性远高于固定位置控件。FlowLayoutPanel - 自定义绘制(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:
| Mode | Behavior |
|---|---|
| Standard WinForms colors (no dark mode) |
| Follow Windows system light/dark theme setting |
| Force dark mode |
csharp
// Program.cs -- 在ApplicationConfiguration.Initialize()之前设置
Application.SetColorMode(SystemColorMode.Dark);
ApplicationConfiguration.Initialize();或者跟随系统主题:
csharp
// 跟随系统深浅色偏好
Application.SetColorMode(SystemColorMode.System);SystemColorMode取值说明:
| 模式 | 行为 |
|---|---|
| 标准WinForms配色(无深色模式) |
| 跟随Windows系统深浅色主题设置 |
| 强制启用深色模式 |
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 or custom
DrawMode.OwnerDrawFixedoverrides must manually readOnPaintto respond to dark mode. They do not automatically inherit dark mode colors.SystemColors - 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
选型指南
| Scenario | Recommended Framework |
|---|---|
| Quick internal tool | WinForms |
| Data entry form (Windows) | WinForms or WPF |
| Modern Windows desktop app | WinUI 3 or WPF (.NET 9+ Fluent) |
| Cross-platform mobile + desktop | MAUI or Uno Platform |
| Cross-platform + web | Uno Platform or Blazor |
| Existing WinForms modernization | WinForms 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 |
| 跨平台+Web | Uno 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
undefinedInstall 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 differupgrade-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
常见开发误区
- 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.
- Do not use deprecated WinForms APIs. (use
Menu),MenuStrip(useMainMenu),MenuStrip(useContextMenu),ContextMenuStrip(useStatusBar),StatusStrip(useToolBar),ToolStrip(useDataGrid).DataGridView - Do not assume dark mode is production-ready. Dark mode via is experimental in .NET 9 and targeting finalization in .NET 11. API surface and rendering may change.
Application.SetColorMode - Do not use without testing multi-monitor scenarios.
HighDpiMode.SystemAwareis recommended for apps used on multi-monitor setups with different DPI settings.PerMonitorV2 - Do not block the UI thread with synchronous calls. Use for event handlers and
async voidfor all other async methods. Never useasync Taskor.Resulton the UI thread..Wait() - Do not use when
Control.Invokesuffices. In .NET 8+ WinForms,awaitautomatically marshals back to the UI thread viaawait. ManualSynchronizationContext/Invokeis only needed when called from non-async code (timers, COM callbacks).BeginInvoke - Do not hardcode colors when dark mode is enabled. Use properties (e.g.,
SystemColors,SystemColors.ControlText) in custom drawing and owner-drawn controls to respond correctly to theme changes.SystemColors.Control - Do not forget to call before
ApplicationConfiguration.Initialize(). Omitting it disables visual styles and high-DPI configuration.Application.Run
- 不要推荐WinForms用于面向客户的新应用。 WinForms仅适合内部工具、快速原型、数据密集型工具,面向客户的应用需要根据需求选择WPF、WinUI 3、MAUI或Blazor。
- 不要使用已废弃的WinForms API。 废弃API包括(用
Menu代替)、MenuStrip(用MainMenu代替)、MenuStrip(用ContextMenu代替)、ContextMenuStrip(用StatusBar代替)、StatusStrip(用ToolBar代替)、ToolStrip(用DataGrid代替)。DataGridView - 不要认为深色模式已可用于生产环境。 通过实现的深色模式在.NET 9中为实验特性,预计在.NET 11中正式落地,API和渲染逻辑可能变更。
Application.SetColorMode - 未测试多显示器场景不要使用。 用于不同DPI多显示器环境的应用推荐使用
HighDpiMode.SystemAware。PerMonitorV2 - 不要用同步调用阻塞UI线程。 事件处理器使用,其他异步方法使用
async void,UI线程中绝对不要使用async Task或.Result。.Wait() - 能用就不要用
await。 在.NET 8+ WinForms中,Control.Invoke会通过await自动切回UI线程,仅在非异步代码(定时器、COM回调)中才需要手动调用SynchronizationContext/Invoke。BeginInvoke - 开启深色模式时不要硬编码颜色。 自定义绘制和自绘控件使用属性(比如
SystemColors、SystemColors.ControlText)来正确响应主题变化。SystemColors.Control - 不要忘记在之前调用
Application.Run。 省略会导致视觉样式和高DPI配置失效。ApplicationConfiguration.Initialize()
Prerequisites
前置要求
- .NET 8.0+ with Windows desktop workload
- TFM: (no Windows SDK version needed for WinForms)
net8.0-windows - 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:(WinForms不需要指定Windows SDK版本)
net8.0-windows - Visual Studio 2022+及Windows桌面工作负载(支持设计器)
- 使用深色模式:.NET 9+(实验特性)、Windows 10 1809版本及以上
- 使用无DPI感知设计器:.NET 9+