簡體   English   中英

C# 圖形閃爍

[英]C# graphics flickering

我正在開發某種繪圖程序,但在繪制橡皮筋線時移動鼠標光標時出現閃爍問題。 我希望你能幫我去除那條閃爍的線,這是代碼:

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 GraphicsTest
{
    public partial class Form1 : Form
    {
        int xFirst, yFirst;
        Bitmap bm = new Bitmap(1000, 1000);
        Graphics bmG;
        Graphics xG;
        Pen pen = new Pen(Color.Black, 1);
        bool draw = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bmG = Graphics.FromImage(bm);
            xG = this.CreateGraphics();
            bmG.Clear(Color.White);
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            xFirst = e.X;
            yFirst = e.Y;
            draw = true;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
            draw = false;
            xG.DrawImage(bm, 0, 0);
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (draw)
            {
                xG.DrawImage(bm, 0, 0);
                xG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            xG.DrawImage(bm, 0, 0);
        }
    }
}

首先不要使用CreateGraphics()除非你絕對必須。 將事件處理程序綁定到OnPaint並在您想要刷新表面時調用Invalidate()

如果您不希望它閃爍,則需要對繪圖表面進行雙重緩沖。 最簡單的方法是將表單的DoubleBuffered屬性設置為 True。

如果您打算將其擴展為將繪圖繪制到 PictureBox 控件,我強烈建議您。 默認情況下,PictureBox 是雙緩沖的,允許您更簡單地控制繪圖區域。

在代碼中:

public partial class Form1 : Form
    {
    int xFirst, yFirst;
    Bitmap bm = new Bitmap(1000, 1000);
    Graphics bmG;
    Pen pen = new Pen(Color.Black, 1);
    bool draw = false;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        bmG = Graphics.FromImage(bm);
        bmG.Clear(Color.White);
    }

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        xFirst = e.X;
        yFirst = e.Y;
        draw = true;
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
        draw = false;
        Invalidate();
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (draw)
        {
            Invalidate();
        }
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        if (draw) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
        } else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }
}

編輯:

另一個問題,您正在創建一個私人Pen成員。 筆(和畫筆,以及許多 GDI+ 對象)表示需要處理的非托管對象的句柄,否則您的程序將泄漏。 要么將它們包裝在using語句中(首選且異常安全的方式),要么在表單的Dispose方法中顯式處理它們。

或者,在 System.Drawing 中,您可以訪問一些不需要(也不應該)處理的預先構建的鋼筆和畫筆。 像這樣使用它們:

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        if (draw) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(Pens.Black, xFirst, yFirst, e.X, e.Y);
        } else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }

它閃爍的原因是您正在繪制背景(立即顯示在屏幕上,擦除線條)然后疊加線條。 因此,該線不斷消失和出現,顯示閃爍。

對此的最佳解決方案稱為雙緩沖。 您所做的是將整個圖像繪制到“屏幕外”位圖,並僅在完成后顯示在屏幕上。 因為您只顯示完成的圖像,所以沒有閃爍效果。 您應該能夠設置 this.DoubleBuffered = true 讓 WinForms 為您完成所有艱苦的工作。

注意:您真的不應該在您的油漆處理程序之外進行繪制 - 理想情況下,您應該 Invalidate() 需要重繪的區域,然后您的油漆處理程序將只重繪該區域(根據需要使用任何重疊線等)。

固定和工作代碼。

public partial class Form1 : Form
{
    int x1, y1, x2, y2;
    bool drag = false;

    Bitmap bm = new Bitmap(1000, 1000);
    Graphics bmg;


    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        bmg = Graphics.FromImage(bm);
    }

    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        drag = true;
        x1 = e.X;
        y1 = e.Y;
    }

    private void pictureBox_MouseUp(object sender, MouseEventArgs e)
    {
        drag = false;

        bmg.DrawLine(Pens.Black, x1, y1, e.X, e.Y);
        pictureBox.Invalidate();
    }

    private void pictureBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (drag)
        {
            x2 = e.X;
            y2 = e.Y;
            pictureBox.Invalidate();
        }
    }

    private void pictureBox_Paint(object sender, PaintEventArgs e)
    {
        if (drag) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(Pens.Black, x1, y1, x2, y2);            
        }
        else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }
}

我用它來管理雙緩沖到面板中:

myPanel.GetType().GetMethod("SetStyle",
    System.Reflection.BindingFlags.Instance |
    System.Reflection.BindingFlags.NonPublic).Invoke(myPanel,
        new object[]
        {
            System.Windows.Forms.ControlStyles.UserPaint | 
            System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
            System.Windows.Forms.ControlStyles.DoubleBuffer, true
        });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM