8

フォームの 4 つの側面すべてに固定されている CheckedListBox (WinForms) コントロール (ListBox から継承されます。Google では ListBox に問題があることを示しています) があります。フォームのサイズが変更されると、ListBox に見苦しいちらつきが生じます。CheckedListBox を継承して ctor に設定しようとDoubleBufferedtrueましたが (この手法は ListView や DataGridView などの他のコントロールでも機能します)、効果はありませんでした。

WS_EX_COMPOSITEDにスタイルを追加しようとしCreateParamsましたが、これは役に立ちましたが、フォームのサイズ変更が遅くなります。

このちらつきを防ぐ他の方法はありますか?

4

4 に答える 4

13

所有者が描画したリストボックスではありますが、同様の問題がありました。私の解決策は、BufferedGraphics オブジェクトを使用することでした。リストが所有者によって描かれていない場合、マイレージはこのソリューションによって異なる場合がありますが、おそらくインスピレーションを与えるでしょう.

TextFormatFlags.PreserveGraphicsTranslateTransform を追加しない限り、TextRenderer が正しい場所にレンダリングするのが難しいことがわかりました。これに代わる方法は、P/Invoke を使用して BitBlt を呼び出し、グラフィックス コンテキスト間でピクセルを直接コピーすることでした。私はこれを 2 つの悪の小さい方として選択しました。

/// <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);
    }
}
于 2009-08-06T15:46:32.503 に答える
2

チェックボックスを使用して ListView コントロールに切り替えると、問題が改善されるかどうかを確認できます。対処するのは簡単ではありません (しかし、WinForms ListBox も天才的なストロークではありません)。サイズ変更動作DoubleBuffered=trueは耐えられることがわかりました。

または、親フォームの背景描画をオーバーライドしてちらつきを減らすこともできます。つまり、中空のブラシを提供するか、WM_ERASEBKND何もせずに戻ることでオーバーライドしますTRUE。(コントロールが親フォームのクライアント領域全体をカバーする場合は問題ありませんが、それ以外の場合は、より複雑な背景描画メソッドが必要になります。

私はこれを Win32 アプリケーションで正常に使用しましたが、Forms コントロールがこれを機能させない独自の魔法を追加するかどうかはわかりません。

于 2009-07-15T20:09:22.853 に答える
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);

参照:MicrosoftFixedLinkのWM_SETREDRAW リファレンス

他の誰かが.NETでWindowsメッセージを使用したことがある場合は、必要に応じてこの投稿を更新してください。

于 2009-08-20T20:58:17.130 に答える