WinForms部分控件OnPaint重写方法未触发的问题求助
我遇到一个问题:OnPaint方法在Button控件中能正常工作,但在DateTimePicker、TextBox、ProgressBar或者我测试的ComboBox控件里完全不会触发,在OnPaint上设断点也根本不会命中。我试过按照旧教程里的方法设置ControlStyles,但没有任何效果。
测试代码如下:
Public Class Form2 Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True) Me.SetStyle(ControlStyles.UserPaint, True) Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True) Me.SetStyle(ControlStyles.ResizeRedraw, True) Dim customComboBox1 As New CustomComboBox() customComboBox1.Location = New Point(50, 50) Me.Controls.Add(customComboBox1) customComboBox1.Invalidate() End Sub End Class Imports System.Windows.Forms Public Class CustomComboBox Inherits ComboBox Protected Overrides Sub OnPaint(e As PaintEventArgs) MyBase.OnPaint(e) Dim rectanglePen As New Pen(Color.Black, 2) e.Graphics.DrawRectangle(rectanglePen, New Rectangle(0, 0, Width - 1, Height - 1)) rectanglePen.Dispose() End Sub End Class
解决方案
WinForms里像ComboBox、DateTimePicker、TextBox这类控件属于原生Windows控件(也叫“封装控件”),它们的绘制逻辑由系统直接接管,默认不会触发UserPaint相关逻辑,哪怕设置了ControlStyles.UserPaint也没用。针对这类控件,有几种可行的自定义绘制思路:
1. 开启控件自绘模式(仅部分控件支持)
比如ComboBox,必须先设置DrawMode属性为OwnerDrawFixed或OwnerDrawVariable,控件才会触发自绘相关的方法:
Public Class CustomComboBox Inherits ComboBox Public Sub New() ' 开启自绘模式是前提 Me.DrawMode = DrawMode.OwnerDrawFixed ' 开启必要的绘制样式 Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.OptimizedDoubleBuffer, True) End Sub Protected Overrides Sub OnPaint(e As PaintEventArgs) MyBase.OnPaint(e) ' 使用Using自动释放资源,避免内存泄漏 Using rectanglePen As New Pen(Color.Black, 2) e.Graphics.DrawRectangle(rectanglePen, New Rectangle(0, 0, Width - 1, Height - 1)) End Using End Sub End Class
注意:TextBox、DateTimePicker这类控件不支持这种方式,它们的自绘限制更严格。
2. 重写WndProc拦截绘制消息
对于无法直接开启自绘的控件,比如TextBox、DateTimePicker,可以通过拦截Windows的WM_PAINT消息来实现自定义绘制:
以TextBox为例:
Public Class CustomTextBox Inherits TextBox Protected Overrides Sub WndProc(ByRef m As Message) MyBase.WndProc(m) ' 拦截WM_PAINT消息(消息编号&HF) If m.Msg = &HF Then Using g As Graphics = Me.CreateGraphics() Using pen As New Pen(Color.Red, 2) ' 在原生绘制基础上叠加自定义边框 g.DrawRectangle(pen, New Rectangle(0, 0, Me.Width - 1, Me.Height - 1)) End Using End Using End If End Sub End Class
如果想完全替换原生绘制逻辑,可以去掉MyBase.WndProc(m),但这样需要自己实现控件的所有绘制和交互逻辑,复杂度很高,一般只建议在简单需求下叠加绘制。
3. 替代方案:使用无原生依赖的自定义控件
如果自定义绘制需求复杂,也可以选择完全从头实现一个控件,或者使用第三方WinForms控件库,避开原生控件的绘制限制。
内容的提问来源于stack exchange,提问作者nidzolino

