【日志】
2019/7/18
今天介绍一下 C# 用 picturebox 画坐标系
2020/6/13
这篇文章是在去年发的,没想到帮助了很多朋友。这段时间又用到 PictureBox 控件绘图了,觉得有必要把自己最近学到的补充一下,分享给大家,所以下面来个补充好了(之前的 虽然很low 但是就不删了,毕竟也是满满的回忆,嘿嘿)

一、19/7/18版本

编译环境:VS2010
1.首先建立一个 Windows窗体应用程序 如下图1所示

图1
建好后,就会出现一个窗体设计界面,可以在属性栏更改其名字,图标,Text……如下图2所示:
图2
2.添加控件 “picturebox” 和 “button” 如图3所示(可根据需要更改名字):
图3
3.双击 “绘图” 按钮添加事件响应函数;
在此之前,首先要明白如何从绘图坐标系转换为我们常用的坐标系,如下图3所示:
黑色的是绘图坐标系,红色的是我们常用的坐标系,简单画下图就可得到图中所示公式
图3
本来想画个写轮眼,可最后成了下图4的样子(笑哭):

图4
要画好看一点要仔细研究点在哪里,这里就简单介绍一下在 picturebox 中怎样画线,画椭圆,“绘图按钮”点击函数中有:

private void button1_Click(object sender, EventArgs e)
        {
            Graphics g1 = pictureBox1.CreateGraphics();//画布
            g1.Clear(Color.White);
            Pen p = new Pen(Color.Red, 2);//画笔
            Pen p1 = new Pen(Color.Black, 2);
            Pen p2 = new Pen(Color.Brown, 5);
            Pen p3 = new Pen(Color.Black, 20);
            Font f = new Font("宋体", 10);//字体
            int a = pictureBox1.Size.Height;
            int b = pictureBox1.Size.Width;
            g1.DrawLine(p1, 0, a, 0, 0);//画线
            g1.DrawLine(p1, 0, a, b, a);
            g1.DrawLine(p1, 0, 0, 5, 10);
            g1.DrawLine(p1, b, a, b - 10, a - 5);
            g1.DrawString("O", f, Brushes.Black, 3, a - 20);
            g1.DrawString("正北X", f, Brushes.Black, 3, 3);
            g1.DrawString("正东Y", f, Brushes.Black, b - 50, a - 20);

            Point1[] Pt=new Point1[20];
            Point1[] Pt1 = new Point1[20];
            Pt[0] = new Point1("0", 400, 200);
            Pt[1] = new Point1("1", 250, 250);
            Pt[2] = new Point1("2", 200, 400);
            Pt[3] = new Point1("3", 150, 250);
            Pt[4] = new Point1("4", 0, 200);
            Pt[5] = new Point1("5", 150, 150);
            Pt[6] = new Point1("6", 200, 0);
            Pt[7] = new Point1("7", 250, 150);
            Pt[9] = new Point1("8",200,a-200);
            int i = 0;
            for (i = 0; i < 8; i++)
            {
                Pt1[i] = new Point1(Pt[i].name, Pt[i].y, a - Pt[i].x);
            }
            g1.FillEllipse(Brushes.Black, Pt[9].x-200, Pt[9].y-200, 400, 400);
            g1.FillEllipse(Brushes.Red, Pt[9].x - 170, Pt[9].y - 170, 340, 340);
            g1.FillEllipse(Brushes.Brown, Pt[9].x - 120, Pt[9].y - 120, 240, 240);
            g1.FillEllipse(Brushes.Red, Pt[9].x - 105, Pt[9].y - 105, 210, 210);
            g1.FillEllipse(Brushes.Black, Pt[9].x - 80, Pt[9].y - 80, 160, 160);
            g1.FillEllipse(Brushes.Red, Pt[9].x - 65, Pt[9].y - 65, 130, 130);
            g1.FillEllipse(Brushes.Black, Pt[9].x - 25, Pt[9].y - 25, 50, 50);
            i = 0;
            while (Pt1[i + 1] != null)
            {
                g1.DrawLine(p2, Pt1[i].x, Pt1[i].y, Pt1[i + 1].x, Pt1[i + 1].y);
                i++;
            }
            g1.DrawLine(p2, Pt1[0].x, Pt1[0].y, Pt1[i].x, Pt1[i].y);
            i = 1;
            while (i<=7)
            {
                g1.FillEllipse(Brushes.Black, Pt1[i].x - 25, Pt1[i].y - 25, 60, 60);
                g1.FillEllipse(Brushes.Red, Pt1[i].x - 20, Pt1[i].y - 20, 40, 40);
                
                //g1.DrawString(Pt1[i].name, f, Brushes.Black, Pt1[i].x - 5, Pt1[i].y + 5);
                i=i+2;
            }
            g1.DrawLine(p3, Pt1[0].x, Pt1[0].y, 200, a-280);
            g1.DrawLine(p3, Pt1[2].x, Pt1[2].y, 280, a - 200);
            g1.DrawLine(p3, Pt1[4].x, Pt1[4].y, 200, a - 120);
            g1.DrawLine(p3, Pt1[6].x, Pt1[6].y, 120, a - 200);
            g1.Dispose();
        }

4.双击“清空画布”按钮,添加响应事件:

private void button2_Click(object sender, EventArgs e)
        {
            Graphics g = pictureBox1.CreateGraphics();
            g.Clear(Color.White);
        }

点击运行,OK!

二、20/6/13版本

还是用的 VS2010,首先说明一点,不论是在PictureBox 上画图,还是在窗体上直接画,都要用到 Graphics,个人感觉他们是一样的,不信我们下面我们一起来看。
1.首先新建个窗体,在加一个pictureBox:
在这里插入图片描述
名字啥的随便区,但是要拉的大一点(不然一会画不下)可见我拉的很长,pictureBox (我取名为pB)占据了右边半壁江山

2.添加 Paint 响应事件:
在这里插入图片描述
同样为pB 添加Paint 响应事件:

		private void Draft_Paint(object sender, PaintEventArgs e)
        {

        }

        private void pB_Paint(object sender, PaintEventArgs e)
        {

        }

下面我们要做的就是在这俩Paint 里面码代码画东西了。这样,我先简单介绍一下 Graphics 里面画图的方法有哪些,之后再来画个“全景图”。
在这里插入图片描述
我觉得我下面介绍的东西应该叫“对Graphics类的介绍”,PictureBox 也好,窗体也好,我们想在上面画图都得用:Graphics g = e.Graphics;其中e为:PaintEventArgs e 是由上面的Paint 事件搞出来的。所以:

Graphics 的属性和方法有很多,可以看看:

MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics?view=netcore-3.1

本来想截个长图放这里的,可是那实在是太长辽,朋友们可以自己去读一读。这里就简单介绍几个常用的绘图方法吧

1.预备知识

1.1 Color
public struct Color : IEquatable<System.Drawing.Color>

表示一种 ARGB 颜色(alpha、红色、绿色、蓝色)。
每个像素的颜色表示为32位数字:8位,分别用于 alpha、红色、绿色和蓝色(ARGB)。 这四个组件都是0到255之间的一个数字,0表示没有强度,255表示完全强度。 Alpha 分量指定颜色的透明度:0表示完全透明,255表示完全不透明。 若要确定颜色的 alpha、红色、绿色或蓝色分量,请分别使用 A、R、G或 B 属性。 您可以通过使用其中一个 FromArgb 方法来创建自定义颜色。

系统自带了好多颜色:

https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.color?view=netcore-3.1

1.2 GraphicsPath
public sealed class GraphicsPath : MarshalByRefObject, ICloneable, IDisposable

表示一系列相互连接的直线和曲线。 无法继承此类。
详见:(目前看不懂)

https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.drawing2d.graphicspath?view=netcore-3.1

1.3 Single
public struct Single : IComparable, IComparable<float>, IConvertible, IEquatable<float>, IFormattable

表示单精度浮点数。
Single 值类型表示单精度32位数字,其值范围从负 3.402823 e 38 到正 3.402823 e 38,以及正或负零、PositiveInfinity、NegativeInfinity,而不是数字(NaN)。 它用于表示非常大的值(例如行星或 galaxies 之间的距离)或极小的值(例如物质的分子质量,以千克为间隔),并且通常不精确(如从地球到另一颗太阳系的距离)。 Single 类型符合二元浮点算术的 IEC 60559:1989 (IEEE 754)标准。

2.一些方法简介

1.清除整个绘图面并以指定背景色填充。
public void Clear (System.Drawing.Color color);

2.释放此 Graphics 使用的所有资源。
public void Dispose ();//前面这俩应该是绘图必用的

3.绘制一段弧线,它表示由一对坐标、宽度和高度指定的椭圆部分。
DrawArc //有四个重载,吐槽一下,和我的脑回路很不同,很费解,可以看看我之前写的一篇博客(链接见下)

4.绘制由 4 个 Point 结构定义的贝塞尔样条。
DrawBezier(Pen, Point, Point, Point, Point);//3重载,挺高大上的,目前没用过

5.绘制由 Point 结构的数组定义的闭合基数样条。
DrawClosedCurve(Pen, Point[], Single, FillMode)

6.绘制经过一组指定的 Point 结构的基数样条。
DrawCurve(Pen, Point[])

7.绘制一个由边框(该边框由一对坐标、高度和宽度指定)定义的椭圆。
DrawEllipse(Pen, Rectangle)

8.在指定坐标处绘制由指定的 Icon 表示的图像。
DrawIcon(Icon, Int32, Int32)

9.在指定位置并且按原始大小绘制指定的 Image。
DrawImage

10.绘制一条连接由坐标对指定的两个点的线条。
DrawLine(Pen, PointF, PointF)

11.绘制一系列连接一组 Point 结构的线段。
DrawLines(Pen, PointF[])

12.绘制 GraphicsPath。
public void DrawPath (System.Drawing.Pen pen, System.Drawing.Drawing2D.GraphicsPath path);

13.绘制一个扇形,该形状由一个坐标对、宽度、高度以及两条射线所指定的椭圆定义。
DrawPie(Pen, Single, Single, Single, Single, Single, Single)

14.绘制由一组 Point 结构定义的多边形。
DrawPolygon(Pen, PointF[])

15.绘制由坐标对、宽度和高度指定的矩形。
DrawRectangle(Pen, Single, Single, Single, Single)

16.绘制一系列由 Rectangle 结构指定的矩形。
DrawRectangles(Pen, Rectangle[])

17.在指定位置并且用指定的 Brush 和 Font 对象绘制指定的文本字符串。
DrawString(String, Font, Brush, Single, Single, StringFormat)

18.填充边框所定义的椭圆的内部,该边框由一对坐标、一个宽度和一个高度指定。
FillEllipse(Brush, Single, Single, Single, Single)

19.填充由一对坐标、一个宽度、一个高度以及两条射线指定的椭圆所定义的扇形区的内部。
FillPie(Brush, Single, Single, Single, Single, Single, Single)

20.填充 Point 结构指定的点数组所定义的多边形的内部。
FillPolygon(Brush, PointF[])

21.填充由一对坐标、一个宽度和一个高度指定的矩形的内部。
FillRectangle(Brush, Rectangle)

22.填充 Region 的内部。
public void FillRegion (System.Drawing.Brush brush, System.Drawing.Region region);

23.测量用指定的 Font 绘制的指定字符串。
MeasureString(String, Font)

DrawArc :https://blog.csdn.net/Gou_Hailong/article/details/106715943

3.使用示例

将这些代码贴到Paint 函数中

			Graphics g = e.Graphics;
            g.Clear(Color.White);//1
            PointF[] pt = new PointF[4];
            pt[0] = new PointF(50, 50);
            pt[1] = new PointF(50, 100);
            pt[2] = new PointF(70, 110);
            pt[3] = new PointF(100, 100);
            // create pen.
            Pen p = new Pen(Color.Blue, 2);
            g.DrawBezier(p, pt[0], pt[1], pt[2], pt[3]);//4
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60;}
            g.DrawClosedCurve(p, pt);//5
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.DrawCurve(p, pt);//6
            
            g.DrawEllipse(p, 100, 100, 60, 100);//7
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.DrawLine(p, pt[1], pt[3]);//10
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.DrawPie(p, pt[0].X, pt[0].Y, 60, 50, 60, 250);//13
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.DrawPolygon(p, pt);//14
            p.Color = Color.Yellow;
            for (int i = 0; i < 4; i++)
            { pt[i].Y -= 60 * 5; pt[i].X += 60; }
            g.DrawRectangle(p, pt[3].X, pt[3].Y, 100, 50);//15
            for (int i = 0; i < 4; i++)
            {  pt[i].X += 60; }
            Font f = new Font("宋体", 15);
            Brush b = Brushes.Cyan;
            g.DrawString("Stay Hungry", f, b, pt[2]);//17
            b = Brushes.DarkOrange;
            for (int i = 0; i < 4; i++)
            { pt[i].X += 60; }
            g.FillEllipse(b, pt[1].X, pt[1].Y, 20, 20);//18
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.FillPie(b, pt[3].X, pt[3].Y, 60, 50, 60, 250);//19
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.FillPolygon(b, pt);//20
            for (int i = 0; i < 4; i++)
            { pt[i].Y += 60; }
            g.FillRectangle(b, pt[3].X, pt[3].Y, 100, 50);//21
            g.Dispose();//2

结果:
在这里插入图片描述

4.问题

当我将上面的代码贴到pB_Paint 中时,之后出现问题:
在这里插入图片描述
按理说应该没啥问题,目前解决不了,到时候来填坑。

参考

https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics?view=netcore-3.1

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐