web-dev-qa-db-ja.com

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

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

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

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

103
Royson

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

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

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

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

ペイントの速度を改善するためにできることはたくさんありますが、ちらつきはもう目立たなくなります。 BackgroundImageに取り組むことから始めます。ソースイメージが大きく、コントロールに合わせて縮小する必要がある場合、実際に高価になる可能性があります。 BackgroundImageLayoutプロパティを「タイル」に変更します。それによって顕著なスピードアップが得られる場合は、ペイントプログラムに戻って、通常のコントロールサイズに合わせて画像のサイズを変更します。または、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はvery無駄です。ポイントアンドクリックには便利ですが、軽量の代替手段(文字列または画像の描画)はOnPaint()メソッドで1行のコードのみを取ります。

297
Hans Passant

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

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

また、アニメーションアイコン(待機ループでアイコンを変更する)は機能しません。 tabPage.ImageKey上のアイコンを削除しても、他のtabPagesのサイズ変更や再描画は適切に行われません。

だから私は、最初のペイントでWS_CLIPCHILDRENをオフにする方法を探していたので、フォームはうまくペイントされた状態でロードされるか、多くのコンポーネントでフォームのサイズを変更するときにのみオンになります。

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

TurnOnFormLevelDoubleBuffering()呼び出しをフォームのResizeBeginイベントに配置します。フォームResizeEndイベントでのTurnOffFormLevelDoubleBuffering()呼び出し(または、最初に適切にペイントされた後、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;
    }
9
user2044810

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

6
Patrick

BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayoutメソッドを試してください。以下を参照してください。
ネストされたwinformコントロールのちらつきの問題を修正する方法
WinForms(例:DataGridView)のコントロールの更新中にフリックする

3
Brij

これをコメントとして追加しようとしましたが、十分なポイントがありません。これが私のちらつきの問題をこれまでに助けた唯一のことです。彼の投稿に対するハンスのおかげです。私のような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
NoComprende

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

これらのスタイルフラグをもっと見つけることができます こちら

お役に立てば幸いです!

2
user7867434

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

2
revobtz

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

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

WS_EX_COMPOSITED、DoubleBuffered、WS_CLIPCHILDRENがちらつきを解決しなかった場合(私にとってはWS_CLIPCHILDRENはさらに悪化しました)、これを試してください:BackColor、ForeColor、またはForeColor、または他の色は、削除するだけで、単色のみを使用します。 haveだけを使用して透明性を使用すると思われるほとんどの場合、使用しません。コードとコントロールを再設計し、単色を使用します。私はひどく、ひどくちらつきがあり、プログラムは遅く動きました。透明度を削除すると、速度が大幅に向上し、ちらつきがゼロになります。

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

1
Daniel

私はこの質問が非常に古いことを知っていますが、それについての私の経験を伝えたいです。

.NET 4.0を使用するWindows 8でTabcontrolOnPaintをオーバーライドしたフォームでOnPaintBackGroundがちらつく問題がたくさんありました。

NOT USEOnPaintGraphics.DrawImageメソッドは、つまり、PaintEventArgsによって提供されるGraphicsに直接描画が行われ、すべての長方形を描画する場合にのみ有効でした。ちらつきが消えました。ただし、DrawImageメソッドを呼び出して、クリップされたビットマップを描画した場合(ダブルバッファリング用に作成された)、フリッカーが表示されます。

それが役に立てば幸い!

0
ZeroWorks

このちらつきの修正このフォントの修正 を組み合わせた後、ペイントでタイマーを開始するために独自のコードを少し追加して、画面外に移動したときに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 vs tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

追伸HotTrack = trueに設定する必要があります。これにより、このバグも修正されます。

0
user3732487