写在前面
承接篇1。本篇拆解 WinForms 的代码组织核心:设计器生成的
.Designer.cs、partial class机制、控件树、.resx资源,以及常用控件和容器布局。
WinForms 开发的 80% 是"在 VS 里拖控件、设属性、挂事件",但这背后设计器替你生成了什么?理解它,你才能脱离设计器手写、排查布局 bug、做自定义控件。
一、partial class:设计器和你的代码各管一半
VS 设计器拖一个 Button 到 Form,生成两个文件:
| |
| |
要点:
- 两个文件同一个类(partial)——编译器合并。设计器管布局(
.Designer.cs),你管逻辑(Form1.cs)。 InitializeComponent()必须在访问设计器生成的控件之前调用——它创建控件实例、设属性、挂事件、建控件树。通常它会放在构造函数开头,不调则设计器生成的界面不会被初始化。.Designer.cs不要手改——下次在设计器里改动会覆盖。要改控件树,在设计器里改,或写到Form1.cs。
记忆法:
.Designer.cs= 设计器的"手稿",Form1.cs= 你的工位。两半拼成 Form1。
二、控件树:Controls 集合
每个 Control(包括 Form)有一个 Controls 集合,装它的子控件。整个界面是一棵树:
| |
| |
Controls.Add/Remove/Clear改树结构。Dock(停靠)和Anchor(锚定)决定控件随窗体缩放的行为——WinForms 布局的两大基石(第 4 篇会再讲)。- 父控件 Dispose 时会级联 Dispose 子控件(自动)。
三、常用控件一览
WinForms 内置控件丰富,覆盖典型桌面需求:
| 控件 | 用途 | 关键属性/事件 |
|---|---|---|
Form | 窗口 | Text/Size/DialogResult/FormClosing |
Button | 按钮 | Text/Click |
TextBox | 文本输入 | Text/ReadOnly/Multiline/TextChanged |
Label | 标签 | Text |
CheckBox / RadioButton | 选择 | Checked/CheckedChanged |
ComboBox | 下拉 | Items/SelectedIndex/SelectedValueChanged |
ListBox / CheckedListBox | 列表 | Items/SelectedIndexChanged |
DataGridView | 表格(数据绑定主力) | DataSource(第 5 篇详讲) |
PictureBox | 图片 | Image |
DateTimePicker | 日期 | Value |
TabControl | 选项卡 | TabPages |
ProgressBar | 进度 | Value/Maximum |
TreeView / ListView | 树/列表视图 | Nodes/Items |
四、容器与布局控件
WinForms 的布局靠容器 + Dock/Anchor,不像 WPF 那样有专门的布局系统(Panel 体系):
| 容器 | 布局方式 |
|---|---|
Panel | 空容器,子控件绝对坐标或 Dock |
GroupBox | 带标题的 Panel(分组) |
TabControl | 选项卡页 |
SplitContainer | 可拖动的分隔条,两块区域 |
TableLayoutPanel | 表格布局(行/列,百分比/绝对) |
FlowLayoutPanel | 流式布局(从左到右/从上到下自动排) |
| |
| |
Dock 的 Z-order 顺序很关键:先加的 Dock 控件占外层,
Fill要最后加,否则被其他 Dock 控件挤掉。设计器里"置于底层/顶层"或代码里Controls.Add顺序控制。
五、.resx 资源文件
Form1.resx 装窗体的资源:图标、本地化字符串、二进制(图片)等。XML 格式。
| |
- 本地化:为不同语言建
Form1.zh-CN.resx/Form1.en.resx,编译成卫星程序集,运行时按文化自动切换。 - 设计器属性:默认情况下,不少属性赋值直接生成在
.Designer.cs中;当窗体启用Localizable后,Text、Font等可本地化属性才会更多地写入对应文化的.resx并由ComponentResourceManager加载;图像等资源也可单独存入.resx。
| |
六、控件生命周期与 Dispose
| |
关键点:
- HWND 懒创建:控件实际窗口句柄在第一次显示或访问
Handle时才建。 - 必须 Dispose:控件持有非托管资源(HWND、GDI 对象),
Dispose释放它们。模式窗体用ShowDialog()显示时,关闭后应由调用者用using或显式Dispose;控件被容器释放时,容器会继续释放其子控件。手动创建且没加入控件树的控件,要由自己负责释放。 Pen/Brush/Bitmap/Font这些 GDI+ 对象也非托管,用完要 Dispose(第 4 篇详讲)。
| |
七、设计时 vs 运行时
设计器在你的控件上跑 ComponentDesigner,会触发控件的某些代码。所以:
- 构造函数别干重活(设计器会 new 你的 Form,重活会拖慢设计器甚至让它崩)。
DesignMode属性:控件在设计器里跑时为 true,可用来跳过运行时才该做的逻辑(查数据库、连网络)。
| |
注意:
DesignMode在构造函数里还不可靠(句柄未建),最稳的是在Load事件里查。
八、小结
| |
下一篇:事件模型——WinForms 的老式 event、订阅/退订的讲究、没退订导致的内存泄漏陷阱。