双缓冲列表框

发布于 2024-07-27 21:23:01 字数 442 浏览 8 评论 0原文

我有一个 CheckedListBox (WinForms) 控件(它继承自 ListBox;谷歌搜索显示问题出在 ListBox),该控件锚定到其窗体的所有四个边。 当调整表单大小时,列表框会出现难看的闪烁。 我尝试继承CheckedListBox并在构造函数中将DoubleBuffered设置为true(此技术适用于其他控件,包括ListView和DataGridView),但没有效果。

我尝试将 WS_EX_COMPOSITED 样式添加到 < code>CreateParams,这有帮助,但使表单调整大小变得更慢。

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

I have a CheckedListBox (WinForms) control (which inherits from ListBox; googling shows that the problem is with ListBox) that is anchored to all four sides of its form. When the form is resized, the ListBox has an ugly flicker. I tried inheriting CheckedListBox and setting DoubleBuffered to true in the ctor (this technique works with other controls, including ListView and DataGridView), but it had no effect.

I tried adding the WS_EX_COMPOSITED style to CreateParams, and this helped, but makes the form resize mush more slowly.

Is there any other way to prevent this flickering?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一笑百媚生 2024-08-03 21:23:02

您可以检查切换到带有复选框的 ListView 控件是否会改善问题。 这并不容易处理(但是,嘿,WinForms ListBox 也不是天才之举),我发现使用 DoubleBuffered=true 调整大小的行为是可以忍受的。

或者,您可以尝试通过覆盖父窗体背景绘图来减少闪烁 - 提供空心画笔,或通过不执行任何操作并返回 TRUE 来覆盖 WM_ERASEBKND。 (如果你的控件覆盖了父窗体的整个客户区那就没问题,否则你需要更复杂的背景绘制方法。

我已经在Win32应用程序中成功使用了这个方法,但我不知道Forms控件是否添加了一些它自身的魔力使其无法发挥作用。

You could check if switching to a ListView Control with checkboxes improves matters. It's not as easy to deal with (but hey, the WinForms ListBox isn't a stroke of genius either), I found that it's resize behavior with DoubleBuffered=true is bearable.

Alternatively, you could try to reduce flicker by overriding the parent forms background drawing - either providing a hollow brush, or overriding WM_ERASEBKND by doing nothing and returning TRUE. (that's ok if your control covers the entire client area of the parent form, otherwise you'd need a more complex background drawing method.

I've used this successfully in Win32 applications, but I don't know if the Forms control adds some of it's own magic that renders this nonfunctional.

娇纵 2024-08-03 21:23:02

这过去是通过向控件发送 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);

另请参阅:Microsoft 的 WM_SETREDRAW 参考 < em>固定链接

如果其他人在.NET下使用过Windows消息,请根据需要更新此帖子。

This used to be handled by sending the WM_SETREDRAW message to the control.

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);

See also: WM_SETREDRAW reference at Microsoft Fixed Link

If anyone else has used windows messages under .NET, please update this posting as necessary.

行至春深 2024-08-03 21:23:02

尽管没有解决闪烁的具体问题,但对于此类问题通常有效的方法是缓存 ListBox 项的最小状态。 然后通过对每个项目执行一些计算来确定是否需要重绘 ListBox。 仅当至少有一项需要更新时才更新 ListBox(当然,并将此新状态保存在缓存中以供下一个周期使用)。

Although not addressing the specific issue of flickering, a method that is frequently effective for this type of issue is to cache a minimal state of the ListBox items. Then determine whether you need to redraw the ListBox by performing some calculation on each item. Only update the ListBox if at least one item needs to be updated (and of course save this new state in the cache for the next cycle).

纵情客 2024-08-03 21:23:01

尽管是所有者绘制的列表框,但我也遇到了类似的问题。 我的解决方案是使用 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);
    }
}

I was having similar issues albeit with an owner drawn listbox. My solution was to use BufferedGraphics objects. Your mileage may vary with this solution if your list isn't owner drawn, but maybe it will give you some inspiration.

I found that TextRenderer had difficulties rendering to the correct location unless I suppled TextFormatFlags.PreserveGraphicsTranslateTransform. The alternative to this was to use P/Invoke to call BitBlt to directly copy pixels between the graphics contexts. I chose this as the lesser of two evils.

/// <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
}

The GDI class (suggested by 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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文