web-dev-qa-db-ja.com

C#winformsのちらつきを止める方法

基本的にペイントアプリケーションのようなプログラムがあります。ただし、私のプログラムにはちらつきの問題があります。私のコードには次の行があります(ちらつきを取り除く必要があります-しません):

this.SetStyle(ControlStyles.AllPaintingInWmPaint 
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

私のコード(形状のスーパークラスとサブクラスを引いたものは次のとおりです:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Paint
{
    public partial class Paint : Form
    {
        private Point startPoint;
        private Point endPoint;
        private Rectangle rect = new Rectangle();
        private Int32 brushThickness = 0;
        private Boolean drawSPaint = false;
        private List<Shapes> listOfShapes = new List<Shapes>();
        private Color currentColor;
        private Color currentBoarderColor;
        private Boolean IsShapeRectangle = false;
        private Boolean IsShapeCircle = false;
        private Boolean IsShapeLine = false;

        public SPaint()
        {

            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

            currentColor = Color.Red;
            currentBoarderColor = Color.DodgerBlue;
            IsShapeRectangle = true; 
        }

        private void panelArea_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = panelArea.CreateGraphics();

            if (drawSPaint == true)
            {

                Pen p = new Pen(Color.Blue);
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

                if (IsShapeRectangle == true)
                {
                    g.DrawRectangle(p, rect);
                }
                else if (IsShapeCircle == true)
                {
                    g.DrawEllipse(p, rect);
                }
                else if (IsShapeLine == true)
                {
                    g.DrawLine(p, startPoint, endPoint);
                }
            }
            foreach (Shapes shape in listOfShapes)
            {

                shape.Draw(g);

            }
        }

        private void panelArea_MouseDown(object sender, MouseEventArgs e)
        {

            startPoint.X = e.X;
            startPoint.Y = e.Y;

            drawSPaint = true;
        }

        private void panelArea_MouseMove(object sender, MouseEventArgs e)
        {


            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {

                if (e.X > startPoint.X)
                {
                    rect.X = startPoint.X;
                    rect.Width = e.X - startPoint.X;
                }
                else
                {
                    rect.X = e.X;
                    rect.Width = startPoint.X - e.X;
                }
                if (e.Y > startPoint.Y)
                {
                    rect.Y = startPoint.Y;
                    rect.Height = e.Y - startPoint.Y;
                }
                else
                {
                    rect.Y = e.Y;
                    rect.Height = startPoint.Y - e.Y;
                }


                panelArea.Invalidate();

            }

        }

        private void panelArea_MouseUp(object sender, MouseEventArgs e)
        {

            endPoint.X = e.X;
            endPoint.Y = e.Y;

            drawSPaint = false;

            if (rect.Width > 0 && rect.Height > 0)
            {
                if (IsShapeRectangle == true)
                {
                    listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeCircle == true)
                {
                    listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeLine == true)
                {
                    listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
                }

                panelArea.Invalidate();
            }
        }


        private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = true;
            IsShapeCircle = false;
            IsShapeLine = false; 
        }

        private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = false;
            IsShapeCircle = true;
            IsShapeLine = false; 
        }

        private void lineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeCircle = false;
            IsShapeRectangle = false;
            IsShapeLine = true; 
        }

        private void ThicknessLevel0_Click(object sender, EventArgs e)
        {
            brushThickness = 0; 
        }

        private void ThicknessLevel2_Click(object sender, EventArgs e)
        {
            brushThickness = 2; 
        }

        private void ThicknessLevel4_Click(object sender, EventArgs e)
        {
            brushThickness = 4; 
        }

        private void ThicknessLevel6_Click(object sender, EventArgs e)
        {
            brushThickness = 6; 
        }

        private void ThicknessLevel8_Click(object sender, EventArgs e)
        {
            brushThickness = 8; 
        }

        private void ThicknessLevel10_Click(object sender, EventArgs e)
        {
            brushThickness = 10; 
        }

        private void ThicknessLevel12_Click(object sender, EventArgs e)
        {
            brushThickness = 12; 
        }

        private void ThicknessLevel14_Click(object sender, EventArgs e)
        {
            brushThickness = 14; 
        }

        private void FillColour_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }

        private void button1_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentBoarderColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }


    }
}

ちらつきを止めるにはどうすればよいですか?

* PDATE: *このコードは、フォームに直接描画しているときに実際にうまく機能します。ただし、パネルに描画しようとすると、ちらつきが問題になります

63
BigBug

最後にちらつきを解決しました。フォームではなくパネルに描画していたため、以下のコード行ではちらつきを解決できません。

this.SetStyle(
    ControlStyles.AllPaintingInWmPaint | 
    ControlStyles.UserPaint | 
    ControlStyles.DoubleBuffer, 
    true);

SetStyleは 'YourProject.YourProject'型(またはその派生型)である必要があるため、そのようなクラスを作成する必要があります(そのため、SPaint.SPaintから派生するMyPanelを使用して、ダブルバッファリングを直接使用できるようにします)パネル-フォームではなく):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPaint; 

namespace YourProject
{
    public class MyPanel : System.Windows.Forms.Panel
    {
        public MyPanel()
        {
            this.SetStyle(
                System.Windows.Forms.ControlStyles.UserPaint | 
                System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | 
                System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, 
                true);
        }
    }
}

これを行った後(自分が何をしているかを本当に理解していない限り、デザイナーコードを編集してはいけませんが)、Form.Designer.csを編集する必要があります。このファイル内には、次のようなコードがあります。

this.panelArea = new YourProject.MyPanel();

上記の行を次のように変更する必要があります。

this.panelArea = new MyPanel(); 

これらの手順を完了した後、ペイントプログラムはちらつきません。

同じ問題を抱えている他の人にとっては、問題は最終的に解決されます。

楽しい!

52
BigBug

「よりクリーンなソリューション」の場合、およびベースパネルを引き続き使用するには、Reflectionを使用して、ダブルバッファリングを実装します。このコードを、描画するパネルを保持するフォームに追加します。

    typeof(Panel).InvokeMember("DoubleBuffered", 
    BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
    null, DrawingPanel, new object[] { true });

「DrawingPanel」は、ダブルバッファリングを実行するパネルの名前です。

質問されてからかなりの時間が経過したことは知っていますが、これは将来誰かを助けるかもしれません。

58
viper

これをコピーしてプロジェクトに貼り付けます

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

これにより、フォームレベルの下からすべてのコントロールのダブルバッファリングが有効になります。そうでない場合は、ダブルバッファリングをそれぞれ個別に有効にする必要があります。

26
user3641393

私も同じ問題を抱えています。ちらつきを100%取り除くことはできませんでしたが(ポイント2を参照)、これを使用しました

protected override void OnPaint(PaintEventArgs e) {}

と同様

this.DoubleBuffered = true;

ちらつきの主な問題は、

  1. 物事を正しい順序で描く!
  2. 描画関数が約1/60秒未満であることを確認してください

winformsは、フォームを再描画する必要があるたびにOnPaintメソッドを呼び出します。フォーム上でマウスカーソルを移動すると、再描画イベントを呼び出すことができるなど、検証を無効にする方法は多数あります。

OnPaintについての重要な注意点は、毎回ゼロから開始するのではなく、現在の場所から開始することです。背景色を塗りつぶすと、ちらつきが発生する可能性があります。

最後にgfxオブジェクト。 OnPaint内では、グラフィックスオブジェクトを再作成する必要がありますが、画面サイズが変更された場合のみです。オブジェクトの再作成は非常に高価であり、再作成する前に破棄する必要があります(ガベージコレクションは100%正しく処理できないため、ドキュメントに記載されています)。クラス変数を作成しました

protected Graphics gfx = null;

OnPaintでローカルに使用しましたが、これはクラス内の他の場所でgfxオブジェクトを使用する必要があったためです。そうでなければ、これをしないでください。 OnPaintでのみペイントする場合は、e.Graphics !!

// clean up old graphics object
gfx.Dispose();

// recreate graphics object (dont use e.Graphics, because we need to use it 
// in other functions)
gfx = this.CreateGraphics();

お役に立てれば。

14
ohmusama

これは本当に古い質問ですが、誰かが役に立つかもしれません。
Viperの答えを少し強化したいと思います。

Panelクラスの単純な拡張を作成し、リフレクションによって設定プロパティを非表示にできます。

public static class MyExtensions {

    public static void SetDoubleBuffered(this Panel panel) {
        typeof(Panel).InvokeMember(
           "DoubleBuffered",
           BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
           null,
           panel,
           new object[] { true });
    }
}

Panel変数の名前がmyPanelの場合、呼び出すことができます
myPanel.SetDoubleBuffered();
以上です。コードはずっときれいに見えます。

3
thorgil

ここでは、ダブルバッファリングはあまり役に立ちません。私は少し前にこれに遭遇し、かなり不器用な方法で別のパネルを追加することになりましたが、それは私のアプリケーションのために機能しました。

持っている元のパネル(panelArea)を透明な領域にし、たとえばpanelDrawと呼ぶ2番目のパネルの上に置きます。必ずpanelAreaを前面に配置してください。私はこれをホイップし、ちらつきを取り除きましたが、描画された形状を塗りつぶしたままにしていたため、完全な解決策でもありませんでした。

透明なパネルは、元のパネルからいくつかのペイントアクションをオーバーライドすることで作成できます。

public class ClearPanel : Panel
{
    public ClearPanel(){}

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= 0x00000020;
            return createParams;
        }
    }

    protected override void OnPaintBackground(PaintEventArgs e){}
}

「panelArea」のMouseMoveイベント中に一時的な形状の描画を処理し、MouseUpイベントで「panelDraw」のみを再描画するという考え方です。

// Use the panelDraw Paint event to draw shapes that are done
void panelDraw_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelDraw.CreateGraphics();

    foreach (Rectangle shape in listOfShapes)
    {
        shape.Draw(g);
    }
}

// Use the panelArea_Paint event to update the new shape-dragging...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelArea.CreateGraphics();

    if (drawSETPaint == true)
    {
        Pen p = new Pen(Color.Blue);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

        if (IsShapeRectangle == true)
        {
            g.DrawRectangle(p, rect);
        }
        else if (IsShapeCircle == true)
        {
            g.DrawEllipse(p, rect);
        }
        else if (IsShapeLine == true)
        {
            g.DrawLine(p, startPoint, endPoint);
        }
    }
}

private void panelArea_MouseUp(object sender, MouseEventArgs e)
{

    endPoint.X = e.X;
    endPoint.Y = e.Y;

    drawSETPaint = false;

    if (rect.Width > 0 && rect.Height > 0)
    {
        if (IsShapeRectangle == true)
        {
            listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeCircle == true)
        {
            listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeLine == true)
        {
            listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
        }

        panelArea.Invalidate();
    }

    panelDraw.Invalidate();
}
3
Tom

この状態では、ダブルバッファを有効にする必要があります。現在のフォームを開き、フォームのプロパティに移動して、ダブルバッファーを適用しますtrue;または、このコードを書くこともできます。

this.DoubleBuffered = true;     

フォームの読み込み中。

2
user2922221

OnPaintBackgroundをオーバーライドし、バックグラウンドを処理して自分で消去することをお勧めします。コントロール全体をペイントしていることがわかっている場合は、OnPaintBackgroundで何もできず(ベースメソッドを呼び出さないでください)、背景色が最初にペイントされるのを防ぎます

2
Matt

上記のすべてが機能しない場合は、Microsoftチュートリアルへの独自のダブルバッファーリンクをいつでも作成できます。 https://docs.Microsoft.com/en-us/dotnet/framework/winforms/advanced/how- to-reduce-graphics-flicker-with-double-buffering-for-forms-and-controls

それがあなたのために働くことを願っています

1

これは.netで円を動かすプログラムで、ちらつきません。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace CircleMove
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        int x=0,y=0;
        Thread t;

        public MainForm()
        {

            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();

            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
        }
        void MainFormPaint(object sender, PaintEventArgs e)
        {
            Graphics g=e.Graphics;
            Pen p=new Pen(Color.Orange);
            Brush b=new SolidBrush(Color.Red);
        //  g.FillRectangle(b,0,0,100,100);
            g.FillEllipse(b,x,y,100,100);
        }
        void MainFormLoad(object sender, EventArgs e)
        {
            t=new Thread(  new ThreadStart(

                ()=>{
                    while(true)
                    {
                        Thread.Sleep(10);
                        x++;y++;
                        this.Invoke(new Action(
                            ()=>{

                                this.Refresh();
                                this.Invalidate();
                                this.DoubleBuffered=true;
                                }
                                            )
                                        );
                    }
                    }
                                            )

                        );

            t.Start();
        }
    }
}
1
sanjay

現在のフォームに描画ロジックを挿入してみてください

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
}

方法。この場合、パラメーターeを使用してGraphicsオブジェクトを取得する必要があります。 e.Graphicsプロパティを使用します。次に、フォームを再描画する必要がある場合は常に、このフォームに対してInvalidate()メソッドを呼び出す必要があります。 PS:DoubleBufferedをtrueに設定する必要があります。

0
DeeDee

メモリが不足している場合(ダブルバッファリングのメモリコストが不要な場合)、フリッカーをなくすことはできませんが、REDUCEを実行する1つの方法は、現在のシーンの背景色を主要な色に設定することです。

これが役立つ理由:フリッカーは背景色の瞬間的なフラッシュであり、子コントロールまたはカスタム描画コードを描画する前にOSが描画します。そのフラッシュが、表示される最終色に近い色である場合、目立たなくなります。

どの色から始めたらよいかわからない場合は、50%グレーから始めます。これは白黒の平均であるため、シーンのほとんどの色に近くなるためです。

myFormOrControl.BackColor = Color.Gray;
0
ToolmakerSteve

フォームが表示されたらthis.Refresh()するだけです。

0
Jam