使用C#绘制带间隙的图形

3
我正在尝试用C#编写一个小的绘图程序。到目前为止一切都运行良好,唯一的问题是当我移动鼠标的速度足够快时,应该是实线的地方会出现间隙。我已经尝试了各种方法,从双缓冲到减少鼠标移动事件的间隔(实际上我没有找到任何方法来做到这一点,我认为这也会对系统上的其他进程产生不良影响^^)。
请问你能指点我正确的方向吗?我尝试重写面板的绘制方法,但是当我尝试这样做时,似乎什么也没有发生。
以下是代码:
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 Form1 : Form
    {

        bool paint;
        SolidBrush color;
        //size of brush
        int pinselGröße;
        List<Point> pointListe;

        public Form1 ()
        {
            InitializeComponent ();
            pointListe = new List<Point>();
            paint = false;
            color = new SolidBrush ( Color.Black );
            //get brush size from combobox 
            pinselGröße = Convert.ToInt32 ( nudBrushSize.Value );
        }

        private void btnExit_Click ( object sender, EventArgs e )
        {
            this.Close ();
        }

        private void btnClear_Click ( object sender, EventArgs e )
        {
            Graphics gfx = pnlCanvas.CreateGraphics ();
            gfx.Clear ( pnlCanvas.BackColor );
        }

        private void pnlCanvas_MouseDown ( object sender, MouseEventArgs e )
        {
            paint = true;
            Graphics grfx = pnlCanvas.CreateGraphics ();
            //draw a rectangle with brush "color" and pinselGröße as the brush size
            grfx.FillRectangle ( color, e.X, e.Y, pinselGröße, pinselGröße );  
        }

        private void pnlCanvas_MouseMove ( object sender, MouseEventArgs e )
        {
            if ( paint )
            {
                //Graphics grfx = pnlCanvas.CreateGraphics();  
                ////put old position of mouse into variable
                //int altePosX = e.X;
                //int altePosY = e.Y;
                ////grfx.FillEllipse ( color, e.X, e.Y, pinselGröße, pinselGröße );
                //grfx.FillRectangle(color, e.X, e.Y, pinselGröße, pinselGröße);
                //grfx.Dispose();
                pointListe.Add(e.Location);
                pnlCanvas.Invalidate();
            }
        }

        private void pnlCanvas_Paint(PaintEventArgs e)
        {

            e.Graphics.DrawLines(new Pen(color), pointListe.ToArray());
        }

        private void pnlCanvas_MouseUp ( object sender, MouseEventArgs e )
        {
            paint = false;
        }

        private void nudBrushSize_ValueChanged ( object sender, EventArgs e )

            //when value of combobox changes, read value into brush size variable
            pinselGröße = Convert.ToInt32 ( nudBrushSize.Value );
        }

        private void cmbColor_SelectedIndexChanged ( object sender, EventArgs e )
        {            
            int index = cmbColor.SelectedIndex;
            color.Dispose ();
            switch ( index )
            {
                case 0:
                    {
                        color = new SolidBrush ( Color.Black );
                        break;
                    }
                case 1:
                    {
                        Console.WriteLine ( "Geht" );
                        color = new SolidBrush ( Color.Red );
                        break;
                    }
                case 2:
                    {
                        color = new SolidBrush ( Color.Blue );
                        break;
                    }
                case 3:
                    {
                        color = new SolidBrush ( Color.Green );
                        break;
                    }
            }
        }


    }
}

当我这样做时:

private void pnlCanvas_MouseMove ( object sender, MouseEventArgs e )
        {
            if ( paint )
            {
                Graphics grfx = pnlCanvas.CreateGraphics();
                ////put old position of mouse into variable
                int altePosX = e.X;
                int altePosY = e.Y;
                //grfx.FillEllipse ( color, e.X, e.Y, pinselGröße, pinselGröße );
                grfx.FillRectangle(color, e.X, e.Y, pinselGröße, pinselGröße);
                grfx.Dispose();
                //pointListe.Add(e.Location);
                //pnlCanvas.Invalidate();
            }
        }

        //private void pnlCanvas_Paint(PaintEventArgs e)
        //{
        //    Console.Write("mjsda2");
        //    e.Graphics.DrawLines(new Pen(color), pointListe.ToArray());
        //}

我明白了,以下是您需要翻译的内容:

我得到了这个:

在此输入图片描述


给我们一个非英文编写的代码,你会减少愿意帮助你的人数。 - Snowbear
好的,抱歉,我添加了一些注释以便理解。 - LeonidasFett
3个回答

4

我不确定我们在绘图模式中采用哪种方式,因此这里有两个版本:

值得注意的是,您的绘图事件处理程序具有错误的签名,因此可能未连接到 pnlCanvas。

进行绘制代码时,您几乎永远不需要调用 CreateGraphics - 这通常意味着“您正在做错事”。

这将允许您通过单击点来绘制线条:

public partial class Form1 : Form
{

    SolidBrush color;
    List<Point> pointListe;
    Point _mousePoint;

    public Form1()
    {
        InitializeComponent();
        pointListe = new List<Point>();
        color = new SolidBrush(Color.Black);
    }

    private void btnClear_Click(object sender, EventArgs e)
    {
        pointListe.Clear();
        pnlCanvas.Invalidate();
    }

    private void pnlCanvas_MouseDown(object sender, MouseEventArgs e)
    {
        pointListe.Add(e.Location);
    }

    private void pnlCanvas_MouseMove(object sender, MouseEventArgs e)
    {
        _mousePoint = e.Location;
        pnlCanvas.Invalidate();
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        if (pointListe.Count > 1)
        {
            e.Graphics.DrawLines(new Pen(color), pointListe.ToArray());
        }

        if (pointListe.Any())
        {
            e.Graphics.DrawLine(new Pen(color), pointListe.Last(), _mousePoint);
        }
    }

}

这会绘制一条连续的线:

public partial class Form1 : Form
{

    SolidBrush color;
    List<List<Point>> _lines;
    Boolean _mouseDown;

    public Form1()
    {
        InitializeComponent();
        _lines = new List<List<Point>>();
        color = new SolidBrush(Color.Black);
        _mouseDown = false;
    }

    private void btnClear_Click(object sender, EventArgs e)
    {
        _lines.Clear();
        pnlCanvas.Invalidate();
    }

    private void pnlCanvas_MouseDown(object sender, MouseEventArgs e)
    {
        _mouseDown = true;
        _lines.Add(new List<Point>());
    }

    private void pnlCanvas_MouseMove(object sender, MouseEventArgs e)
    {
        if (_mouseDown)
        {
            _lines.Last().Add(e.Location);
            pnlCanvas.Invalidate();
        }
    }

    private void pnlCanvas_MouseUp(object sender, MouseEventArgs e)
    {
        _mouseDown = false;
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        foreach (var lineSet in _lines)
        {
            if (lineSet.Count > 1)
            {
                e.Graphics.DrawLines(new Pen(color), lineSet .ToArray());
            }   
        }

    }

}

2
+1给你详细的答案。我可以补充一下,如果他想要在绘制线条而不是像素时继续进行,他应该在后台位图上绘制,然后在Paint事件处理程序中将该位图绘制在画布上。此外,如果没有重写PaintBackground(取决于画布是什么),你的代码可能会产生闪烁,所以我建议先在位图上绘制,然后再将位图绘制在画布上。 - Nikola Davidovic
1
@user1589728,你需要在你的代码中设置 pnlCanvas.Paint+=new PaintEventHandler(pnlCanvas_Paint);。 - Nikola Davidovic
1
@NikolaD-Nick 是的,它有一点点闪烁。个人而言,我会将我的画布设置为UserControl,并将DoubleBuffered设置为true。 - Pondidum
@user1589728 请查看Nick的回复以添加处理程序,您也可以从设计器中的“属性”窗口执行此操作 - 找到绘制事件,并使用下拉列表选择您(现在正确的)绘制方法(这就是我用来连接您的代码的方法)。 - Pondidum
免责声明:此机器上没有Visual Studio,因此现在可能会有一些语法错误。希望这将让您绘制多个单独的线条,每条线都存储为“_lines”中的点列表。 - Pondidum
显示剩余3条评论

2

我不清楚间隙发生在哪里,但是你应该在MouseDown事件中添加你的第一个点吗?这可以解释你所看到的间隙类型吗?

另外,为什么要在MouseDown事件中填充矩形?

否则,也许可以提供这些间隙的截图。


我认为他正在鼠标按下时绘制,以支持通过单击画布来绘制单个“点”(仅按下和松开,无拖动)。 - tcarvin
但是除此之外,看起来忘记在列表中记录鼠标按下点也会导致间隙,初始的“点”后面跟着一个小的未涂色区域,然后是从第一个鼠标移动开始形成的线。很好地捕捉到了这个问题! - tcarvin
我在MouseMove方法中添加了点,但正如我所说,用这种方式什么也没有被绘制。只有当我删除覆盖方法并改为在MouseMove方法中绘制线条时,它才会给我产生间隙。 - LeonidasFett
好的,我已经添加了一张截图和一个小的解释说明我之前所做的事情。我注意到 pnlCanvas_Paint 方法从未被调用过,我尝试使用简单的控制台输出进行测试,但它从未被处理。 :( - LeonidasFett

2

鼠标移动事件会被跳过 - 鼠标实际上可以移动得非常快,比事件和您的应用程序更快。因此,您将无法获得一个漂亮的连续的鼠标移动流,每个像素都有一个。

您需要做的是跟踪前一个mousemove中得到的先前位置,然后不是绘制一个点而是从先前位置到当前位置绘制一条线。除非用户移动鼠标非常快,否则这将足以近似鼠标移动,您不会注意到它没有精确跟踪每个像素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接