C# 双缓冲列表框
我有一个CheckedListBox(WinForms)控件(它继承自ListBox;谷歌搜索表明问题出在ListBox上),它锚定在表单的所有四个侧面。调整窗体大小时,列表框会出现异常闪烁。我尝试继承CheckedListBox并在ctor中将C# 双缓冲列表框,c#,.net,winforms,listbox,doublebuffered,C#,.net,Winforms,Listbox,Doublebuffered,我有一个CheckedListBox(WinForms)控件(它继承自ListBox;谷歌搜索表明问题出在ListBox上),它锚定在表单的所有四个侧面。调整窗体大小时,列表框会出现异常闪烁。我尝试继承CheckedListBox并在ctor中将DoubleBuffered设置为true(此技术适用于其他控件,包括ListView和DataGridView),但没有效果 我尝试将样式添加到CreateParams,这很有帮助,但会使表单的调整速度变慢 有没有其他方法可以防止这种闪烁?您可以检查切
DoubleBuffered
设置为true
(此技术适用于其他控件,包括ListView和DataGridView),但没有效果
我尝试将样式添加到CreateParams
,这很有帮助,但会使表单的调整速度变慢
有没有其他方法可以防止这种闪烁?您可以检查切换到带有复选框的ListView控件是否会改善问题。它不是那么容易处理(但是,嘿,WinForms列表框也不是天才),我发现它的调整大小行为
DoubleBuffered=true
是可以忍受的
或者,您可以尝试通过覆盖父窗体背景图形来减少闪烁-提供空心画笔,或者通过不执行任何操作并返回TRUE
来覆盖WM_ERASEBKND
。(如果控件覆盖父窗体的整个客户端区域,则可以,否则需要更复杂的背景绘制方法
我已经在Win32应用程序中成功地使用了它,但我不知道Forms控件是否添加了一些使其不起作用的魔力。我遇到了类似的问题,尽管是所有者绘制的列表框。我的解决方案是使用BufferedGraphics对象。如果列表不是所有者绘制的,您的里程可能会因此解决方案而异,但是也许它会给你一些灵感 我发现TextRenderer很难渲染到正确的位置,除非我添加了TextFormatFlags.PreserveGraphicsTranslation。替代方法是使用p/Invoke调用BitBlt在图形上下文之间直接复制像素。我选择这两个缺点中的较小者
/// <summary>
/// This class is a double-buffered ListBox for owner drawing.
/// The double-buffering is accomplished by creating a custom,
/// off-screen buffer during painting.
/// </summary>
public sealed class DoubleBufferedListBox : ListBox
{
#region Method Overrides
/// <summary>
/// Override OnTemplateListDrawItem to supply an off-screen buffer to event
/// handlers.
/// </summary>
protected override void OnDrawItem(DrawItemEventArgs e)
{
BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;
Rectangle newBounds = new Rectangle(0, 0, e.Bounds.Width, e.Bounds.Height);
using (BufferedGraphics bufferedGraphics = currentContext.Allocate(e.Graphics, newBounds))
{
DrawItemEventArgs newArgs = new DrawItemEventArgs(
bufferedGraphics.Graphics, e.Font, newBounds, e.Index, e.State, e.ForeColor, e.BackColor);
// Supply the real OnTemplateListDrawItem with the off-screen graphics context
base.OnDrawItem(newArgs);
// Wrapper around BitBlt
GDI.CopyGraphics(e.Graphics, e.Bounds, bufferedGraphics.Graphics, new Point(0, 0));
}
}
#endregion
}
这通常通过向控件发送WM_SETREDRAW消息来处理
const int WM_SETREDRAW = 0x0b;
Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 0, (IntPtr) 0);
yourform.DefWndProc(ref m);
// do your updating or whatever else causes the flicker
Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 1, (IntPtr) 0);
yourform.DefWndProc(ref m);
另请参见:固定链接
如果其他人在.NET下使用过windows邮件,请根据需要更新此帖子。虽然没有解决闪烁的特定问题,但对于此类问题通常有效的方法是缓存列表框项目的最低状态。然后,通过执行一些计算来确定是否需要重新绘制列表框仅当至少有一个项目需要更新时才更新列表框(当然,将此新状态保存在缓存中以供下一个周期使用).闪烁是由调整大小引起的,因此这不是一个最佳解决方案。但是,我可能还是会这样做。这并不能解决我的列表框上的闪烁问题。这个答案是错误的,因为当表单调整大小时,您的代码甚至没有执行。@Eric:您从哪里获得GDI?它是一个引用吗?例如,我尝试添加
图形GDI=this.CreateGraphics();
但它没有CopyGraphics方法。或者您以前导入过Gdi32.dll吗?好的-正在工作。我已使用BitBlt
方法添加了Gdi32.dll
,将其包装为GDI.CopyGraphics(…)
现在它可以工作了……唯一的问题是它和原来的列表框一样闪烁。有什么办法解决它吗?@Matt:列表框设置为自己绘制的吗?Eric,你能发布GDI.CopyGraphics
包装方法吗?@Jeremythonpson,我在谷歌上搜索了GDI.CopyGraphics,找到了它
const int WM_SETREDRAW = 0x0b;
Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 0, (IntPtr) 0);
yourform.DefWndProc(ref m);
// do your updating or whatever else causes the flicker
Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 1, (IntPtr) 0);
yourform.DefWndProc(ref m);