6

ビットマップを使用する独自のユーザー コントロールには 2 つの問題があります。

  1. .NET の「Refresh」メソッドで再描画するとちらつきます。
  2. 性能が悪いです。

コントロールは、次の 3 つのビットマップで構成されます。

  • 静的背景画像。
  • 回転するローター。
  • ローターの角度によって別のイメージ。

使用されるすべてのビットマップの解像度は 500x500 ピクセルです。コントロールは次のように機能します: https://www.dropbox.com/s/t92gucestwdkx8z/StatorAndRotor.gif (gif アニメーションです)

ユーザー コントロールは、新しいローター角度を取得するたびに自分自身を描画する必要があります。したがって、次のようなパブリック プロパティ 'RotorAngle' があります。

public double RotorAngle
{
    get { return mRotorAngle; }
    set
    {
        mRotorAngle = value;
        Refresh();
    }
}

Refreshイベントを発生させPaintます。イベント ハンドラは次のOnPaintようになります。

private void StatorAndRotor2_Paint(object sender, PaintEventArgs e)
{
    // Draw the three bitmaps using a rotation matrix to rotate the rotor bitmap.
    Draw((float)mRotorAngle);
}

しかし、このコードを使用すると (他の独自のユーザー コントロールでうまく機能します)、コントロールが を介してダブル バッファリングされている場合、ユーザー コントロールはまったく描画されませんSetStyle(ControlStyles.OptimizedDoubleBuffer, true)。このフラグを true に設定しないと、再描画時にコントロールがちらつきます。

コントロールコンストラクターで設定しました:

SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.ContainerControl, false);
// User control is not drawn if "OptimizedDoubleBuffer" is true.
// SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);

まず、コントロールが描画されるたびに背景がクリアされるため、ちらつくと思いました。したがって、設定しSetStyle(ControlStyles.AllPaintingInWmPaint, true)ました。しかし、それは役に立ちませんでした。

では、なぜちらつくのでしょうか。他のコントロールは、このセットアップで非常にうまく機能します。そして、なぜコントロールが描画されないのですかSetStyle(ControlStyles.OptimizedDoubleBuffer, true)

Drawプロパティを変更した直後にメソッドを呼び出すと、コントロールがちらつかないことがわかりましたRotorAngle

public float RotorAngle
{
    get { return mRotorAngle; }
    set
    {
        mRotorAngle = value;
        Draw(mRotorAngle);
    }
}

しかし、これは特に全画面モードで非常に悪いパフォーマンスをもたらします。コントロールを 20 ミリ秒ごとに更新することはできません。自分で試すことができます。完全な Visual Studio 2008 ソリューションを以下に添付します。

では、なぜこんなにパフォーマンスが悪いのでしょうか。他の (自分の) コントロールを 20 ミリ秒ごとに更新しても問題ありません。それは本当にビットマップのせいですか?

2 つの問題を示すために、簡単なビジュアル Visual Studio 2008 ソリューションを作成しまし

ディレクトリ に実行可能ファイルがありますbin\Debug

ご協力いただきありがとうございます。

4

5 に答える 5

4

まず、LarsTech の回答に従って、Graphicsで提供されているコンテキストを使用する必要がありますPaintEventArgs。ハンドラーCreateGraphics()内で呼び出すと、正しく動作しなくなります。PaintOptimizedDoubleBuffer

次に、SetStyle ブロックに次を追加します。

SetStyle( ControlStyles.Opaque, true );

... Paint ハンドラーを呼び出す前に、基本クラス Control が背景色を塗りつぶさないようにします。

サンプルプロジェクトでこれをテストしましたが、ちらつきが解消されたようです。

于 2012-05-14T15:24:04.393 に答える
4

を使用せずCreateGraphics、ペイント イベントから渡された Graphic オブジェクトを使用します。

サイズを変更するとゴースト画像が表示されるため、次のように変更し、クリアを追加しました。

private void Draw(float rotorAngle, Graphics graphics)
{
  graphics.Clear(SystemColors.Control);
  graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

  // yada-yada-yada

  // do not dispose since you did not create it:
  // graphics.Dispose();
}

から呼び出されます:

private void StatorAndRotor2_Paint(object sender, PaintEventArgs e)
{
  Draw((float)mRotorAngle, e.Graphics);
}

コンストラクターでダブル バッファリングを有効にしますが、透過性は必要ないと思います。

SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.ContainerControl, false);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
//SetStyle(ControlStyles.SupportsTransparentBackColor, true);
于 2012-05-14T15:25:52.307 に答える
2

ペイントイベントで画面に描画するのは大変な作業です。メモリバッファでペイントを行うのは比較的高速です。

ダブルバッファリングはパフォーマンスを向上させます。ペイントグラフィックスに描画する代わりに、新しいグラフィックスを作成し、それにすべての描画を行います。

ビットマップの描画が完了したら、ビットマップ全体をPaintEventArgsから受け取ったe.Graphicsにコピーします。

GDI +とC#を使用したちらつきのない描画

于 2012-05-14T15:17:32.483 に答える
0

私の答えは、https://stackoverflow.com/a/2608945/455904からインスピレーションを得て、上記のLarsTechsの答えからヒントを得ました。

すべての OnPaints で完全な画像を再生成する必要がないようにするには、変数を使用して生成された画像を保持します。

private Bitmap mtexture;

Draw() を使用してテクスチャを生成する

private void Draw(float rotorAngle)
{
    using (var bufferedGraphics = Graphics.FromImage(mtexture))
    {
        Rectangle imagePosition = new Rectangle(0, 0, Width, Height);
        bufferedGraphics.DrawImage(mStator, imagePosition);
        bufferedGraphics.DrawImage(RotateImage(mRotor, mRotorAngle), imagePosition);

        float normedAngle = mRotorAngle % cDegreePerFullRevolution;

        if (normedAngle < 0)
            normedAngle += cDegreePerFullRevolution;

        if (normedAngle >= 330 || normedAngle <= 30)
            bufferedGraphics.DrawImage(mLED101, imagePosition);
        if (normedAngle > 30 && normedAngle < 90)
            bufferedGraphics.DrawImage(mLED001, imagePosition);
        if (normedAngle >= 90 && normedAngle <= 150)
            bufferedGraphics.DrawImage(mLED011, imagePosition);
        if (normedAngle > 150 && normedAngle < 210)
            bufferedGraphics.DrawImage(mLED010, imagePosition);
        if (normedAngle >= 210 && normedAngle <= 270)
            bufferedGraphics.DrawImage(mLED110, imagePosition);
        if (normedAngle > 270 && normedAngle < 330)
            bufferedGraphics.DrawImage(mLED100, imagePosition);
    }
}

OnPaint のオーバーライドでコントロールにテクスチャを描画する

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Rectangle imagePosition = new Rectangle(0, 0, Width, Height);
    e.Graphics.DrawImage(mtexture, imagePosition);
}

必要に応じてテクスチャを Draw() に OnInvalidated() をオーバーライドします。

protected override void OnInvalidated(InvalidateEventArgs e)
{
    base.OnInvalidated(e);

    if (mtexture != null)
    {
        mtexture.Dispose();
        mtexture = null;
    }

    mtexture = new Bitmap(Width, Height);
    Draw(mRotorAngle);
}

Draw を呼び出す代わりに、イメージを無効にします。これにより、OnInvalidated と OnPaint を使用して再描画されます。

public float RotorAngle
{
    get { return mRotorAngle; }
    set
    {
        mRotorAngle = value;
        Invalidate();
    }
}

そこにすべて入っていることを願っています:)

于 2012-05-14T16:05:17.543 に答える
0

ご助力ありがとうございます。

ちらつきが解消されます。:)

ここで、LarsTech の提案に従い、 のGraphicsオブジェクトを使用しますPaintEventArgs

内部ハンドラが の正しい機能を妨げているというヒントをくれたlnmxに感謝します。これは、有効になっていてもちらつきの問題を説明しています。私はそれを知りませんでしたし、MSDN Libraryにもこれが見つかりませんでした。以前のコントロールでは、 のオブジェクトも使用しました。CreateGraphics()PaintOptimizedDoubleBufferOptimizedDoubleBufferGraphicsPaintEventArgs

Sallow さん、ご尽力いただきありがとうございます。今日あなたのコードをテストし、フィードバックします。正しいダブルバッファリングにもかかわらず、まだパフォーマンスの問題があるため、これによりパフォーマンスが向上することを願っています。

元のコードには、別のパフォーマンス上の問題がありました。

かわった

graphics.DrawImage(mStator, imagePosition);
graphics.DrawImage(RotateImage(mRotor, rotorAngle), imagePosition);

graphics.DrawImage(mStator, imagePosition);
Bitmap rotatedImage = RotateImage(mRotor, rotorAngle);
graphics.DrawImage(rotatedImage, imagePosition);
rotatedImage.Dispose(); // Important, otherwise the RAM will be flushed with bitmaps.
于 2012-05-15T06:37:23.383 に答える