c# 如何自定义一个 control

来源:这里教程网 时间:2026-02-21 17:39:49 作者:

继承 Control 类是最直接的方式

自定义控件本质是扩展

System.Windows.Forms.Control
(WinForms)或
System.Windows.Controls.Control
(WPF),前者更轻量、适合底层绘制与事件控制。不建议从
UserControl
开始——它自带容器逻辑和设计器依赖,反而限制对绘制、消息循环、焦点行为的精细控制。

若需完全掌控绘制(比如画波形图、仪表盘),继承
Control
并重写
OnPaint
若要支持双缓冲防闪烁,设
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true)
必须手动调用
base.OnCreateControl()
base.OnHandleCreated()
,否则生命周期钩子可能失效
别忘了在构造函数里设
ResizeRedraw = true
,否则窗口缩放时不会自动重绘

重写 OnPaint 时绕开 GDI+ 默认背景擦除

默认

Control
在每次
OnPaint
前会用
BackColor
填充整个客户区,这会导致自绘内容被覆盖或出现残影。尤其在动画或高频刷新场景下特别明显。

在构造函数中关闭默认擦除:
this.SetStyle(ControlStyles.Opaque, true)
同时禁用背景绘制:
this.SetStyle(ControlStyles.ResizeRedraw, true)
重写
OnPaintBackground
为空实现,避免父类填充背景
OnPaint
中只用传入的
Graphics
绘制内容,不要调用
e.Graphics.Clear(...)
protected override void OnPaint(PaintEventArgs e) {
    // 不调用 base.OnPaint(e)
    using (var pen = new Pen(Color.Blue, 2)) {
        e.Graphics.DrawLine(pen, 10, 10, 100, 100);
    }
}

响应鼠标/键盘事件必须显式启用焦点支持

直接继承

Control
的控件默认不可聚焦,
KeyDown
KeyPress
等事件根本不会触发,哪怕你重写了
OnKeyDown

构造函数中调用
this.TabStop = true
this.TabIndex = 0
重写
CanSelect
属性返回
true
OnMouseDown
中主动调用
this.Focus()
,否则点击不会获得输入焦点
若需捕获非客户端区域(如边框)的鼠标,需处理
WM_NCHITTEST
消息,通过
WndProc
拦截

设计器支持需要 [ToolboxItem] 和默认构造函数

没有无参构造函数或缺少特性标记,控件拖不到 WinForms 设计器里,且属性面板不显示自定义属性。

必须提供 public 无参构造函数:
public MyCustomControl() { InitializeComponent(); }
添加
[ToolboxItem(true)]
特性,否则设计器忽略该类型
让属性可序列化:对希望在设计器中编辑的属性加
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
若属性影响外观(如
LineColor
),重写
OnPropertyChanged
并调用
Invalidate()
触发重绘
WinForms 自定义控件最难缠的不是绘制,而是消息分发边界——比如鼠标进入区域后移出但未触发
MouseLeave
,或键盘焦点在嵌套控件间跳转时丢失。这些往往要靠精确拦截
WndProc
和检查
NativeWindow.AssignHandle
状态来定位。

相关推荐