【发布时间】: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 对象的内容。不确定这是否可以解决。