28

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

これらのコントロールは問題ありません (顧客によると非常に見栄えが良い) が、DoubleBuffer にもかかわらず、特に同じフォームに 20++ ボタン (たとえば) がある場合に、再描画が見られることがあります。フォームをロードすると、ボタンが描画されるのが見えます…これは面倒です。

私たちのボタンが地球上で最速のものではないことは確かですが、私の質問は、ダブルバッファが「オン」の場合、すべての再描画がバックグラウンドで行われ、Windows サブシステムが結果を「即座に」表示するべきではないということです。

一方、ラベルを作成する「複雑な」foreach ループがある場合は、ループの前にパネルのレイアウトを一時停止し、ループが終了したときにパネルのレイアウトを再開する場合は、ラベルをパネルに追加し (ダブル バッファー)、それらのプロパティを変更します。これらのコントロール (ラベルとボタン) はすべて「ほぼ瞬時に」表示されるべきではないでしょうか? これはそのようには起こりません。パネルがいっぱいになっているのがわかります。

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

4

10 に答える 10

13

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

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

したがって、50 個の子コントロールがあると、背景ペイントのために親コントロールで追加の 50 個のペイント呼び出しが生成されます。また、勾配は一般に低速であるため、パフォーマンスの低下が見られます。

お役に立てれば。

于 2009-05-18T18:30:16.260 に答える
13

この問題も見てきました。

これを「修正」する 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);
于 2009-05-07T15:03:31.530 に答える
9

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

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

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

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

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

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

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

編集:

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

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

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

ダブルバッファリングを簡単に言うと、 これは非常に単純で、数行のコードです。ペイント イベントで、オブジェクトにレンダリングする代わりにビットマップにレンダリングし、Graphicsそのビットマップをオブジェクトに描画しGraphicsます。

于 2009-05-07T16:37:04.457 に答える
4

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

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

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

于 2009-05-07T14:52:52.583 に答える
3

コントロールとその子のペイントを一時停止するにはどうすればよいですか?という私の質問に対する答えを確認することをお勧めします。より良い一時停止/再開のために。

于 2009-10-30T16:01:26.627 に答える
2

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

あなたのコントロールで

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

その塗装方法で

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
}

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

于 2009-07-16T10:36:38.320 に答える
2

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

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

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

于 2009-08-17T21:26:42.360 に答える
0

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

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

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

于 2009-06-16T13:09:37.040 に答える
0

表示したいユーザーコントロールを切り替えるときに、テーブルレイアウトパネルで同じ問題が発生しました。

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

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;
        }
    }
}
于 2010-02-04T19:23:03.147 に答える
0

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

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

于 2016-01-24T04:14:10.033 に答える