必须用usercontrol当组合多个控件并封装共用逻辑;继承button仅适用于复用原生行为并微调外观或状态;继承control应避免,因需手动实现焦点、键盘导航、dpi适配等关键功能。

选 UserControl 还是继承 Button,不是风格问题,而是语义和交互契约问题——错选基类会导致 Tab 键失效、双击不响应、设计器属性丢失等“看起来正常但用不了”的故障。
什么时候必须用 UserControl?
当你在组合多个标准控件(比如 TextBox + Button + ComboBox)并封装共用逻辑时,UserControl 是唯一合理选择。
- 它天然支持嵌套子控件的布局、焦点传递、Tab 导航链和设计器拖放
- 你不需要手动处理句柄创建、消息泵或 DPI 缩放适配——这些由基类托管
- 常见错误:把
UserControl当成画布,在OnPaint里硬画按钮形状。这会绕过 Windows 按钮的可访问性支持(如屏幕阅读器识别、高对比度模式) - 若需设计时可见属性(如
SearchText、ClearButtonVisible),必须加[Browsable(true)]和[Category("Behavior")],否则属性窗口里不显示
什么时候该直接继承 Button?
当你想复用按钮所有原生行为,只改外观或加极少量状态(如计数、加载态、角标),就继承 Button。
- 空格键触发
Click、PerformClick()可调用、右键菜单含“属性”项——这些开箱即得 - 重写
OnPaint时,必须先调base.OnPaint(e):它负责绘制焦点矩形、禁用灰度、DPI 缩放后的边框像素对齐,手动画极易出错 - 别在
OnPaint里 newBrush或Font:缓存为私有字段(如private SolidBrush _badgeBrush),并在Dispose中释放 - 若添加新属性(如
BadgeText),要配套调用Invalidate()触发重绘,否则改了属性界面不更新
为什么千万别一上来就继承 Control?
90% 的所谓“完全自定义”需求,其实并不需要放弃标准交互能力。继承 Control 意味着你主动放弃:
- 内置 TabStop / TabIndex 管理,得自己遍历子控件维护焦点链
- 键盘导航(方向键切换、Enter/Space 触发)需手动拦截
WM_KEYDOWN - 高 DPI 缩放、暗色模式适配、系统字体变化监听全得自己补
- 设计器中双击无法进入代码视图(因缺少默认事件绑定机制)
- VS 工具箱不会自动识别——除非你显式实现
DesignerAttribute并注册类型转换器
让自定义控件出现在工具箱的关键动作
编译后不会自动出现。必须满足三个硬性条件:
- 控件类必须是 public,且所在项目输出为 DLL(不能是 EXE 项目)
- DLL 必须被 VS 工具箱显式“选择项”:右键工具箱 → “选择项” → “浏览” → 指向你的
.dll - 设计器文件(
.Designer.cs)里基类声明必须是UserControl或Button,而不是System.Windows.Forms.Control(VB.NET 项目尤其容易漏改)
最常被忽略的一点:继承 Button 的控件,若重写了 OnClick 却没调 base.OnClick,会导致 Click 事件不触发——因为基类那行 Click?.Invoke(...) 被跳过了。











