I have a custom button control where I need to do some drawing. Nothing fancy or complex, yet I notice quite a difference in performance when drawing within WndProc WM_PAINT and drawing within the OnPaint event.
When I draw in OnPaint I do not get any flickering at all. When I draw in WM_PAINT I do get flickering, though only when entering and leaving the button. So the flickering occurs when the button receives or looses highlight (BN_HILITE / BN_UNHILITE notification).
As far as I know the OnPaint event is simply a event based wrapper for the WM_PAINT message. So in theory the OnPaint event should be less efficent since it adds a layer of abstraction to the painting process.
I am unsure if it is my code producing the flickering or something else.
Here is the Code of the overridden OnPaint event of my custom button:
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);
}
}
Here is the WndProc implementation which produces flickering:
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);
}
}
}
}
Here the AdjustFont method:
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);
}
}
}