为啥从 WM_NCPAINT 使用 DrawImageUnscaled 会导致闪烁?
Posted
技术标签:
【中文标题】为啥从 WM_NCPAINT 使用 DrawImageUnscaled 会导致闪烁?【英文标题】:Why does DrawImageUnscaled cause flickering when used from WM_NCPAINT?为什么从 WM_NCPAINT 使用 DrawImageUnscaled 会导致闪烁? 【发布时间】:2011-09-09 02:47:55 【问题描述】:我目前正在构建一个从System.Windows.Forms.ContainerControl
派生的控件,它有一个我需要自己绘制的边界区域。由于没有要覆盖的OnPaintNonClientArea
,所以我自己构建了它(为简洁起见,删除了WM_NCCALCSIZE
、WM_NCHITTEST
等其他消息的处理):
protected override void WndProc(ref Message m)
switch (m.Msg)
case WM_NCPAINT:
IntPtr hDC = NativeApi.Methods.GetWindowDC(m.HWnd);
if (hDC != IntPtr.Zero)
using (Graphics canvas = Graphics.FromHdc(hDC))
if (Width > 0 && Height > 0)
using (PaintEventArgs e = new PaintEventArgs(canvas, new Rectangle(0, 0, Width, Height)))
OnPaintNonClientArea(e);
NativeApi.Methods.ReleaseDC(m.HWnd, hDC);
m.Result = IntPtr.Zero;
break;
base.WndProc(ref m);
在OnPaintNonClientArea
,我做到了:
private void OnPaintNonClientArea(PaintEventArgs e)
if (_ncBuffer == null)
_ncBuffer = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(_ncBuffer))
// painting occurs here ...
// this causes flickering
e.Graphics.DrawImageUnscaled(_ncBuffer, 0, 0, Width, Height);
保持OnPaintNonClientArea
不变,这将消除闪烁:
protected override void WndProc(ref Message m)
switch (m.Msg)
case WM_NCPAINT:
using(Bitmap ncBitmap = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
using(Graphics ncGraphics = Graphics.FromImage(ncBitmap))
using (PaintEventArgs e = new PaintEventArgs(ncGraphics, new Rectangle(0, 0, Width, Height)))
OnPaintNonClientArea(e);
IntPtr hDCWin = NativeApi.Methods.GetWindowDC(m.HWnd);
IntPtr hDCImg = ncGraphics.GetHdc();
IntPtr hBmp = ncBitmap.GetHbitmap();
IntPtr hBmpOld = NativeApi.Methods.SelectObject(hDCImg, hBmp);
Padding p = GetNonClientArea();
NativeApi.Methods.ExcludeClipRect(hDCWin, p.Left, p.Top,Width- p.Right, Height-p.Bottom);
NativeApi.Methods.BitBlt(hDCWin, 0, 0, Width, Height, hDCImg, 0, 0,NativeApi.TernaryRasterOperations.SRCCOPY);
NativeApi.Methods.SelectObject(hDCImg, hBmpOld);
NativeApi.Methods.DeleteObject(hBmp);
ncGraphics.ReleaseHdc(hDCImg);
NativeApi.Methods.ReleaseDC(m.HWnd, hDCWin);
m.Result = IntPtr.Zero;
break;
base.WndProc(ref m);
那么,为什么DrawImageUnscaled
会导致这种闪烁?在绘制缓冲区之前,它似乎用白色刷子擦除了它工作的区域。我在文档中没有找到任何澄清这个问题的东西。如果只是控件周围的一个小边框也没关系,但是NC区域内会显示文本,因此该区域清晰可见,因此闪烁确实可见且烦人。
相关问题:我是否正确地处理了原生 GDI 内容,或者是否存在我现在看不到的潜在问题?另外,在创建ncBitmap
时,我使用的是控件的宽度和高度,但是GDI+是与分辨率无关的,会有什么问题吗?
【问题讨论】:
您是否尝试过在表单中使用双缓冲?双缓冲应该处理诸如此类的图形问题。此外,是否有 SuspendLayout 和 PerformLayout 等效项可用于在加载图形对象之前阻止控件更新? 谢谢。我摆弄这些东西很长一段时间并尝试了各种各样的东西,有一次我确实对没有任何影响的表单进行了双缓冲。我认为这不适用于这里,因为我已经有效地进行了双缓冲 - 我正在使用Graphics
对象绘制所有内容,然后使用 DrawImageUnscaled
我尝试将缓冲区绘制到由 @ 创建的 Graphics 对象上987654334@。它是 DrawImageUnscaled 本身,它首先使用白色画笔擦除要绘制的区域,然后绘制 Graphics 对象的内容。不确定这是否可以解决。
【参考方案1】:
为避免在 UserControl 中闪烁,我在 BufferedGraphics 类中获得了更好的运气。
MSDN
这是一个选项吗?
【讨论】:
+1 表示我不知道的一种方法。但是对于绘制非客户区,这会导致与我尝试的第一种方法一样多的闪烁。以上是关于为啥从 WM_NCPAINT 使用 DrawImageUnscaled 会导致闪烁?的主要内容,如果未能解决你的问题,请参考以下文章