web-dev-qa-db-ja.com

Winforms:SuspendLayout / ResumeLayoutでは不十分ですか?

いくつかの「カスタムコントロール」のライブラリがあります。基本的に、独自のボタン、丸みを帯びたコーナーパネル、カスタムペイントを備えたいくつかのグループボックスがあります。 OnPaintメソッドの「数学」にもかかわらず、コントロールはかなり標準的です。ほとんどの場合、丸みを帯びたコーナーを描画し、背景にグラデーションを追加するだけです。これらすべてにGDI +を使用しています。

これらのコントロールは問題ありません(お客様によると非常に見栄えが良いです)。ただし、DoubleBufferにもかかわらず、特に同じフォームに(たとえば)20 ++ボタンがある場合、一部の再描画を確認できます。フォームの読み込み時に、ボタンが描画されているのがわかります...

私たちのボタンは地球上で最速のものではないと確信していますが、私の質問は、ダブルバッファが「オン」の場合、再描画がすべてバックグラウンドで実行され、Windowsサブシステムが「瞬時に」結果を表示する必要があるのではないですか。

一方、ラベルを作成する「複雑な」foreachループがある場合は、それらをパネル(ダブルバッファー)に追加してプロパティを変更します。ループの前にパネルのレイアウトを中断し、ループのときにパネルのレイアウトを再開します。以上、これらすべてのコントロール(ラベルとボタン)が「ほとんど瞬時に」表示されないようにしてください。これはそのようには起こらず、パネルが埋められているのがわかります。

なぜこれが起こらないのか?サンプルコードなしでは評価するのが難しいことは知っていますが、それも複製するのは難しいです。私はカメラでビデオを作ることができましたが、これを信じてください、それは速くありません:)

26

この問題も確認しました。

「修正」する方法の1つは、準備ができるまでコントロールの描画を完全に中断することです。これを実現するために、コントロールにWM_SETREDRAWメッセージを送信します。

// Note that WM_SetRedraw = 0XB

// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);

...

// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
12

注目すべき点の1つは、パネルの子コントロールのいずれかにBackColor = Transparentを設定しているかどうかです。特に親パネルがグラデーションを使用している場合、BackColor = Transparentはレンダリングパフォーマンスを大幅に低下させます。

Windowsフォームは実際の透明度を使用せず、「偽の」透明度を使用します。各子コントロールのPaint呼び出しは親にPaint呼び出しを生成するので、親はその背景をペイントして、子コントロールがコンテンツをペイントして透明に見えるようにすることができます。

そのため、50の子コントロールがあり、背景の描画のために親コントロールで追加の50ペイント呼び出しを生成する場合。また、勾配は一般的に遅いため、パフォーマンスが低下します。

お役に立てれば。

パフォーマンスの観点から問題に取り組みます。

ラベルを作成してパネルに追加し(ダブルバッファリング)、プロパティを変更するforeachループ

それが実行される順序である場合、改善の余地があります。最初にすべてのラベルを作成し、それらのプロパティを変更して、準備が整ったら、パネルに追加します:Panel.Controls.AddRange(Control[])

ほとんどの場合、丸みを帯びたコーナーを描画し、背景にグラデーションを追加するだけです

同じことを何度も繰り返していますか?グラデーションはどのように生成されますか?画像の書き込みが遅くなることはありません。 1680x1050のグラデーションをインメモリで作成する必要がありましたが、Stopwatchには速すぎるため、グラデーションの描画はそれほど難しくありません。

私のアドバイスは、いくつかのものをキャッシュしてみることです。ペイントを開き、コーナーを描画してディスクに保存するか、メモリ内に1度だけ画像を生成します。次に、必要に応じてロード(およびサイズ変更)します。グラデーションについても同じです。

異なるボタンは異なる色を持っていますが、同じモチーフの場合でも、ペイントなどを使用してビットマップを作成し、実行時にそれをロードして、色の値に別の色を掛けることができます。

編集:

ループの前にパネルのレイアウトを中断し、ループが終了したときにパネルのレイアウトを再開する場合

これは、SuspendLayoutとResumeLayoutの目的ではありません。レイアウトロジック、つまりコントロールの自動配置を一時停止します。 FlowLayoutPanelおよびTableLayoutPanelに最も関連しています。

ダブルバッファリングについては、それがカスタム描画コードに適用されるかどうかはわかりません(まだ試していません)。自分で実装すべきだと思います。

一言で言えばダブルバッファリング:非常にシンプルで、数行のコードです。 Paintイベントで、Graphicsオブジェクトにレンダリングする代わりにビットマップにレンダリングし、そのビットマップをGraphicsオブジェクトに描画します。

9
DonkeyMaster

DoubleBufferedプロパティに加えて、これをコントロールのコンストラクターに追加してみてください:

SetStyle(ControlStyles.OptimizedDoubleBuffer | 
         ControlStyles.AllPaintingInWmPaint, true);

そして、それが十分ではない場合(私は手足に出て、そうではないと言うつもりです)、 この質問 への私の回答を見て、再描画を一時停止/再開することを検討してくださいパネルまたはフォームの。これにより、レイアウト操作が完了し、完了したらすべての描画を実行できます。

4
Adam Robinson

あなたは私の質問への答えを見てみたいかもしれません コントロールとその子のペイントを一時停止するにはどうすればいいですか? より良い一時停止/再開のために。

2
Simon

あなたが探しているのは、まるで1つの大きなビットマップのように、アプリケーション全体が一度に描画される「複合」ディスプレイのようです。これは、アプリケーションを囲む「クロム」(タイトルバー、サイズ変更ハンドル、スクロールバーなど)を除いて、WPFアプリケーションで何が起こるかです。

通常、いくつかのウィンドウスタイルを変更していない限り、各Windowsフォームコントロールが自身のペイントを担当します。つまり、すべてのコントロールはWM_ Paint、WM_ NCPAINT、WM_ERASEBKGNDなどで関連するメッセージをペイントして亀裂を取得し、これらのメッセージを個別に処理します。これが意味することは、ダブルバッファリングは、処理している単一のコントロールにのみ適用されるということです。クリーンで合成された効果にいくらか近づくには、描画しているカスタムコントロールだけでなく、それらが配置されているコンテナーコントロールも考慮する必要があります。たとえば、GroupBoxを含むフォームにカスタム描画ボタンが含まれている場合、これらの各コントロールのDoubleBufferedプロパティをTrueに設定する必要があります。このプロパティは保護されていることに注意してください。つまり、さまざまなコントロールを継承して(ダブルバッファリングプロパティを設定するため)、リフレクションを使用して保護プロパティを設定します。また、すべてのWindowsフォームコントロールがDoubleBufferedプロパティを尊重するわけではありません。内部的にはネイティブの「共通」コントロールのラッパーにすぎないためです。

Windowsをターゲットにしている場合は、複合フラグを設定する方法がありますXP(そしておそらく後で)。WS_EX_ COMPOSITEDウィンドウスタイルがあります。結果を混合する前に使用しました。 WPF/WinFormハイブリッドアプリケーションではうまく機能せず、DataGridViewコントロールでもうまく機能しません。このルートを使用する場合は、奇妙な結果が見られたため、さまざまなマシンで多くのテストを行ってください。最後に、私はこのアプローチの使用を放棄しました。

2
Notre

たぶん、最初にコントロールのみの「可視」(プライベート)バッファに描画してから、それをレンダリングします。

あなたのコントロールで

BufferedGraphicsContext gfxManager;
BufferedGraphics gfxBuffer;
Graphics gfx;

グラフィックをインストールする機能

private void InstallGFX(bool forceInstall)
{
    if (forceInstall || gfxManager == null)
    {
        gfxManager = BufferedGraphicsManager.Current;
        gfxBuffer = gfxManager.Allocate(this.CreateGraphics(), new Rectangle(0, 0, Width, Height));
        gfx = gfxBuffer.Graphics;
    }
}

そのPaintメソッドで

protected override void OnPaint(PaintEventArgs e)
{
    InstallGFX(false);
    // .. use GFX to draw
    gfxBuffer.Render(e.Graphics);
}

そのサイズ変更メソッドで

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);
    InstallGFX(true); // To reallocate drawing space of new size
}

上記のコードはある程度テストされています。

2
gsgc

表示したいユーザーコントロールを切り替えると、tablelayoutpanelで同じ問題が発生しました。

テーブルを継承するクラスを作成してちらつきを完全になくし、ダブルバッファリングを有効にしました。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace myNameSpace.Forms.UserControls
{
    public class TableLayoutPanelNoFlicker : TableLayoutPanel
    {
        public TableLayoutPanelNoFlicker()
        {
            this.DoubleBuffered = true;
        }
    }
}
0
Kevin

私は過去に多くの同様の問題を抱えていましたが、解決する方法は、標準のMicrosoftコントロールではなく、サードパーティのUIスイート(つまり DevExpress )を使用することでした。

マイクロソフトの標準コントロールを使い始めましたが、コントロールによって引き起こされた問題を常にデバッグしていることがわかりました。 Microsoftは通常、特定された問題のいずれも修正せず、適切な回避策を提供するためにほとんど機能しないという事実により、問題はさらに悪化します。

DevExpressに切り替えましたが、言うべきことは他にありません。製品はしっかりしており、優れたサポートとドキュメントを提供しており、実際に顧客の声に耳を傾けています。質問や問題があるときはいつでも、24時間以内に友好的な返事が返ってきました。いくつかのケースでバグを見つけましたが、どちらの場合も、次のサービスリリースの修正を実装しました。

0

コントロールが不足しているフォントを参照しているフォームで、不正なwinformがちらつくのを見ました。

これはおそらく一般的ではありませんが、他のすべてを試した場合は調べる価値があります。

0
jm.