代码之家  ›  专栏  ›  技术社区  ›  SLaks

双缓冲列表框

  •  7
  • SLaks  · 技术社区  · 15 年前

    我有一个checkedlistbox(winforms)控件(继承自listbox;googling显示问题出在listbox),它锚定在表单的所有四个方面。当窗体被调整大小时,列表框会有一个难看的闪烁。我尝试继承CheckedListBox和设置 DoubleBuffered true 在ctor中(此技术与其他控件(包括ListView和DataGridView)一起工作),但它没有效果。

    我试图添加 WS_EX_COMPOSITED 风格到 CreateParams 这有帮助,但使形状调整得更慢。

    有没有其他方法来防止这种闪烁?

    4 回复  |  直到 6 年前
        1
  •  11
  •   D.Kastier K Yugandhar Reddy    6 年前

    我也遇到过类似的问题,尽管我有一个业主绘制的列表框。我的解决方案是使用Bufferedgraphics对象。如果您的列表不是由所有者绘制的,您的里程数可能会因此解决方案而异,但它可能会给您一些启发。

    我发现,除非我补充了textformFlags.PreserveGraphicTranslateTransform,否则textfrender很难呈现到正确的位置。另一种方法是使用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 课程(建议人 法式面包 )

    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);
        }
    }
    
        2
  •  2
  •   EricLaw    12 年前

    您可以检查切换到带有复选框的ListView控件是否可以改善问题。处理起来并不容易(但是,WinForms列表框也不是天才的一笔),我发现它的调整行为 DoubleBuffered=true 是可以忍受的。

    或者,您可以尝试通过重写父窗体背景绘图来减少闪烁-要么提供一个空心画笔,要么重写 WM_ERASEBKND 什么都不做就回来 TRUE .(如果控件覆盖了父窗体的整个工作区,那就没问题了,否则需要更复杂的背景绘制方法。

    我已经在Win32应用程序中成功地使用了它,但是我不知道窗体控件是否添加了一些它自己的魔力,使其无法正常工作。

        3
  •  0
  •   Jarett Millard    15 年前

    这通常通过向控件发送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邮件,请根据需要更新此文章。

        4
  •  0
  •   AlainD    8 年前

    虽然没有解决闪烁的特定问题,但对于此类问题通常有效的方法是缓存列表框项的最小状态。然后通过对每个项执行一些计算来确定是否需要重新绘制列表框。只有在至少需要更新一个项时才更新列表框(当然,在下一个循环的缓存中保存这个新状态)。