大家好,我是CSDN的一名技术博主。在日常开发中,无论是为了快速验证一个想法,还是为团队内部开发一个轻量级的工具,C# Windows Forms(WinForms)窗口应用程序都是一个非常高效的选择。它上手快、开发效率高,无需复杂的Web前端知识,就能构建出功能丰富的桌面应用。本文将围绕“纯代码”这一核心,抛开设计器,完全通过C#代码来构建一个功能完整的WinForms应用,并深入探讨其能实现的各种可能性。无论你是刚接触C#的新手,还是想深入了解WinForms底层机制的中级开发者,都能从本文中找到从环境搭建、核心控件创建、事件处理到高级功能集成的完整实战路径。
1. 背景与核心概念:为什么选择纯代码WinForms?
在Visual Studio等IDE中,我们通常习惯于使用拖拽控件的方式快速构建WinForms界面。这种方式直观高效,适合快速原型开发。然而,“纯代码”开发方式,即完全通过编写C#代码来创建窗口、控件并定义其行为,有着不可替代的优势:
-
深入理解框架机制
:通过代码手动创建和配置每一个控件,你能更深刻地理解WinForms的控件树结构、布局逻辑(如
Dock、Anchor属性)以及事件委托模型。这对于排查复杂界面问题至关重要。 - 实现动态界面 :当应用的界面需要根据运行时数据(如数据库查询结果、用户配置)动态生成时,拖拽设计器就无能为力了。纯代码可以让你在程序运行时灵活地创建、排列和销毁控件。
-
更好的版本控制与代码复用
:
.Designer.cs文件在团队协作时偶尔会产生合并冲突。纯代码将界面逻辑完全置于.cs文件中,更利于代码审查、版本管理和逻辑复用。 - 定制化与轻量化 :你可以完全掌控应用程序的启动流程、主循环(虽然WinForms封装了)和资源释放,构建出更精简、启动更快的应用。
WinForms应用程序的核心架构
可以简化为:一个从
System.Windows.Forms.Form
类派生的主窗口类。这个类在
Main
方法中被实例化,并通过
Application.Run(form)
启动消息循环。所有控件(如按钮、文本框)都是
System.Windows.Forms.Control
的派生类实例,它们被添加到窗体的
Controls
集合中,形成一个可视化的控件树。
2. 环境准备与版本说明
在开始编写代码之前,我们需要准备好开发环境。本文的示例基于.NET Framework和.NET Core/.NET 5+ 的WinForms项目,两种方式的核心代码是相通的。
-
开发环境
:
- 操作系统 :Windows 10 或 Windows 11。
- IDE :强烈推荐使用 Visual Studio 2022 (社区版免费)。它提供了最完善的WinForms开发支持,包括项目模板、智能提示和调试工具。也可以使用 Visual Studio Code 配合相应的C#扩展,但项目初始化和调试体验略逊于VS。
-
项目类型与.NET版本
:
- .NET Framework :选择“Windows窗体应用(.NET Framework)”,版本可以选择4.7.2或4.8。这是经典且稳定的选择,拥有最广泛的第三方控件库支持。
- .NET 6/7/8 或更高版本 :选择“Windows窗体应用”。这是微软主推的跨平台.NET(虽然WinForms本身仅在Windows上完全支持)的未来方向,性能和新特性更优。本文示例将主要基于.NET 6+ 的WinForms项目,因为这是当前推荐的技术栈。
-
创建项目
:
在Visual Studio中,新建项目,搜索“Windows Forms”,选择对应的模板,为项目命名(例如
PureCodeWinFormsDemo),然后创建。创建后,你会看到一个默认带有Form1的设计器界面。 为了实现纯代码,我们将删除或清空这个Form1的设计器文件 。 -
项目结构调整(关键步骤)
:
在解决方案资源管理器中,找到
Form1.cs,右键选择“查看代码”。你会看到Form1.cs和Form1.Designer.cs两个文件。我们不需要Form1.Designer.cs。-
右键点击
Form1.Designer.cs-> 从项目中排除。 -
或者,更彻底的方法是:直接删除
Form1.cs和Form1.Designer.cs,然后我们从头开始创建一个新的主窗体类。
-
右键点击
版本说明 :本文的代码示例在.NET 6/8的WinForms项目下测试通过。大部分核心API在.NET Framework 4.5+ 上也完全兼容。如果遇到细微差异,我会在文中指出。
3. 核心语法、配置与原理拆解
3.1 应用程序入口点:Program.cs
在.NET 6+ 的WinForms模板中,应用程序的启动逻辑被精简并放在了
Program.cs
中,采用了新的顶级语句风格。理解它是控制应用生命周期的起点。
// Program.cs
namespace PureCodeWinFormsDemo;
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread] // 必须的属性,表示线程模型为单线程单元
static void Main()
{
// 启用应用程序的视觉样式,让控件使用系统主题
ApplicationConfiguration.Initialize();
// 创建并运行主窗体实例
Application.Run(new MainForm());
}
}
-
[STAThread]:这是WinForms和Windows COM组件交互所必需的线程属性,务必保留。 -
ApplicationConfiguration.Initialize():在.NET 6+中,这个方法替代了旧的Application.EnableVisualStyles()等调用,用于进行应用程序的全局配置初始化。 -
Application.Run(Form mainForm):这是WinForms应用的核心。它启动Windows消息循环,使窗体能够接收并处理用户输入(鼠标、键盘)、系统消息等,直到主窗体关闭。
3.2 窗体的生命周期与核心属性
我们的主窗体类
MainForm
继承自
Form
。通过重写其生命周期方法和设置属性,我们可以精细控制窗体的行为。
// MainForm.cs
using System.Drawing;
using System.Windows.Forms;
namespace PureCodeWinFormsDemo;
public partial class MainForm : Form
{
public MainForm()
{
// 构造函数:在此初始化组件(控件)
InitializeComponent(); // 注意:由于我们删除了Designer,这个方法需要我们自己实现或删除调用。
// 我们将在这里调用一个自定义的初始化方法
InitializeMyComponents();
// 设置窗体基本属性
this.Text = "纯代码WinForms演示"; // 窗口标题
this.StartPosition = FormStartPosition.CenterScreen; // 启动时居中屏幕
this.Size = new Size(800, 600); // 窗体初始大小
this.MinimumSize = new Size(400, 300); // 窗体最小尺寸
}
private void InitializeMyComponents()
{
// 所有控件的创建和配置将在这里进行
}
// 生命周期方法示例:窗体加载时触发
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// 可以在这里执行依赖窗体句柄的初始化,如读取配置文件并更新UI
MessageBox.Show("窗体加载完毕!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// 生命周期方法示例:窗体关闭前触发
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
// 例如,询问用户是否确认退出
DialogResult result = MessageBox.Show(
"确定要退出程序吗?",
"退出确认",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (result == DialogResult.No)
{
e.Cancel = true; // 取消关闭操作
}
else
{
// 可以在这里执行资源清理,如保存设置、断开连接
}
}
}
关键属性 :
-
Text:窗体标题栏文本。 -
Size,Width,Height:窗体尺寸。 -
StartPosition:窗体首次显示的位置(如CenterScreen居中屏幕)。 -
FormBorderStyle:边框样式(可设置为无边框、固定对话框等)。 -
WindowState:窗口状态(正常、最大化、最小化)。
3.3 控件的创建、属性设置与事件处理
这是纯代码开发的核心。我们以创建一个按钮和一个文本框为例。
private void InitializeMyComponents()
{
// 1. 创建文本框
TextBox textBoxInput = new TextBox();
textBoxInput.Name = "textBoxInput"; // 为控件命名,便于后续查找(非必须,但建议)
textBoxInput.Location = new Point(20, 20);
textBoxInput.Size = new Size(200, 23);
textBoxInput.Text = "请输入内容";
textBoxInput.ForeColor = Color.Gray;
// 设置获得焦点和失去焦点的事件,实现Placeholder效果
textBoxInput.Enter += (sender, e) =>
{
if (textBoxInput.Text == "请输入内容")
{
textBoxInput.Text = "";
textBoxInput.ForeColor = Color.Black;
}
};
textBoxInput.Leave += (sender, e) =>
{
if (string.IsNullOrWhiteSpace(textBoxInput.Text))
{
textBoxInput.Text = "请输入内容";
textBoxInput.ForeColor = Color.Gray;
}
};
// 2. 创建按钮
Button buttonSubmit = new Button();
buttonSubmit.Name = "buttonSubmit";
buttonSubmit.Location = new Point(230, 20); // 放在文本框右侧
buttonSubmit.Size = new Size(75, 23);
buttonSubmit.Text = "提交";
buttonSubmit.BackColor = Color.LightBlue;
buttonSubmit.FlatStyle = FlatStyle.Flat;
// 3. 为按钮添加点击事件处理程序
buttonSubmit.Click += ButtonSubmit_Click;
// 4. 创建标签用于显示结果
Label labelResult = new Label();
labelResult.Name = "labelResult";
labelResult.Location = new Point(20, 60);
labelResult.Size = new Size(300, 23);
labelResult.Text = "等待输入...";
// 5. 将控件添加到窗体的控件集合中
this.Controls.Add(textBoxInput);
this.Controls.Add(buttonSubmit);
this.Controls.Add(labelResult);
}
// 按钮点击事件的处理方法
private void ButtonSubmit_Click(object? sender, EventArgs e)
{
// 通过窗体Controls集合的Find方法按名称查找控件(强类型转换)
if (this.Controls.Find("textBoxInput", true).FirstOrDefault() is TextBox tb &&
this.Controls.Find("labelResult", true).FirstOrDefault() is Label lbl)
{
string input = tb.Text.Trim();
if (input == "请输入内容" || string.IsNullOrEmpty(input))
{
lbl.Text = "输入内容不能为空!";
lbl.ForeColor = Color.Red;
}
else
{
lbl.Text = $"您输入的内容是:{input}";
lbl.ForeColor = Color.Green;
}
}
}
核心要点 :
-
创建
:使用
new关键字实例化控件。 -
属性
:设置
Location(位置)、Size(大小)、Text(文本)、BackColor(背景色)等属性来定义外观。 -
事件
:通过
+=操作符将事件处理程序(一个方法或Lambda表达式)订阅到控件的事件上(如Click、TextChanged)。 -
布局
:纯代码布局主要靠计算控件的
Location坐标。对于复杂布局,可以使用TableLayoutPanel或FlowLayoutPanel等容器控件,它们也可以通过代码创建和配置,能简化布局逻辑。 -
访问控件
:在事件处理程序中,可以通过
sender参数获取触发事件的控件,也可以通过窗体的Controls集合按名称(Find)或索引查找。
4. 完整实战案例:构建一个简易文本笔记管理器
现在,我们将综合运用以上知识,构建一个功能更丰富的应用:一个可以添加、保存、加载和清除文本笔记的简易管理器。
4.1 项目结构与设计
我们将创建以下控件:
-
RichTextBox: 用于编辑和显示笔记内容。 -
TextBox: 用于输入笔记标题。 -
ListBox: 用于显示已保存的笔记标题列表。 -
多个
Button: 用于执行新建、保存、加载、删除等操作。 -
StatusStrip和ToolStripStatusLabel: 用于显示状态信息。
4.2 核心代码实现
以下是
MainForm.cs
的完整代码:
// MainForm.cs
using System.Text;
using System.Windows.Forms;
namespace PureCodeWinFormsDemo;
public partial class MainForm : Form
{
// 用于存储笔记数据:键为标题,值为内容
private Dictionary<string, string> _notes = new Dictionary<string, string>();
private string _currentNoteTitle = string.Empty;
private RichTextBox _rtbContent;
private TextBox _txtTitle;
private ListBox _lstNotes;
private ToolStripStatusLabel _statusLabel;
public MainForm()
{
this.Text = "简易文本笔记管理器 (纯代码实现)";
this.StartPosition = FormStartPosition.CenterScreen;
this.Size = new Size(900, 650);
this.MinimumSize = new Size(700, 500);
InitializeComponents();
LoadSampleNotes(); // 加载示例笔记
UpdateStatus("就绪");
}
private void InitializeComponents()
{
// 1. 创建标题输入框
_txtTitle = new TextBox();
_txtTitle.Location = new Point(20, 20);
_txtTitle.Size = new Size(250, 23);
_txtTitle.PlaceholderText = "输入笔记标题..."; // .NET 6+ 新增属性
// 2. 创建内容编辑区 (RichTextBox)
_rtbContent = new RichTextBox();
_rtbContent.Location = new Point(300, 20);
_rtbContent.Size = new Size(550, 400);
_rtbContent.Font = new Font("微软雅黑", 10);
_rtbContent.TextChanged += (s, e) => UpdateStatus("内容已修改");
// 3. 创建笔记列表
_lstNotes = new ListBox();
_lstNotes.Location = new Point(20, 60);
_lstNotes.Size = new Size(250, 350);
_lstNotes.SelectedIndexChanged += LstNotes_SelectedIndexChanged;
// 4. 创建按钮
int buttonY = 420;
int buttonX = 300;
int buttonWidth = 100;
int buttonSpacing = 110;
CreateButton("新建笔记", buttonX, buttonY, buttonWidth, (s, e) => NewNote());
CreateButton("保存笔记", buttonX + buttonSpacing, buttonY, buttonWidth, (s, e) => SaveNote());
CreateButton("删除笔记", buttonX + buttonSpacing * 2, buttonY, buttonWidth, (s, e) => DeleteNote());
CreateButton("清空内容", buttonX + buttonSpacing * 3, buttonY, buttonWidth, (s, e) => ClearContent());
// 5. 创建状态栏
StatusStrip statusStrip = new StatusStrip();
_statusLabel = new ToolStripStatusLabel("就绪");
statusStrip.Items.Add(_statusLabel);
statusStrip.Dock = DockStyle.Bottom; // 停靠在底部
this.Controls.Add(statusStrip);
// 6. 将主要控件添加到窗体
this.Controls.AddRange(new Control[] { _txtTitle, _rtbContent, _lstNotes });
// 7. 添加标签说明
AddLabel("笔记列表:", 20, 40);
AddLabel("笔记内容:", 300, 40);
}
private void CreateButton(string text, int x, int y, int width, EventHandler clickHandler)
{
Button btn = new Button();
btn.Text = text;
btn.Location = new Point(x, y);
btn.Size = new Size(width, 30);
btn.Click += clickHandler;
this.Controls.Add(btn);
}
private void AddLabel(string text, int x, int y)
{
Label lbl = new Label();
lbl.Text = text;
lbl.Location = new Point(x, y);
lbl.AutoSize = true;
this.Controls.Add(lbl);
}
private void LoadSampleNotes()
{
_notes["欢迎使用"] = "这是一个使用纯C#代码构建的简易笔记管理器。\n你可以新建、保存和删除笔记。";
_notes["待办事项"] = "1. 学习WinForms高级控件。\n2. 尝试数据绑定。\n3. 打包发布应用。";
RefreshNoteList();
}
private void RefreshNoteList()
{
_lstNotes.Items.Clear();
_lstNotes.Items.AddRange(_notes.Keys.ToArray());
}
private void UpdateStatus(string message)
{
_statusLabel.Text = $"[{DateTime.Now:HH:mm:ss}] {message}";
}
// ============= 事件处理方法 =============
private void LstNotes_SelectedIndexChanged(object? sender, EventArgs e)
{
if (_lstNotes.SelectedItem != null)
{
string selectedTitle = _lstNotes.SelectedItem.ToString()!;
if (_notes.TryGetValue(selectedTitle, out string content))
{
_currentNoteTitle = selectedTitle;
_txtTitle.Text = selectedTitle;
_rtbContent.Text = content;
UpdateStatus($"已加载笔记: {selectedTitle}");
}
}
}
private void NewNote()
{
_currentNoteTitle = string.Empty;
_txtTitle.Clear();
_rtbContent.Clear();
UpdateStatus("已创建新笔记");
}
private void SaveNote()
{
string title = _txtTitle.Text.Trim();
string content = _rtbContent.Text;
if (string.IsNullOrWhiteSpace(title))
{
MessageBox.Show("笔记标题不能为空!", "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// 如果标题已存在,询问是否覆盖
if (_notes.ContainsKey(title) && title != _currentNoteTitle)
{
var result = MessageBox.Show($"笔记“{title}”已存在,是否覆盖?", "确认覆盖", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result != DialogResult.Yes) return;
}
_notes[title] = content;
// 如果重命名了当前笔记,删除旧的条目
if (!string.IsNullOrEmpty(_currentNoteTitle) && _currentNoteTitle != title && _notes.ContainsKey(_currentNoteTitle))
{
_notes.Remove(_currentNoteTitle);
}
_currentNoteTitle = title;
RefreshNoteList();
// 选中刚保存的笔记
_lstNotes.SelectedItem = title;
UpdateStatus($"笔记已保存: {title}");
}
private void DeleteNote()
{
if (_lstNotes.SelectedItem == null)
{
MessageBox.Show("请先选择要删除的笔记!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
string titleToDelete = _lstNotes.SelectedItem.ToString()!;
var result = MessageBox.Show($"确定要删除笔记“{titleToDelete}”吗?", "确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
_notes.Remove(titleToDelete);
if (_currentNoteTitle == titleToDelete)
{
NewNote(); // 如果删除的是当前笔记,清空编辑区
}
RefreshNoteList();
UpdateStatus($"已删除笔记: {titleToDelete}");
}
}
private void ClearContent()
{
_rtbContent.Clear();
UpdateStatus("内容已清空");
}
}
4.3 运行与验证
-
将上述代码复制到你的
MainForm.cs文件中。 -
按
F5运行程序。 - 你将看到一个带有笔记列表、编辑区和功能按钮的窗口。
-
功能测试
:
- 在标题框输入新标题,在内容区输入文字,点击“保存笔记”。观察列表是否更新。
- 点击列表中的不同笔记,查看内容是否加载。
- 尝试修改已有笔记的内容并保存。
- 选中一个笔记,点击“删除笔记”。
- 观察底部状态栏的信息变化。
4.4 结果说明
通过这个案例,我们实现了一个具备完整CRUD(增删改查)功能的桌面应用。全部界面和逻辑均由C#代码构建,没有使用任何设计器。这演示了纯代码方式如何实现:
- 动态UI构建 :所有控件在运行时创建。
- 复杂事件交互 :列表选择、按钮点击、文本修改等事件相互联动。
-
数据管理
:使用内存中的
Dictionary模拟数据持久化(实际项目可连接数据库或读写文件)。 - 状态反馈 :通过状态栏实时反馈操作结果。
5. 常见问题与排查思路
在纯代码开发WinForms时,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 控件不显示 |
1. 控件未添加到窗体的
Controls
集合。
2. 控件的
Visible
属性被设置为
false
。
3. 控件被其他控件完全遮挡。 4. 控件
Location
坐标超出窗体可视区域。
|
1. 检查代码中是否有
this.Controls.Add(myControl)
。
2. 确认控件
Visible
属性是否为
true
。
3. 检查控件层级和位置,或使用
BringToFront()
。
4. 调整
Location
或使用
Dock
、
Anchor
布局。
|
| 事件不触发 |
1. 事件处理程序未正确订阅(
+=
)。
2. 控件被禁用(
Enabled = false
)。
3. 事件在父控件中被标记为已处理(
Handled = true
)。
|
1. 检查订阅事件的代码行,确保语法正确。
2. 检查控件的
Enabled
属性。
3. 对于某些冒泡事件,检查父控件的事件处理。 |
| 布局错乱 |
1. 使用绝对坐标(
Location
)时,窗体大小改变导致布局混乱。
2. 多个控件坐标计算错误,发生重叠。 |
1. 优先使用
Dock
(停靠)和
Anchor
(锚定)属性进行相对布局。
2. 使用
TableLayoutPanel
或
FlowLayoutPanel
容器来管理复杂布局,它们也支持代码创建。
|
| 内存泄漏 |
1. 事件订阅未取消,导致窗体无法被垃圾回收(常见于长期运行的应用程序)。
2. 非托管资源(如图片、文件句柄)未及时释放。 |
1. 在窗体关闭时(
Dispose
方法或
FormClosing
事件中),对长生命周期对象的事件订阅使用
-=
取消。
2. 为持有非托管资源的类实现
IDisposable
接口,并在
using
语句中使用或手动调用
Dispose()
。
|
| 跨线程更新UI异常 |
在非UI线程(如后台线程、任务)中直接修改控件属性,会抛出
InvalidOperationException
。
|
使用控件的
Invoke
或
BeginInvoke
方法,将更新UI的操作委托回UI线程执行。
|
| 应用程序无法启动 |
1.
Program.cs
中
Main
方法缺失或签名错误。
2. 引用的DLL缺失或版本冲突。 3. 窗体构造函数中抛出未处理的异常。 |
1. 检查
Main
方法是否存在且带有
[STAThread]
属性。
2. 检查项目引用和输出目录。 3. 在构造函数和
OnLoad
等方法中添加
try-catch
进行调试。
|
6. 最佳实践与工程建议
将纯代码WinForms应用到实际项目中,需要遵循良好的工程实践以确保代码的可维护性、可扩展性和健壮性。
-
代码组织与分层 :
- 不要将所有代码堆在窗体类中 。将业务逻辑、数据访问层分离到独立的类库中。窗体类应只负责UI展示和用户交互。
-
使用“视图-逻辑”分离
:可以为复杂的窗体创建一个专门的
Presenter或ViewModel类来处理业务逻辑,窗体只负责绑定数据和触发命令。
-
资源管理与释放 :
-
实现
IDisposable:如果窗体或自定义控件使用了非托管资源(如数据库连接、文件流、GDI+对象Bitmap,Pen,Brush),务必实现IDisposable接口,并在Dispose方法中释放它们。 -
取消事件订阅
:如果窗体订阅了长生命周期对象(如静态类、单例服务)的事件,记得在窗体关闭时取消订阅(
-=),防止内存泄漏。
-
实现
-
异步编程与UI响应 :
- 执行耗时操作(如网络请求、大文件读写、复杂计算)时, 务必使用异步方法 ,避免阻塞UI线程导致界面“假死”。
-
使用
async/await配合Task.Run。在UI线程外获取数据,然后使用Control.Invoke或更现代的SynchronizationContext来安全更新UI。
private async void ButtonLoadData_Click(object sender, EventArgs e) { buttonLoadData.Enabled = false; try { // 在后台线程执行耗时操作 var data = await Task.Run(() => _dataService.FetchHeavyDataAsync()); // 回到UI线程更新控件 this.Invoke((MethodInvoker)delegate { listBoxData.DataSource = data; UpdateStatus("数据加载完成"); }); } catch (Exception ex) { MessageBox.Show($"加载失败: {ex.Message}"); } finally { buttonLoadData.Enabled = true; } } -
配置与状态持久化 :
-
使用
Settings(项目属性 -> 设置)来存储用户配置,或使用System.Text.Json/Newtonsoft.Json序列化到本地文件。 - 在窗体关闭时保存状态(如窗口位置、大小、列宽),在加载时恢复。
-
使用
-
错误处理与用户反馈 :
-
使用
try-catch块妥善处理可能抛出异常的操作。 - 向用户提供友好、明确的错误信息,而不是原始的异常堆栈。
-
使用
StatusStrip、ToolTip或非模态对话框(如自定义的Toast提示)来提供操作反馈。
-
使用
-
可访问性与国际化 :
-
为按钮、输入框等控件设置
AccessibleName和AccessibleDescription属性,提升辅助技术支持。 -
如果应用需要多语言,不要将文本硬编码在代码中。使用资源文件(
.resx)来管理字符串,便于国际化。
-
为按钮、输入框等控件设置
-
生产环境部署 :
- 发布为独立部署 :在.NET Core/5+中,可以发布为“独立”应用,包含运行时,用户无需单独安装.NET。
- 代码签名 :考虑为你的可执行文件进行代码签名,以消除Windows SmartScreen的警告。
- 安装程序 :使用WiX Toolset、InstallShield或微软MSIX打包工具创建安装程序,提供更专业的安装体验。
通过遵循这些实践,即使是纯代码编写的WinForms应用,也能具备良好的架构和可维护性,满足从小工具到复杂业务客户端的需求。从简单的示例到功能完备的管理器,纯代码方式赋予了你对应用程序每一个像素和每一行逻辑的完全控制权,是深入掌握Windows桌面开发精髓的绝佳路径。



1万+

被折叠的 条评论
为什么被折叠?



