性能差异 WndProc WM_PAINT 与 OnPaint
Posted
技术标签:
【中文标题】性能差异 WndProc WM_PAINT 与 OnPaint【英文标题】:Performance difference WndProc WM_PAINT vs OnPaint 【发布时间】:2020-10-15 14:53:06 【问题描述】:我有一个自定义按钮控件,我需要在其中进行一些绘图。 没有什么花哨或复杂的东西,但我注意到在 WndProc WM_PAINT 中绘图和在 OnPaint 事件中绘图时性能有很大差异。
当我在 OnPaint 中绘图时,我根本没有任何闪烁。 当我在 WM_PAINT 中绘制时,我确实会闪烁,但只有在进入和离开按钮时才会出现。所以当按钮接收或失去高亮显示时会发生闪烁(BN_HILITE / BN_UNHILITE 通知)。
据我所知,OnPaint 事件只是 WM_PAINT 消息的基于事件的包装器。 所以理论上 OnPaint 事件的效率应该较低,因为它为绘画过程增加了一层抽象。
我不确定是我的代码产生了闪烁还是其他原因。
这是我的自定义按钮的重写 OnPaint 事件的代码:
protected override void OnPaint(PaintEventArgs pevent)
base.OnPaint(pevent);
if (Day <= 0) return;
if (string.IsNullOrEmpty(Text)) return;
// Adjust font size so all text will fit.
AdjustFont(pevent.ClipRectangle);
// Check which ForeColor to use.
Color fc = Month == ExpectedMonth
? ForeColor
: UnexpectedMonthForeColor;
using (var brush = new SolidBrush(fc))
// Use StringFormat to center string in control.
StringFormat sf = new StringFormat
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
;
pevent.Graphics.DrawString(Text, Font, brush, pevent.ClipRectangle, sf);
这是产生闪烁的 WndProc 实现:
const int WM_PAINT = 0x000f;
protected override void WndProc(ref Message m)
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
if (Day <= 0) return;
if (string.IsNullOrEmpty(Text)) return;
using (var gr = Graphics.FromHwnd(Handle))
//Adjust font size so all text will fit.
AdjustFont(ClientRectangle);
//Check which ForeColor to use.
Color fc = Month == ExpectedMonth
? ForeColor
: UnexpectedMonthForeColor;
using (var brush = new SolidBrush(fc))
// Use StringFormat to center string in control.
StringFormat sf = new StringFormat
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
;
gr.DrawString(Text, Font, brush, ClientRectangle, sf);
这里是AdjustFont方法:
private void AdjustFont(Rectangle rcBounds)
// Calculate string size and check if it fits into the current bounds.
var szText = TextRenderer.MeasureText(Text, Font);
if (szText.Width > rcBounds.Width || szText.Height > rcBounds.Height)
// Reduce font size by 0.25 until the text fits into the bounds.
while (Font.Size > 0.25f
&& (szText.Width > rcBounds.Width
|| szText.Height > rcBounds.Height))
Font = new Font(
Font.FontFamily,
Font.Size - 0.25f,
FontStyle.Regular,
Font.Unit,
Font.GdiCharSet,
Font.GdiVerticalFont);
szText = TextRenderer.MeasureText(Text, Font);
【问题讨论】:
考虑添加一个计数器来确定调用次数。两种实现的调用次数是否相同? 请不要猜测。 @mjwills 我已经测试过了。两种实现都产生完全相同数量的调用。 鉴于两个代码块并不完全相同,最简单的解释是不同位中的一个在一个版本中比另一个慢。 (例如,gr.Dispose
在一个版本中,但不在另一个版本中)。
AdjustFont
的while
循环(其中的逻辑)在两个版本中执行的次数是否相同?
@mjwills 刚刚测量过,循环在两个版本中执行的次数完全相同。我还测量了产生了多少毫秒和滴答声,结果表明 WndProc 实现要慢得多。我猜是由于额外的 IF 子句以及图形元素的创建和处置。
【参考方案1】:
我选择使用 OnPaint() 版本,因为它的性能更好,我不需要在设计器中立即更新。
我将按照@TnTinMn 的建议深入研究 WM_PAINT 实现。 任何新结果都将添加到此答案中。
【讨论】:
以上是关于性能差异 WndProc WM_PAINT 与 OnPaint的主要内容,如果未能解决你的问题,请参考以下文章
TWinControl的消息覆盖函数大全(41个WM_函数和31个CM_函数,它的WndProc就处理鼠标(转发)键盘(取消拖动)焦点和WM_NCHITTEST一共4类消息)