116

私のアプリケーションでは、あるコントロールから別のコントロールに常に移動しています。いいえ作成しました。ユーザーコントロールの数ですが、ナビゲーション中にコントロールがちらつきます。更新には 1 ~ 2 秒かかります。これを設定してみました

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true);

しかし、それは役に立ちませんでした...各コントロールには、異なるコントロールを持つ同じ背景画像があります。それで、それに対する解決策は何ですか..
ありがとう。

4

12 に答える 12

327

ダブルバッファリングで解決できるようなちらつきではありません。また、BeginUpdateやSuspendLayoutもありません。コントロールが多すぎると、BackgroundImageによってさらに悪化する可能性があります

UserControlが自分自身をペイントしたときに開始します。これはBackgroundImageを描画し、子コントロールウィンドウが配置される場所に穴を残します。次に、各子コントロールは自分自身をペイントするメッセージを受け取り、ウィンドウの内容で穴を埋めます。多くのコントロールがある場合、それらの穴はしばらくの間ユーザーに表示されます。それらは通常白で、暗いときのBackgroundImageとは対照的です。または、フォームにOpacityまたはTransparencyKeyプロパティが設定されている場合は黒になり、ほぼすべてのものとは対照的です。

これはWindowsフォームのかなり基本的な制限であり、Windowsがウィンドウをレンダリングする方法に固執しています。WPFで修正されましたが、子コントロールにウィンドウを使用しません。必要なのは、子コントロールを含むフォーム全体をダブルバッファリングすることです。それは可能です、解決策についてはこのスレッドの私のコードをチェックしてください。ただし、副作用があり、実際にはペイント速度は向上しません。コードは単純です。これをフォームに貼り付けてください(ユーザーコントロールではありません)。

protected override CreateParams CreateParams {
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    return cp;
  }
} 

ちらつきが目立たなくなるまで、ペイント速度を向上させるためにできることはたくさんあります。BackgroundImageに取り組むことから始めます。ソースイメージが大きく、コントロールに合わせて縮小する必要がある場合、これらは非常に高価になる可能性があります。BackgroundImageLayoutプロパティを「Tile」に変更します。それで目立ったスピードアップが得られる場合は、ペイントプログラムに戻り、通常のコントロールサイズに合うように画像のサイズを変更します。または、UCのOnResize()メソッドでコードを記述して、適切なサイズの画像のコピーを作成し、コントロールが再描画するたびにサイズを変更する必要がないようにします。そのコピーにはFormat32bppPArgbピクセル形式を使用します。これは、他のどのピクセル形式よりも約10倍高速にレンダリングされます。

次にできることは、穴が目立ち、画像とのコントラストが悪くなるのを防ぐことです。UCのWS_CLIPCHILDRENスタイルフラグをオフにすることができます。これは、子コントロールが移動する領域でUCがペイントされないようにするフラグです。次のコードをUserControlのコードに貼り付けます。

protected override CreateParams CreateParams {
  get {
    var parms = base.CreateParams;
    parms.Style &= ~0x02000000;  // Turn off WS_CLIPCHILDREN
    return parms;
  }
}

子コントロールは、背景画像の上に自分自身をペイントします。あなたはまだ彼らが一つずつ自分自身を描いているのを見るかもしれませんが、醜い中間の白または黒の穴は見えません。

大事なことを言い忘れましたが、子コントロールの数を減らすことは、遅いペイントの問題を解決するための良いアプローチです。UCのOnPaint()イベントをオーバーライドし、現在子に表示されているものを描画します。特定のラベルとPictureBoxは非常に無駄です。ポイントアンドクリックには便利ですが、軽量の代替手段(文字列または画像の描画)では、OnPaint()メソッドで1行のコードしか使用できません。

于 2010-04-10T11:58:32.127 に答える
11

これは実際の問題であり、Hans Passant が提供した回答は、ちらつきを抑えるのに最適です。ただし、彼が述べたように副作用があり、醜い (UI 醜い) 場合があります。「UCのスタイルフラグをオフにすることができます」と述べたように、それWS_CLIPCHILDRENはUCのスタイルフラグをオフにするだけです。メイン フォームのコンポーネントにはまだ問題があります。

たとえば、パネルのスクロール バーは、技術的には子領域にあるため、描画されません。ただし、子コンポーネントはスクロール バーを描画しないため、マウス オーバー (または別のイベントによってトリガーされる) まで描画されません。

また、アニメーション化されたアイコン (待機ループでのアイコンの変更) は機能しません。上のアイコンを削除してtabPage.ImageKeyも、他のタブページが適切にサイズ変更/再描画されません。

WS_CLIPCHILDRENそのため、最初の描画をオフにして、フォームが適切に描画されるようにするか、多くのコンポーネントでフォームのサイズを変更するときにオンにする方法を探していました。

CreateParams秘訣は、目的WS_EX_COMPOSITED/WS_CLIPCHILDRENのスタイルでアプリケーションを呼び出すことです。ここでハックを見つけました ( https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of- flicker-on-windows-forms-applications.aspx ) であり、うまく機能します。ありがとうAngryHacker!

TurnOnFormLevelDoubleBuffering()呼び出しをフォームResizeBeginイベントに入れ、TurnOffFormLevelDoubleBuffering()フォームの ResizeEnd イベントを呼び出します (または、WS_CLIPCHILDREN最初に適切に描画された後にそのままにしておきます)。

    int originalExStyle = -1;
    bool enableFormLevelDoubleBuffering = true;

    protected override CreateParams CreateParams
    {
        get
        {
            if (originalExStyle == -1)
                originalExStyle = base.CreateParams.ExStyle;

            CreateParams cp = base.CreateParams;
            if (enableFormLevelDoubleBuffering)
                cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED
            else
                cp.ExStyle = originalExStyle;

            return cp;
        }
    }

    public void TurnOffFormLevelDoubleBuffering()
    {
        enableFormLevelDoubleBuffering = false;
        this.MaximizeBox = true;
    }
于 2013-05-20T18:42:37.820 に答える
7

コントロールでカスタム ペイントを行っている (つまり、OnPaint をオーバーライドしている) 場合は、自分でダブル バッファリングを試すことができます。

Image image;
protected override OnPaint(...) {
    if (image == null || needRepaint) {
        image = new Bitmap(Width, Height);
        using (Graphics g = Graphics.FromImage(image)) {
            // do any painting in image instead of control
        }
        needRepaint = false;
    }
    e.Graphics.DrawImage(image, 0, 0);
}

そして、プロパティでコントロールを無効にしますNeedRepaint

それ以外の場合は、SuspendLayout と ResumeLayout を使用した上記の回答がおそらく必要です。

于 2010-04-10T08:40:19.107 に答える
4

次のコードをコンストラクターまたは OnLoad イベントに入れます。サブ コントロールを持つある種のカスタム ユーザー コントロールを使用している場合は、これらのカスタム コントロールもダブル バッファーであることを確認する必要があります (MS のドキュメントでは、デフォルトでは true に設定されています)。

カスタム コントロールを作成している場合は、このフラグを ctor に追加することをお勧めします。

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

必要に応じて、フォーム/コントロールでこのコードを使用できます。

foreach (Control control in Controls)
{
    typeof(Control).InvokeMember("DoubleBuffered",
        BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
        null, control, new object[] { true });
}

フォーム/コントロール内のすべてのコントロールを繰り返し処理し、それらのDoubleBufferedプロパティにアクセスしてから、フォーム上の各コントロールをダブル バッファリングするために true に変更します。ここでリフレクションを行う理由は、アクセスできない子コントロールを持つコントロールがあると想像してください。そうすれば、それらがプライベート コントロールであっても、プロパティを true に変更します。

ダブル バッファリング技術の詳細については、こちらを参照してください。

この問題を分類するために、私が通常オーバーライドする別のプロパティがあります。

protected override CreateParams CreateParams
{
    get
    {
        CreateParams parms = base.CreateParams;
        parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
        return parms;
    }
}

WS_EX_COMPOSITED- ダブル バッファリングを使用して、ウィンドウのすべての子孫を下から上への描画順で描画します。

これらのスタイル フラグの詳細については、こちらを参照してください。

それが役立つことを願っています!

于 2017-04-21T10:05:40.683 に答える
3

Try BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayout methods. See following
How to fix nested winform control flicker issues
Flickering during updates to Controls in WinForms (e.g. DataGridView)

于 2010-04-10T07:54:07.717 に答える
3

背景画像が存在するメイン フォームまたはユーザー コントロールで、BackgroundImageLayoutプロパティをCenterまたはに設定しますStretch。ユーザー コントロールがレンダリングされると、大きな違いに気付くでしょう。

于 2014-05-31T15:57:56.217 に答える
2

ハンスが与えた答えに追加するだけです:

(TLDRバージョン:透明度は思ったより重いです。どこでも単色のみを使用してください)

WS_EX_COMPOSITED、DoubleBuffered、およびWS_CLIPCHILDRENがちらつきを解決しなかった場合(私にとってはWS_CLIPCHILDRENがさらに悪化しました)、これを試してください:すべてのコントロールとすべてのコードを調べ、BackColor、ForeColor、または他の色は、それを削除するだけで、単色のみを使用してください。透明性を使用する必要があると考えるほとんどの場合、そうではありません。コードとコントロールを再設計し、単色を使用します。私はひどい、ひどいちらつきがあり、プログラムは遅く実行されていました。透明度を削除すると、大幅に高速化され、ちらつきはなくなりました。

編集:さらに追加するために、WS_EX_COMPOSITEDはウィンドウ全体である必要はなく、特定のコントロールにのみ適用できることを発見しました。これは私に多くのトラブルを救った。必要なコントロールから継承されたカスタムコントロールを作成し、すでに投稿されているWS_EX_COMPOSITEDのオーバーライドを貼り付けるだけです。このようにして、このコントロールでのみ低レベルのダブルバッファーを取得し、アプリケーションの残りの部分での厄介な副作用を回避します。

于 2013-03-15T16:08:36.017 に答える
1

この質問は非常に古いことは知っていますが、私の経験を紹介したいと思います。

Tabcontrol上書きされたフォームOnPaintOnPaintBackGround.NET 4.0を使用するWindows 8でちらつきに多くの問題がありました。

機能した唯一の考えは、オーバーライドでメソッドを使用しないことです。つまり、描画が によって提供されたグラフィックスに直接行われた場合、すべての四角形をペイントしても、ちらつきが消えました。ただし、このメソッドを呼び出すと、クリッピングされたビットマップ (ダブル バッファリング用に作成) を描画しても、ちらつきが発生します。Graphics.DrawImageOnPaintPaintEventArgsDrawImage

それが役に立てば幸い!

于 2014-06-27T17:57:58.313 に答える
1

このちらつきの修正このフォントの修正を組み合わせた後、独自のコードを少し追加して、ペイント時にタイマーを開始し、画面外に出て戻ったときに TabControl を無効にする必要がありました。

3つすべてがこれを行います:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    private const int WM_PAINT = 0x0f;
    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;
    private System.Drawing.Bitmap buffer;
    private Timer timer = new Timer();
    public TabControlEx()
    {
        timer.Interval = 1;
        timer.Tick += timer_Tick;
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
    void timer_Tick(object sender, EventArgs e)
    {
        this.Invalidate();
        this.Update();
        timer.Stop();
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PAINT) timer.Start();
        base.WndProc(ref m);
    }
    protected override void OnPaint(PaintEventArgs pevent)
    {
        this.SetStyle(ControlStyles.UserPaint, false);
        base.OnPaint(pevent);
        System.Drawing.Rectangle o = pevent.ClipRectangle;
        System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
        if (o.Width > 0 && o.Height > 0)
        DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
        pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        buffer = new System.Drawing.Bitmap(Width, Height);
    }
    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        this.OnFontChanged(EventArgs.Empty);
    }
    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        IntPtr hFont = this.Font.ToHfont();
        SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        this.UpdateStyles();
    }
}

私は作成者ではありませんが、私が理解していることから、ビットマップはすべてのバグをバイパスします。

これは、TabControl (アイコン付き) のちらつきを明確に解決した唯一のものでした。

違いの結果のビデオ: バニラ tabcontrol と tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps。HotTrack = true を設定する必要があります。これにより、そのバグも修正されるためです。

于 2016-03-23T23:53:33.473 に答える
-2

Control.DoubleBufferedプロパティを試しましたか?

ちらつきを軽減または防止するために、このコントロールがセカンダリ バッファーを使用してその表面を再描画する必要があるかどうかを示す値を取得または設定します。

これこれも役立つかもしれません。

于 2010-04-10T10:06:59.953 に答える
-9

ダブルバッファリングなどの必要はありません...

シンプルなソリューション...

MDI インターフェイスを使用している場合は、以下のコードをメイン フォームに貼り付けるだけです。ページからすべてのちらつきを取り除きます。ただし、読み込みに時間がかかる一部のページは、1 ~ 2 秒で表示されます。しかし、これは、各項目が 1 つずつ表示されるちらつきのあるページを表示するよりも優れています。

これは、アプリケーション全体に対する唯一の最良のソリューションです。メイン フォームに配置するコードを参照してください。

protected override CreateParams CreateParams {
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    return cp;
  }
} 
于 2012-10-02T19:37:52.587 に答える