winforms 在visual studio 2013中从面板复制徒手绘图

aor9mmx1  于 2023-01-14  发布在  其他
关注(0)|答案(1)|浏览(96)

我想在visual studio上的一个窗体(图片框)中徒手绘制,并在另一个面板/图片框上复制相同的图形(我绘制的)。另外,它们不应该是点形成一条线,而是一条连续的线。请帮助。

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

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Pen p_white;

        bool draw = true;

        private Graphics objgraphics;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Pen p_black = new Pen(new SolidBrush(Color.Black));

            if (draw)
            {
                objgraphics = panel1.CreateGraphics();

            }

        }

        /*private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            bool draw = true;
        }*/

        private void panel1_MouseMove_1(object sender, MouseEventArgs e)
        {
                      Rectangle rEllipse = new Rectangle();

            switch (e.Button)
            {

                case MouseButtons.Left:

                    rEllipse.X = e.X;
                    rEllipse.Y = e.Y;
                    rEllipse.Width = 5;
                    rEllipse.Height = 5;
                    objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
                    break;

                case MouseButtons.Right:

                    rEllipse.X = e.X;
                    rEllipse.Y = e.Y;
                    rEllipse.Width = 3;
                    rEllipse.Height = 3;
                    objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
                    break;

                default:
                    return;
           } 

        }

        /*private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            bool draw = false;
        } */

        private void form_Paint(object sender, EventArgs e)
        {

        }

        private void panel2_Paint(object sender, PaintEventArgs e)
        {

            Pen p_black = new Pen(new SolidBrush(Color.Black));

            if (draw)
            {                objgraphics = panel1.CreateGraphics();

            }

        }

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

    }
}
mm5n2pyu

mm5n2pyu1#

看了你的代码,我恐怕不得不说:这是全错了
很抱歉这么直率,但是你绝对不能使用控件。CreateGraphics!!

  • 要做的第一件事是丢弃Graphics objgraphics对象。

存储Graphics对象(几乎)总是错误的!
相反,您必须使用从控件的Paint事件中的e.Graphics参数获取的值。
注意Graphics不包含任何图形,它是一个用于在关联的Bitmap或控件表面上绘图的工具。

  • 接下来要做的是了解如何画手绘线。通常你可以看到你所拥有的代码;但它是无用的,只是一个例子,说明你在介绍中发现了多少愚蠢的东西。算了吧。它总是看起来像垃圾,因为圆圈从来没有看起来光滑,一旦你移动鼠标快一些,伪线完全崩溃。

有一个很好的方法DrawCurve可以画出平滑的线条,你给它一个Pen和一个Points数组。
这就是我们要用的。
让我们回到基本点:如何创建图形?现在我们知道需要在Paint事件中调用DrawCurve

e.Graphics.DrawCurve(somePen, somePointsArray);

这就引出了下一个问题:

  • 有什么了不起
  • 什么是某个点数组

还有隐藏的第三个问题:

  • 画更多的线怎么样?

第一个很简单;你创建了一个Pen,描边宽度为5.5像素,

Pen somePen = new Pen(Color.Blue, 5.5f);

如果你愿意,你可以给予它一个线条(破折号),太。
现在来看阵列:在其最简单的形式,这是很容易的。
MouseMove事件中,将当前的Location存储在一个点列表中。首先,我们在类级别声明它:

List<Point> currentLine = new List<Point>();

然后,我们开始填充它,只要按下左键:

private void panel1_MouseMove_1(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        currentLine.Add(e.Location);
        panel1.Invalidate();
    }  
}

注意最后一行:在控件上调用Invalidate会触发system调用Paint事件。这可能看起来很复杂,但这是唯一正确的方法,因为它保证了当某些其他原因需要时,同样的绘图也会发生。

我们需要绘图,因为我们更改了应该绘制的数据。但是有许多外部原因,最有名的是Minimize/maximize序列,它也会清除绘图并触发Paint事件。所以我们需要配合windows绘制其控件的方式!只有这样图形才会持久**。

另外,注意我们没有使用数组,因为我们不知道我们需要多少个Points,而是使用一个List,然后将其转换为Array
让我们为简单的情况编写Paint事件:

private void panel1_Paint(object sender, PaintEventArgs e)
{
   using (Pen somePen = new Pen(Color.Blue, 5.5f) )
     if (currentLine.Count > 1) e.Graphics.DrawCurve(yourPen , currentLine.ToArray());
}

注意,我在using子句中创建了Pen,这是确保正确处理Pen的一种廉价而安全的方法。
还要注意我们如何将List强制转换为Array
上面的代码本身就可以工作,并允许您徒手画一条线。
但是下一行怎么办?它不应该连接到第一行,所以我们不能只是添加更多的点!
因此,我们不仅需要一个点列表,而且需要更多点列表。事实上,我们需要点列表的列表:

List<List<Point>> curves = new List<List<Point>>();

每当松开鼠标时,我们将当前曲线添加到其中:

private void panel1_MouseUp(object sender, MouseEventArgs e)
{
    if (currentLine.Count > 1) curves.Add(currentLine.ToList());  // copy!!
    currentLine.Clear();
    panel1.Invalidate();
}

注意我是如何使用从ListList的强制转换来强制执行复制的,否则只会分配引用,然后在下一行中清除。
我们再次触发Paint事件,作为最后要做的事情。
现在,我们应该更改Paint事件以显示所有线,包括当前正在绘制的线和所有之前绘制的线。

private void panel1_Paint(object sender, PaintEventArgs e)
{
    using (Pen somePen = new Pen(Color.Blue, 5.5f) )
    {
       if (currentLine.Count > 1) e.Graphics.DrawCurve(somePen, currentLine.ToArray());
       foreach (List<Point> lp in curves)
          if (lp.Count > 1) e.Graphics.DrawCurve(somePen, lp.ToArray());
    }
}

现在我们基本上完成了徒手绘制部分。
所以我们回到你最初的问题:如何将绘图复制到Panel
那么,您已经将所有内容存储在curves数据结构中。
所以你有两个选择或者简单地在panel2_Paint事件中使用相同的数据,或者如果您需要将数据复制并更改到,则可以调整为不同的大小。
以下是仍然缺少的东西:

  • 保存数据,保存图形(SerializeDrawToBitMap
  • 用不同的笔和颜色绘制(创建一个drawAction类来存储所有需要的内容)
  • 使用其他工具,如线条或矩形(创建drawAction类)
  • 清除绘图(参见下文)
  • 撤消和重做(查看StacksQueues
  • 线数较多时缓存(将第一部分绘制成BackgroundImage Bitmap

下面是一个清除代码:

curves.Clear(); currentLine .Clear(); panel1.Invalidate();

我注意到你原来的代码允许你用左右键画两种不同的笔画宽度。这就说明这段代码不是很好。谁会a)想到这一点,b)只满足于两种笔画宽度。
请阅读这篇文章,我解释了一点关于创建一个类,可以存储一个笔宽,颜色等,以便您可以改变然后在您绘制的线条。

相关问题