ListBox с двойной буферизацией - PullRequest
7 голосов
/ 15 июля 2009

У меня есть элемент управления CheckedListBox (WinForms) (который наследуется от ListBox; поиск в Google показывает, что проблема связана с ListBox), который привязан ко всем четырем сторонам его формы. При изменении размера формы в ListBox появляется мерцание. Я попытался унаследовать CheckedListBox и установить DoubleBuffered в true в ctor (этот метод работает с другими элементами управления, включая ListView и DataGridView), но он не имел никакого эффекта.

Я попытался добавить стиль WS_EX_COMPOSITED к CreateParams, и это помогло, но заставило форму изменить размер помедленнее.

Есть ли другой способ предотвратить это мерцание?

Ответы [ 4 ]

11 голосов
/ 06 августа 2009

У меня были похожие проблемы, хотя со списком, нарисованным владельцем. Моим решением было использовать объекты 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);
    }
}
2 голосов
/ 16 июля 2009

Вы можете проверить, улучшает ли переключение на элемент управления ListView с флажками. С этим не так легко справиться (но эй, WinForms ListBox тоже не гениальный ход), я обнаружил, что его поведение при изменении размера с DoubleBuffered=true терпимо.

Кроме того, вы можете попытаться уменьшить мерцание, переопределив фоновый рисунок родительских форм - либо предоставив пустую кисть, либо переопределив WM_ERASEBKND, ничего не делая и вернув TRUE. (Это нормально, если ваш элемент управления покрывает всю клиентскую область родительской формы, в противном случае вам потребуется более сложный метод рисования фона.

Я успешно использовал это в приложениях Win32, но я не знаю, добавляет ли элемент управления Forms часть своего собственного волшебства, которое делает это нефункциональным.

0 голосов
/ 04 октября 2016

Несмотря на то, что не решается конкретная проблема мерцания, часто эффективным методом для этого типа проблемы является кэширование минимального состояния элементов ListBox. Затем определите, нужно ли перерисовывать ListBox, выполнив некоторые вычисления для каждого элемента. Обновляйте ListBox только в том случае, если необходимо обновить хотя бы один элемент (и, конечно, сохранить это новое состояние в кэше для следующего цикла).

0 голосов
/ 21 августа 2009

Раньше это обрабатывалось отправкой сообщения 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 в Microsoft Фиксированная ссылка

Если кто-то еще использовал сообщения Windows в .NET, обновите это сообщение по мере необходимости.

...