ユーザーコントロールのちらつきを修正する方法


107

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

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

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


これらのステートメントはどこにありますか?理想的には、それらをコンストラクターに入れます。UpdateStylesこれらを設定した後に電話しましたか?文書化は不十分ですが、必要になる場合があります。
トーマス

回答:


306

ダブルバッファリングで解決できるようなちらつきではありません。BeginUpdateまたはSuspendLayoutも使用できません。あなたは、背景画像は、それを作ることができ、あまりにも多くのコントロールを持ってたくさん悪いこと。

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

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

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

子コントロールは、背景画像の上に自分自身をペイントします。それらが1つずつ自分自身をペイントしているのを見ることはできますが、醜い中間のホワイトまたはブラックホールは表示されません。

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


WS_CLIPCHILDRENをオフにすると、ユーザーエクスペリエンスが向上します。
Mahesh

絶対に完璧!..ありがとうございました
AlejandroAlis

8

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

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

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

したがって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;
    }

コードにTurnOnFormLevelDoubleBuffering()メソッドが含まれていない...
Dan W

@DanWこの回答に投稿されたURL(angryhacker.com/blog/archive/2010/07/21/…)を
ご覧ください

この回答のリンクは機能していないようです。解決策について知りたいのですが、別の例へのリンクはありますか?
プラット

6

コントロールでカスタムペイントを行う場合(つまり、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を使用した上記の回答がおそらく必要です。


これは、ダブルバッファをシミュレートするクリエイティブな方法です!。if (image != null) image.Dispose();前に追加できますimage = new Bitmap...
S.Serpooshan 2017年


2

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


2

これをコメントとして追加しようとしたのですが、ポイントが足りません。これは、ハンスの投稿に感謝し、私のちらつきの問題を解決した唯一のことです。私のようなc ++ビルダーを使用している人のためにここに翻訳があります

CreateParams宣言をアプリケーションのメインフォームの.hファイルに追加します。

class TYourMainFrom : public TForm
{
protected:
    virtual void __fastcall CreateParams(TCreateParams &Params);
}

これを.cppファイルに追加します

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
    Params.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    TForm::CreateParams(Params);
}

2

コンストラクターまたは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 -ダブルバッファリングを使用して、ウィンドウのすべての子孫を下から上への描画順序で描画します。

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

お役に立てば幸いです。


1

ハンスの答えに加えて:

(TLDRバージョン:透明度はあなたが考えるよりも重い、どこでも無地の色のみを使用してください)

WS_EX_COMPOSITED、DoubleBuffered、WS_CLIPCHILDRENでちらつきが解決しなかった場合(私はWS_CLIPCHILDRENでさらにひどくなった)、これを試してください。すべてのコントロールとすべてのコードを調べ、BackColor、ForeColor、または他の色は、それを削除して、単色のみを使用してください。あなたはあなただけだと思うほとんどの場合持って透明性を使用するために、あなたはしないでください。コードとコントロールを再設計し、単色を使用します。私はひどく、ひどくちらつきがあり、プログラムはゆっくりと動いていました。透明度を削除すると、大幅にスピードアップし、ちらつきはありません。

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


0

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

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

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

それが役に立てば幸い!


0

このちらつきの修正フォントの修正を組み合わせた後、ペイント時にタイマーを開始して、画面外に出たときなどに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を設定する必要があります。


-2

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

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

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


-9

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

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

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

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

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

12
それで、あなたが言っていることは、2年以上前にハンスが提供した答えは、実際には正しいということですか?クシチさん、ありがとうございます。それは本当にとても役に立ちます!
フェルナンド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.