在Visual Studio 2013中从面板复制免费手绘图 [英] copying free hand drawing from panel in visual studio 2013

查看:77
本文介绍了在Visual Studio 2013中从面板复制免费手绘图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Visual Studio中以一种形式(图片框)自由绘画,并在另一个面板/图片框上复制相同的图形(我绘制的). 同样,它们不应是形成一条直线的点,而是连续的点.请帮忙.

I want to draw free hand in a form (picture box) on visual studio and copy the same figure (that I draw) on another panel/picture box. Also they should not be dots forming one line but a continuous line. Please help.

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();
        }

    }
}

推荐答案

在查看您的代码时,我不得不说:这都错了.

Looking at your code I'm afraid I have to say: This is all wrong.

很抱歉,但是您绝不能使用control.CreateGraphics!

  • 要做的第一件事是扔掉Graphics objgraphics对象.

存储Graphics对象总是(几乎)错误!

It is (almost) always wrong to store a Graphics object!

相反,您必须使用从控件的Paint事件中的e.Graphics参数中获得的参数.

Instead you have to use the one you get from the e.Graphics parameter in the Paint events of your controls.

请注意,Graphics 不包含任何图形,它是用于在关联的Bitmap或控件表面上绘制的工具.

Note that Graphics doesn't contain any graphics, it is a tool used to draw onto an associated Bitmap or a control's surface.

  • 接下来要做的是了解绘制徒手线条.通常,您可以看到您拥有的代码.但这是没有用的,仅是您在简介中发现多少愚蠢事物的一个示例.算了吧.它总是看起来像胡扯,因为圆圈根本就不会看起来光滑,而且一旦您更快地移动鼠标,伪线就会完全消失.

有一个不错的方法DrawCurve,它将绘制平滑的线条.您为它提供一个Pen和一个Points数组.

There is a nice method DrawCurve that will draw smooth lines. You feed it a Pen and an array of Points.

这就是我们将要使用的.

This is what we will use.

让我们回到基础:如何创建图形?现在我们知道您需要在Paint事件中调用DrawCurve:

Let's return to the basics: How to create a graphic? Now we know that you need to call DrawCurve in the Paint event:

e.Graphics.DrawCurve(somePen, somePointsArray);

这带来了下一个问题:

  • 几笔是什么
  • somePointsArray是什么

还有一个隐藏的第三个问题:

There is a hidden third question:

  • 如何画更多的线?

第一个很简单;您创建的Pen笔触宽度为5.5像素为

The first one is simple; you create a Pen with a stroke width of 5.5 pixels as

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

如果您愿意,也可以为其设置线条样式(破折号).

If you want to you can give it a linestyle (dashes), too.

现在使用数组:以其最简单的形式,这也很容易.

Now for the array: In its simplest form this is easy as well..

MouseMove事件中,您将当前Location存储在点列表中.首先我们在班级声明它:

In the MouseMove event you store the current Location in a list of points. first we declare it at class level:

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

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

Then we start filling it as long as the left button is pressed:

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

请注意最后一行:在控件上调用Invalidate会触发系统来调用Paint事件.它可能看起来很复杂,但这是唯一正确的方法,因为它可以确保在某些其他原因使其必要时,也会发生完全相同的绘制.

Note the last line: Calling Invalidate on a control triggers the system to invoke the Paint event. It may look complicated but this is the only correct way as it guarantees that the very same drawing will also happen when some other reason makes it necessary.

我们需要绘制,因为我们已经更改了应绘制的数据.但是有很多外部原因,最着名的是Minimize/maximize序列也将清除图形并触发Paint事件.因此,我们需要与 windows 绘制控件的方式进行合作!只有这样,图形才会持续.

We need to draw because we have changes the data that should be drawn. But there are many outside reasons, most notoriously the Minimize/maximize sequence that will clear the drawing and trigger the Paint event, too. So we need to cooperate with the way windows draws its controls! Only this way the graphics will persist.

还请注意,我们不使用数组,因为我们不知道需要多少Points个.取而代之的是,我们使用List,然后将其强制转换为Array.

Also note we don't use an array as we don't know how many Points we will need. Instead we use a List and later cast it to Array..

让我们为简单的案例编写Paint事件的代码:

Lets code the Paint event for our simple case:

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的廉价且安全的方法.

Note that I have created the Pen in a using clause. This is a cheap and safe way to ensure that the Pen is disposed of properly.

还要注意我们如何将List强制转换为Array

Also note how we cast the the List to an Array!

仅上面的代码就可以工作,并且可以让您徒手画一条线.

The above code alone would work and allow you free-hand drawing a line.

但是下一行呢?它不应该与第一个连接,所以我们不能只添加更多的点!

But what about the next line? It should not connect to the first one so we can't just add more points!

因此,我们不仅需要一个点列表,还需要更多,实际上,需要一个点列表:

So we need not just one list of points but more than that, in fact a list of list of points is called for:

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

每当释放鼠标时,我们就会向其中添加当前曲线:

And we add the current curve to it whenever the mouse is released:

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

请注意我如何使用从ListList的强制转换来强制执行副本,否则将只分配引用,然后在下一行清除..

Note how I use a cast from List to List to enforce a copy or else only the reference would be assigned and then, in the next line cleared..

我们再次触发Paint事件是最后要做的事情.

Again we trigger the Paint event as the final thing to do.

我们现在应该更改Paint事件以显示所有线条,包括当前正在绘制的线条和所有较早的线条..

We now should change the Paint event to display all the lines, both the one currently being drawn and all the earlier ones..:

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());
    }
}

现在我们基本上完成了手绘图部分.

Now we are basically done with the free-hand drawing part.

因此,我们回到您的原始问题:如何将图形复制到 second Panel?

So we return to your original question: How can you copy the drawing to a second Panel?

好吧,您已经将所有内容存储在curves数据结构中.

Well, you have stored everything in the curves data structure.

因此,您有两个选择:要么在panel2_Paint事件中简单地使用相同的数据,要么如果您需要将数据复制和更改为该数据,则可以适应不同的大小.

So you have two options: Either simply use the same data in the panel2_Paint event or if you need to copy and change the data to, maybe adapt to a different size..

SO不是代码编写服务.因此,通常我不应该给您更多关于我在上述注释中所写内容的提示.但是随着这个问题的出现,我常常写出一个非常基本的Doodle应用程序的完整代码.

SO is not a code writing service. So usually I shouldn't give you much more hints that what I wrote in the above comments. But as this question comes up so often I wrote up the full code for a very basic doodle application..

这里仍然缺少东西:

  • 保存数据,保存图形(SerializeDrawToBitMap)
  • 用不同的笔绘画&颜色(创建一个drawAction类以存储所需的所有内容)
  • 使用其他工具,例如线"或矩形"(创建drawAction类)
  • 清除图形(见下文)
  • 撤消和重做(查看StacksQueues)
  • 当行数很多时进行缓存(将第一部分绘制为BackgroundImage Bitmap)
  • Saving the data, saving the drawing (Serialize, DrawToBitMap)
  • Drawing with varying pens & colors (create a drawAction class to store all you need)
  • Using other tools like Line or Rectangle (create a drawAction class)
  • Clearing the drawing (see below)
  • Undo and Redo (look into Stacks an Queues)
  • Caching when there are a great number of lines ( draw the first portion into a BackgroundImage Bitmap)

这是清除代码:

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

我注意到您的原始代码使您可以使用左右按钮来绘制两种不同的笔触宽度.仅此一点就表明该代码不是很好.谁会(a)想到这一点,并且(b)仅用两个笔触宽度来满足..

I noted that your original code lets you draw with two different stroke widths using the left and right button. This alone shows that this code is not very good. Who would a) think of that and b) be satisfied with only two stroke widths..

请阅读此帖子,在这里我对创建一个可以存储笔宽,颜色等的类进行了一些解释,以便您可以在绘制的线条之间进行更改..

Please read this post where I explain a little about creating a class that can store a pen width, a color etc so that you can change then between lines you draw..

这篇关于在Visual Studio 2013中从面板复制免费手绘图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆