我有一个CheckedListBox(WinForms)控件(它继承自ListBox; googling显示问题在于ListBox),它固定在其表单的所有四个边上。调整窗体大小时,ListBox有一个丑陋的闪烁。我尝试继承CheckedListBox并在ctor中将DoubleBuffered
设置为true
(此技术适用于其他控件,包括ListView和DataGridView),但它没有效果。
我尝试将WS_EX_COMPOSITED
样式添加到CreateParams
,这有帮助,但会使表单调整速度更慢。
还有其他方法可以防止这种闪烁吗?
答案 0 :(得分:11)
虽然有一个所有者绘制的列表框,但我遇到了类似的问题。我的解决方案是使用BufferedGraphics对象。如果您的列表不是所有者绘制的,那么您的里程可能因此解决方案而异,但也许它会给您一些灵感。
我发现TextRenderer很难渲染到正确的位置,除非我提供了TextFormatFlags.PreserveGraphicsTranslateTransform。替代方法是使用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
}
GDI
课程(由 frenchtoast 建议)。
public static class GDI
{
private const UInt32 SRCCOPY = 0x00CC0020;
[DllImport("gdi32.dll", CallingConvention = CallingConvention.StdCall)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, UInt32 dwRop);
public static void CopyGraphics(Graphics g, Rectangle bounds, Graphics bufferedGraphics, Point p)
{
IntPtr hdc1 = g.GetHdc();
IntPtr hdc2 = bufferedGraphics.GetHdc();
BitBlt(hdc1, bounds.X, bounds.Y,
bounds.Width, bounds.Height, hdc2, p.X, p.Y, SRCCOPY);
g.ReleaseHdc(hdc1);
bufferedGraphics.ReleaseHdc(hdc2);
}
}
答案 1 :(得分:2)
您可以检查切换到带有复选框的ListView控件是否可以改善问题。它并不容易处理(但是,嘿,WinForms ListBox也不是天才的一击),我发现用DoubleBuffered=true
调整行为是可以忍受的。
或者,您可以尝试通过覆盖父窗体背景图来减少闪烁 - 提供空心画笔,或通过不执行任务并返回WM_ERASEBKND
来覆盖TRUE
。 (如果您的控件覆盖父窗体的整个客户区域,那就没问题,否则您需要更复杂的背景绘制方法。
我已经在Win32应用程序中成功使用了这个,但我不知道Forms控件是否会添加一些自己的魔法,使其无法正常运行。
答案 2 :(得分:0)
过去通过向控件发送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);
另请参阅:WM_SETREDRAW reference at Microsoft 固定链接
如果其他人在.NET下使用过Windows消息,请根据需要更新此帖子。
答案 3 :(得分:0)
虽然没有解决闪烁的具体问题,但是对于此类问题经常有效的方法是缓存ListBox项的最小状态。然后通过对每个项目执行一些计算来确定是否需要重绘ListBox。如果至少需要更新一个项目,则只更新ListBox(当然,在下一个周期的缓存中保存这个新状态)。