找回密码
 立即注册
首页 业界区 业界 [原创]《C#高级GDI+实战:从零开发一个流程图》第04章: ...

[原创]《C#高级GDI+实战:从零开发一个流程图》第04章:来个圆形,连线它!

归筠溪 2025-6-25 09:21:04
一、前言

上一节我们实现了在矩形与矩形之间添加连线,光是矩形太单调了,某些问题也暴露不出来,我们本节就来看一下,如何添加一个圆形,且支持圆形与圆形、圆形与矩形、矩形与矩形间的连线。在这个过程中我们会发现一些问题,这些问题我们后续课程会进行处理,大家也请带着自己的思考和理解去看。
相信看完的你,一定会有所收获!
本文地址:https://www.cnblogs.com/lesliexin/p/18923109
二、先看效果

我们先来看一下本节课整体要实现的效果,先有个整体印象。
特别说明一下,课程的编写是根据实际情况实时调整的,所以有时候与视频无法一一对应,像本篇教程,只需要看视频中的前半截就行,后半截视频的讲解在下节教课程中。
本节课程我们要依次实现两个效果:
绘制不同颜色颜色的、可拖动的圆形
连线支持圆形与圆形、圆形与矩形、矩形与矩形间连线
我们下面就来开始讲解。
三、实现效果1:添加不同颜色的、可拖动的圆形

有前面几节课程的基础,我们不需要过多的讲解,和矩形一样,只是在绘制时绘制成圆的,且拖动时要判断选的是圆形还是矩形。
我们下面就来看一下代码实操。
1,设计器界面

1.png

很简单的界面,就两个按钮:添加矩形、添加圆形
2,圆形定义

就像矩形的定义一样,我们也定义一个圆形的类,用来描述这个圆形:
2.png

我们注意到圆形核心也是一个Rectangle类型,这是因为GDI+在绘制圆形是需要的就是Rectangle,不过过多纠结:
3.png

因为整个界面我们要添加多个圆形,所以我们定义一个圆形集合:
4.png

在绘制时,依次绘制所有圆形:
5.png

3,鼠标点击事件

在鼠标点击时,我们这里就要多判断一步:点击的是圆形?还是矩形?
6.png

4,鼠标移动事件

在鼠标移动时,我们同样要判断当前点按着的是圆形还是矩形,并修改对应形状的位置。
7.png

5,添加圆形的方法

和添加矩形一样,往圆形集合中添加一个圆形:
8.png

好了,代码很简单,到此就实现了效果1,下面是完整的代码,大家试试吧。
点击查看代码
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. namespace FlowChartDemo
  11. {
  12.     public partial class FormDemo03V1 : FormBase
  13.     {
  14.         public FormDemo03V1()
  15.         {
  16.             InitializeComponent();
  17.             DemoTitle = "第05节随课Demo  Part1";
  18.             DemoNote = "效果:支持添加圆形,支持不同颜色,支持拖动";
  19.         }
  20.         /// <summary>
  21.         /// 矩形定义
  22.         /// </summary>
  23.         public class RectShape
  24.         {
  25.             /// <summary>
  26.             /// 矩形ID
  27.             /// </summary>
  28.             public string Id { get; set; }
  29.             /// <summary>
  30.             /// 矩形位置和尺寸
  31.             /// </summary>
  32.             public Rectangle Rect { get; set; }
  33.         }
  34.         /// <summary>
  35.         /// 圆形定义
  36.         /// </summary>
  37.         public class EllipseShape
  38.         {
  39.             /// <summary>
  40.             /// 矩形ID
  41.             /// </summary>
  42.             public string Id { get; set; }
  43.             /// <summary>
  44.             /// 矩形位置和尺寸
  45.             /// </summary>
  46.             public Rectangle Rect { get; set; }
  47.         }
  48.         /// <summary>
  49.         /// 直线连线定义
  50.         /// </summary>
  51.         public class LineLink
  52.         {
  53.             /// <summary>
  54.             /// 连线ID
  55.             /// </summary>
  56.             public string Id { get; set; }
  57.             /// <summary>
  58.             /// 开始形状是否是矩形
  59.             /// </summary>
  60.             public bool StartShapeIsRect { get; set; }
  61.             /// <summary>
  62.             /// 结束开关是否是矩形
  63.             /// </summary>
  64.             public bool EndShapeIsRect { get; set; }
  65.             /// <summary>
  66.             /// 连线开始形状ID
  67.             /// </summary>
  68.             public string StartShapeId { get; set; }
  69.             /// <summary>
  70.             /// 连线结束形状ID
  71.             /// </summary>
  72.             public string EndShapeId { get; set; }
  73.         }
  74.         /// <summary>
  75.         /// 矩形集合
  76.         /// </summary>
  77.         List<RectShape> RectShapes = new List<RectShape>();
  78.         /// <summary>
  79.         /// 圆形集合
  80.         /// </summary>
  81.         List<EllipseShape> EllipseShapes = new List<EllipseShape>();
  82.         /// <summary>
  83.         /// 当前界面连线集合
  84.         /// </summary>
  85.         List<LineLink> Links = new List<LineLink>();
  86.         /// <summary>
  87.         /// 画一个矩形(不同颜色)
  88.         /// </summary>
  89.         /// <param name="g"></param>
  90.         /// <param name="shape"></param>
  91.         void DrawRectShape2(Graphics g, RectShape shape)
  92.         {
  93.             var index = RectShapes.FindIndex(a => a.Id == shape.Id);
  94.             g.FillRectangle(GetBrush(index), shape.Rect);
  95.             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);
  96.         }
  97.         /// <summary>
  98.         /// 画一个圆形(不同颜色)
  99.         /// </summary>
  100.         /// <param name="g"></param>
  101.         /// <param name="shape"></param>
  102.         void DrawEllipseShape2(Graphics g, EllipseShape shape)
  103.         {
  104.             var index = EllipseShapes.FindIndex(a => a.Id == shape.Id);
  105.             g.FillEllipse(GetBrush(index), shape.Rect);
  106.             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect.X+20,shape.Rect.Y+20);            //注:这里可以讲一下,要+20,是显示文本
  107.         }
  108.         /// <summary>
  109.         /// 绘制一条连线(不同颜色)
  110.         /// </summary>
  111.         /// <param name="g"></param>
  112.         /// <param name="line"></param>
  113.         void DrawLine2(Graphics g, LineLink line)
  114.         {
  115.             //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标
  116.             var startPoint = line.StartShapeIsRect? GetCentertPointRect(line.StartShapeId):
  117.                 GetCentertPointEllipse(line.StartShapeId);
  118.             var endPoint =line.EndShapeIsRect? GetCentertPointRect(line.EndShapeId) :
  119.                 GetCentertPointEllipse(line.EndShapeId);
  120.             var index = Links.FindIndex(a => a.Id == line.Id);
  121.             //绘制一条直线
  122.             g.DrawLine(GetPen(index), startPoint, endPoint);
  123.         }
  124.         /// <summary>
  125.         /// 重新绘制当前所有矩形和连线
  126.         /// </summary>
  127.         /// <param name="g"></param>
  128.         void DrawAll(Graphics g)
  129.         {
  130.             g.Clear(panel1.BackColor);
  131.             //绘制所有矩形
  132.             foreach (var sp in RectShapes)
  133.             {
  134.                 DrawRectShape2(g, sp);
  135.             }
  136.             //绘制所有圆形
  137.             foreach (var sp in EllipseShapes)
  138.             {
  139.                 DrawEllipseShape2(g, sp);
  140.             }
  141.             //绘制所有连线
  142.             foreach (var ln in Links)
  143.             {
  144.                 DrawLine2(g, ln);
  145.             }
  146.         }
  147.         //注:文章中说明;此处不过于抽象,后续章节会有
  148.         /// <summary>
  149.         /// 当前是否有鼠标按下,且有矩形被选中
  150.         /// </summary>
  151.         bool _isMouseDown = false;
  152.         /// <summary>
  153.         /// 是否是矩形被选中,不是则是圆形
  154.         /// </summary>
  155.         bool _isRectMouseDown = true;
  156.         /// <summary>
  157.         /// 最后一次鼠标的位置
  158.         /// </summary>
  159.         Point _lastMouseLocation = Point.Empty;
  160.         /// <summary>
  161.         /// 当前被鼠标选中的矩形
  162.         /// </summary>
  163.         RectShape _selectedRectShape = null;
  164.         /// <summary>
  165.         /// 当前被鼠标选中的圆形
  166.         /// </summary>
  167.         EllipseShape _selectedEllipseShape = null;
  168.         /// <summary>
  169.         /// 获取不同的背景颜色
  170.         /// </summary>
  171.         /// <param name="i"></param>
  172.         /// <returns></returns>
  173.         Brush GetBrush(int i)
  174.         {
  175.             switch (i)
  176.             {
  177.                 case 0: return Brushes.Red;
  178.                 case 1: return Brushes.Green;
  179.                 case 2: return Brushes.Blue;
  180.                 case 3: return Brushes.Orange;
  181.                 case 4: return Brushes.Purple;
  182.                 default: return Brushes.Red;
  183.             }
  184.         }
  185.         /// <summary>
  186.         /// 获取不同的画笔颜色
  187.         /// </summary>
  188.         /// <param name="i"></param>
  189.         /// <returns></returns>
  190.         Pen GetPen(int i)
  191.         {
  192.             return new Pen(GetBrush(i), 2);
  193.         }
  194.         /// <summary>
  195.         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点
  196.         /// </summary>
  197.         /// <param name="shapeId"></param>
  198.         /// <returns></returns>
  199.         Point GetCentertPointRect(string shapeId)
  200.         {
  201.             var sp = RectShapes.Find(a => a.Id == shapeId);
  202.             if (sp != null)
  203.             {
  204.                 var line1X = sp.Rect.X + sp.Rect.Width / 2;
  205.                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;
  206.                 return new Point(line1X, line1Y);
  207.             }
  208.             return Point.Empty;
  209.         }
  210.         /// <summary>
  211.         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点
  212.         /// </summary>
  213.         /// <param name="shapeId"></param>
  214.         /// <returns></returns>
  215.         Point GetCentertPointEllipse(string shapeId)
  216.         {
  217.             var sp = EllipseShapes.Find(a => a.Id == shapeId);
  218.             if (sp != null)
  219.             {
  220.                 var line1X = sp.Rect.X + sp.Rect.Width / 2;
  221.                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;
  222.                 return new Point(line1X, line1Y);
  223.             }
  224.             return Point.Empty;
  225.         }
  226.         private void toolStripButton1_Click(object sender, EventArgs e)
  227.         {
  228.             var rs = new RectShape()
  229.             {
  230.                 Id = "矩形" + (RectShapes.Count + 1),
  231.                 Rect = new Rectangle()
  232.                 {
  233.                     X = 50,
  234.                     Y = 50,
  235.                     Width = 100,
  236.                     Height = 100,
  237.                 },
  238.             };
  239.             RectShapes.Add(rs);
  240.             //重绘所有矩形
  241.             DrawAll(panel1.CreateGraphics());
  242.         }
  243.         private void panel1_MouseDown(object sender, MouseEventArgs e)
  244.         {
  245.             //当鼠标按下时
  246.             //取最上方的矩形,也就是最后添加的矩形
  247.             var sp = RectShapes.FindLast(a => a.Rect.Contains(e.Location));
  248.             //取最上方的圆形,也就是最后添加的圆形
  249.             var ep = EllipseShapes.FindLast(a => a.Rect.Contains(e.Location));
  250.             //注:说明,这里是简化情况,因为是两个LIST,无法判断序号,就先判断矩形
  251.             //当前没有处理连线状态
  252.             if (sp != null)
  253.             {
  254.                 //设置状态及选中矩形
  255.                 _isMouseDown = true;
  256.                 _lastMouseLocation = e.Location;
  257.                 _selectedRectShape = sp;
  258.                 _selectedEllipseShape = null;
  259.                 _isRectMouseDown = true;
  260.             }
  261.             else if (ep != null)
  262.             {
  263.                 //设置状态及选中圆形
  264.                 _isMouseDown = true;
  265.                 _lastMouseLocation = e.Location;
  266.                 _selectedRectShape = null;
  267.                 _selectedEllipseShape = ep;
  268.                 _isRectMouseDown = false;
  269.             }
  270.         }
  271.         private void panel1_MouseMove(object sender, MouseEventArgs e)
  272.         {
  273.             if (_isMouseDown)
  274.             {
  275.                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作
  276.                 //改变选中矩形的位置信息,随着鼠标移动而移动
  277.                 //计算鼠标位置变化信息
  278.                 var moveX = e.Location.X - _lastMouseLocation.X;
  279.                 var moveY = e.Location.Y - _lastMouseLocation.Y;
  280.                 //将选中形状的位置进行同样的变化
  281.                 if (_isRectMouseDown)
  282.                 {
  283.                     var oldXY = _selectedRectShape.Rect.Location;
  284.                     oldXY.Offset(moveX, moveY);
  285.                     _selectedRectShape.Rect = new Rectangle(oldXY, _selectedRectShape.Rect.Size);
  286.                 }
  287.                 else
  288.                 {
  289.                     var oldXY = _selectedEllipseShape.Rect.Location;
  290.                     oldXY.Offset(moveX, moveY);
  291.                     _selectedEllipseShape.Rect = new Rectangle(oldXY, _selectedEllipseShape.Rect.Size);
  292.                 }
  293.                 //记录当前鼠标位置
  294.                 _lastMouseLocation.Offset(moveX, moveY);
  295.                 //重绘所有矩形
  296.                 DrawAll(panel1.CreateGraphics());
  297.             }
  298.         }
  299.         private void panel1_MouseUp(object sender, MouseEventArgs e)
  300.         {
  301.             //当鼠标松开时
  302.             if (_isMouseDown)
  303.             {
  304.                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作
  305.                 //重置相关记录信息
  306.                 _isMouseDown = false;
  307.                 _lastMouseLocation = Point.Empty;
  308.                 _selectedRectShape = null;
  309.                 _selectedEllipseShape = null;
  310.             }
  311.         }
  312.         private void toolStripButton4_Click(object sender, EventArgs e)
  313.         {
  314.             var rs = new EllipseShape()
  315.             {
  316.                 Id = "圆形" + (EllipseShapes.Count + 1),
  317.                 Rect = new Rectangle()
  318.                 {
  319.                     X = 50,
  320.                     Y = 50,
  321.                     Width = 100,
  322.                     Height = 100,
  323.                 },
  324.             };
  325.             EllipseShapes.Add(rs);
  326.             //重绘所有矩形
  327.             DrawAll(panel1.CreateGraphics());
  328.         }
  329.     }
  330. }
复制代码
四、实现效果2:连线支持圆形与圆形、圆形与矩形、矩形与矩形间连线

上一小节我们添加上了圆形,下面就来支持连线。
1,设计器界面

我们在上节的基础上稍做添加:
9.png

2,连线定义修改

因为我们要支持圆形与矩形间不同组合的连线,所以我们需要对连线定义进行扩展:
10.png

我们增加了两个布尔变量,来标识开始形状和结束形状是否是矩形,不是矩形则是圆形。
注:各位读者看到这里,可能会感觉很死板和繁琐。是的,确实如此。我们现在是刚开始起步,所以要从简入深,从繁化简,也是在这样一步步深入,我们明白了其中的痛点,引导着我们去调整、去优化。大概下下节课,我们就会使用抽象大法,到时一切都会变得优雅。
3,绘制连线

在绘制连线时,我们通过判断形状是圆形还是矩形,来取不同的中心点,并绘制连线。
注:当然,现在连线的是形状的圆心,所以并没有太大的区别。
11.png

12.png

4,鼠标点击事件

这部分不难理解但会比较繁琐,在判断是否连线时,还要判断开始形状与结束形状的类型等。
13.png

剩下的就和上一小节没多少差别了,不再赘述。下面是完整的代码,大家可以参照尝试。
点击查看代码
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. namespace FlowChartDemo
  11. {
  12.     public partial class FormDemo03V2 : FormBase
  13.     {
  14.         public FormDemo03V2()
  15.         {
  16.             InitializeComponent();
  17.             DemoTitle = "第05节随课Demo  Part2";
  18.             DemoNote = "效果:支持矩形与矩形间连线、矩形与圆形间连线、圆形与圆形间连线";
  19.         }
  20.         /// <summary>
  21.         /// 矩形定义
  22.         /// </summary>
  23.         public class RectShape
  24.         {
  25.             /// <summary>
  26.             /// 矩形ID
  27.             /// </summary>
  28.             public string Id { get; set; }
  29.             /// <summary>
  30.             /// 矩形位置和尺寸
  31.             /// </summary>
  32.             public Rectangle Rect { get; set; }
  33.         }
  34.         /// <summary>
  35.         /// 圆形定义
  36.         /// </summary>
  37.         public class EllipseShape
  38.         {
  39.             /// <summary>
  40.             /// 矩形ID
  41.             /// </summary>
  42.             public string Id { get; set; }
  43.             /// <summary>
  44.             /// 矩形位置和尺寸
  45.             /// </summary>
  46.             public Rectangle Rect { get; set; }
  47.         }
  48.         /// <summary>
  49.         /// 直线连线定义
  50.         /// </summary>
  51.         public class LineLink
  52.         {
  53.             /// <summary>
  54.             /// 连线ID
  55.             /// </summary>
  56.             public string Id { get; set; }
  57.             /// <summary>
  58.             /// 开始形状是否是矩形
  59.             /// </summary>
  60.             public bool StartShapeIsRect { get; set; }
  61.             /// <summary>
  62.             /// 结束开关是否是矩形
  63.             /// </summary>
  64.             public bool EndShapeIsRect { get; set; }
  65.             /// <summary>
  66.             /// 连线开始形状ID
  67.             /// </summary>
  68.             public string StartShapeId { get; set; }
  69.             /// <summary>
  70.             /// 连线结束形状ID
  71.             /// </summary>
  72.             public string EndShapeId { get; set; }
  73.         }
  74.         /// <summary>
  75.         /// 矩形集合
  76.         /// </summary>
  77.         List<RectShape> RectShapes = new List<RectShape>();
  78.         /// <summary>
  79.         /// 圆形集合
  80.         /// </summary>
  81.         List<EllipseShape> EllipseShapes = new List<EllipseShape>();
  82.         /// <summary>
  83.         /// 当前界面连线集合
  84.         /// </summary>
  85.         List<LineLink> Links = new List<LineLink>();
  86.         /// <summary>
  87.         /// 画一个矩形(不同颜色)
  88.         /// </summary>
  89.         /// <param name="g"></param>
  90.         /// <param name="shape"></param>
  91.         void DrawRectShape2(Graphics g, RectShape shape)
  92.         {
  93.             var index = RectShapes.FindIndex(a => a.Id == shape.Id);
  94.             g.FillRectangle(GetBrush(index), shape.Rect);
  95.             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);
  96.         }
  97.         /// <summary>
  98.         /// 画一个圆形(不同颜色)
  99.         /// </summary>
  100.         /// <param name="g"></param>
  101.         /// <param name="shape"></param>
  102.         void DrawEllipseShape2(Graphics g, EllipseShape shape)
  103.         {
  104.             var index = EllipseShapes.FindIndex(a => a.Id == shape.Id);
  105.             g.FillEllipse(GetBrush(index), shape.Rect);
  106.             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect.X+20,shape.Rect.Y+20);            //注:这里可以讲一下,要+20,是显示文本
  107.         }
  108.         /// <summary>
  109.         /// 绘制一条连线(不同颜色)
  110.         /// </summary>
  111.         /// <param name="g"></param>
  112.         /// <param name="line"></param>
  113.         void DrawLine2(Graphics g, LineLink line)
  114.         {
  115.             //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标
  116.             var startPoint = line.StartShapeIsRect? GetCentertPointRect(line.StartShapeId): GetCentertPointEllipse(line.StartShapeId);
  117.             var endPoint =line.EndShapeIsRect? GetCentertPointRect(line.EndShapeId) : GetCentertPointEllipse(line.EndShapeId);
  118.             var index = Links.FindIndex(a => a.Id == line.Id);
  119.             //绘制一条直线
  120.             g.DrawLine(GetPen(index), startPoint, endPoint);
  121.         }
  122.         /// <summary>
  123.         /// 重新绘制当前所有矩形和连线
  124.         /// </summary>
  125.         /// <param name="g"></param>
  126.         void DrawAll(Graphics g)
  127.         {
  128.             g.Clear(panel1.BackColor);
  129.             //绘制所有矩形
  130.             foreach (var sp in RectShapes)
  131.             {
  132.                 DrawRectShape2(g, sp);
  133.             }
  134.             //绘制所有圆形
  135.             foreach (var sp in EllipseShapes)
  136.             {
  137.                 DrawEllipseShape2(g, sp);
  138.             }
  139.             //绘制所有连线
  140.             foreach (var ln in Links)
  141.             {
  142.                 DrawLine2(g, ln);
  143.             }
  144.         }
  145.         //注:文章中说明;此处不过于抽象,后续章节会有
  146.         /// <summary>
  147.         /// 当前是否有鼠标按下,且有矩形被选中
  148.         /// </summary>
  149.         bool _isMouseDown = false;
  150.         /// <summary>
  151.         /// 是否是矩形被选中,不是则是圆形
  152.         /// </summary>
  153.         bool _isRectMouseDown = true;
  154.         /// <summary>
  155.         /// 最后一次鼠标的位置
  156.         /// </summary>
  157.         Point _lastMouseLocation = Point.Empty;
  158.         /// <summary>
  159.         /// 当前被鼠标选中的矩形
  160.         /// </summary>
  161.         RectShape _selectedRectShape = null;
  162.         /// <summary>
  163.         /// 当前被鼠标选中的圆形
  164.         /// </summary>
  165.         EllipseShape _selectedEllipseShape = null;
  166.         /// <summary>
  167.         /// 添加连线时选中的第一个是否是矩形,不是则是圆形
  168.         /// </summary>
  169.         bool _selectedStartIsRect = true;
  170.         /// <summary>
  171.         /// 添加连线时选中的第一个矩形
  172.         /// </summary>
  173.         RectShape _selectedStartRectShape = null;
  174.         /// <summary>
  175.         /// 添加连线时选中的第一个圆形
  176.         /// </summary>
  177.         EllipseShape _selectedStartEllipseShape = null;
  178.         /// <summary>
  179.         /// 添加连线时选中的第二个是否是矩形,不是则是圆形
  180.         /// </summary>
  181.         bool _selectedEndIsRect = true;
  182.         /// <summary>
  183.         /// 添加连线时选中的第二个矩形
  184.         /// </summary>
  185.         RectShape _selectedEndRectShape = null;
  186.         /// <summary>
  187.         /// 添加连线时选中的第二个圆形
  188.         /// </summary>
  189.         EllipseShape _selectedEndEllipseShape = null;
  190.         /// <summary>
  191.         /// 是否正添加连线
  192.         /// </summary>
  193.         bool _isAddLink = false;
  194.         /// <summary>
  195.         /// 获取不同的背景颜色
  196.         /// </summary>
  197.         /// <param name="i"></param>
  198.         /// <returns></returns>
  199.         Brush GetBrush(int i)
  200.         {
  201.             switch (i)
  202.             {
  203.                 case 0: return Brushes.Red;
  204.                 case 1: return Brushes.Green;
  205.                 case 2: return Brushes.Blue;
  206.                 case 3: return Brushes.Orange;
  207.                 case 4: return Brushes.Purple;
  208.                 default: return Brushes.Red;
  209.             }
  210.         }
  211.         /// <summary>
  212.         /// 获取不同的画笔颜色
  213.         /// </summary>
  214.         /// <param name="i"></param>
  215.         /// <returns></returns>
  216.         Pen GetPen(int i)
  217.         {
  218.             return new Pen(GetBrush(i), 2);
  219.         }
  220.         /// <summary>
  221.         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点
  222.         /// </summary>
  223.         /// <param name="shapeId"></param>
  224.         /// <returns></returns>
  225.         Point GetCentertPointRect(string shapeId)
  226.         {
  227.             var sp = RectShapes.Find(a => a.Id == shapeId);
  228.             if (sp != null)
  229.             {
  230.                 var line1X = sp.Rect.X + sp.Rect.Width / 2;
  231.                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;
  232.                 return new Point(line1X, line1Y);
  233.             }
  234.             return Point.Empty;
  235.         }
  236.         /// <summary>
  237.         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点
  238.         /// </summary>
  239.         /// <param name="shapeId"></param>
  240.         /// <returns></returns>
  241.         Point GetCentertPointEllipse(string shapeId)
  242.         {
  243.             var sp = EllipseShapes.Find(a => a.Id == shapeId);
  244.             if (sp != null)
  245.             {
  246.                 var line1X = sp.Rect.X + sp.Rect.Width / 2;
  247.                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;
  248.                 return new Point(line1X, line1Y);
  249.             }
  250.             return Point.Empty;
  251.         }
  252.         private void toolStripButton1_Click(object sender, EventArgs e)
  253.         {
  254.             var rs = new RectShape()
  255.             {
  256.                 Id = "矩形" + (RectShapes.Count + 1),
  257.                 Rect = new Rectangle()
  258.                 {
  259.                     X = 50,
  260.                     Y = 50,
  261.                     Width = 100,
  262.                     Height = 100,
  263.                 },
  264.             };
  265.             RectShapes.Add(rs);
  266.             //重绘所有矩形
  267.             DrawAll(panel1.CreateGraphics());
  268.         }
  269.         private void panel1_MouseDown(object sender, MouseEventArgs e)
  270.         {
  271.             //当鼠标按下时
  272.             //取最上方的矩形,也就是最后添加的矩形
  273.             var sp = RectShapes.FindLast(a => a.Rect.Contains(e.Location));
  274.             //取最上方的圆形,也就是最后添加的圆形
  275.             var ep = EllipseShapes.FindLast(a => a.Rect.Contains(e.Location));
  276.             if (!_isAddLink)
  277.             {
  278.                 //注:说明,这里是简化情况,因为是两个LIST,无法判断序号,就先判断矩形
  279.                
  280.                 //当前没有处理连线状态
  281.                 if (sp != null)
  282.                 {
  283.                     //设置状态及选中矩形
  284.                     _isMouseDown = true;
  285.                     _lastMouseLocation = e.Location;
  286.                     _selectedRectShape = sp;
  287.                     _selectedEllipseShape = null;
  288.                     _isRectMouseDown = true;
  289.                 }
  290.                 else if (ep != null)
  291.                 {
  292.                     //设置状态及选中圆形
  293.                     _isMouseDown = true;
  294.                     _lastMouseLocation = e.Location;
  295.                     _selectedRectShape = null;
  296.                     _selectedEllipseShape = ep;
  297.                     _isRectMouseDown = false;
  298.                 }
  299.             }
  300.             else
  301.             {
  302.                 //正在添加连线
  303.                 if (_selectedStartRectShape == null && _selectedStartEllipseShape == null)
  304.                 {
  305.                     //证明没有矩形和圆形被选中则设置开始形状
  306.                     if (sp != null)
  307.                     {
  308.                         //设置开始形状是矩形
  309.                         _selectedStartRectShape = sp;
  310.                         _selectedStartEllipseShape = null;
  311.                         _selectedStartIsRect = true;
  312.                     }
  313.                     else if (ep != null)
  314.                     {
  315.                         //设置开始形状是圆形
  316.                         _selectedStartRectShape = null;
  317.                         _selectedStartEllipseShape = ep;
  318.                         _selectedStartIsRect = false;
  319.                     }
  320.                     toolStripStatusLabel1.Text = "请点击第2个形状";
  321.                 }
  322.                 else
  323.                 {
  324.                     //判断第2个形状是否是第1个形状
  325.                     if (sp != null)
  326.                     {
  327.                         //证明当前选中的是矩形
  328.                         if (_selectedStartRectShape != null)
  329.                         {
  330.                             //证明第1步选中的矩形
  331.                             //判断当前选中的矩形是否是第1步选中的矩形
  332.                             if (_selectedStartRectShape.Id == sp.Id)
  333.                             {
  334.                                 toolStripStatusLabel1.Text = "不可选择同一个形状,请重新点击第2个形状";
  335.                                 return;
  336.                             }
  337.                         }
  338.                     }
  339.                     else if (ep != null)
  340.                     {
  341.                         //证明当前选中的圆形
  342.                         if (_selectedStartEllipseShape != null)
  343.                         {
  344.                             //证明第1步选中的矩形
  345.                             //判断当前选中的矩形是否是第1步选中的矩形
  346.                             if (_selectedStartEllipseShape.Id == ep.Id)
  347.                             {
  348.                                 toolStripStatusLabel1.Text = "不可选择同一个形状,请重新点击第2个形状";
  349.                                 return;
  350.                             }
  351.                         }
  352.                     }
  353.                     //注:文章中说明:因为太过复杂,且不是本节重点,但不再进行去重判断
  354.                     if (sp != null)
  355.                     {
  356.                         //设置结束形状是矩形
  357.                         _selectedEndRectShape = sp;
  358.                         _selectedEndEllipseShape = null;
  359.                         _selectedEndIsRect = true;
  360.                     }
  361.                     else if (ep != null)
  362.                     {
  363.                         //设置结束形状是圆形
  364.                         _selectedEndRectShape = null;
  365.                         _selectedEndEllipseShape = ep;
  366.                         _selectedEndIsRect = false;
  367.                     }
  368.                     else
  369.                     {
  370.                         return;
  371.                     }
  372.                     //两个形状都设置了,便添加一条新连线
  373.                     Links.Add(new LineLink()
  374.                     {
  375.                         Id = "连线" + (Links.Count + 1),
  376.                         StartShapeId =_selectedStartIsRect? _selectedStartRectShape.Id:_selectedStartEllipseShape.Id,
  377.                         EndShapeId =_selectedEndIsRect? _selectedEndRectShape.Id:_selectedEndEllipseShape.Id,
  378.                         StartShapeIsRect=_selectedStartIsRect,
  379.                         EndShapeIsRect=_selectedEndIsRect,
  380.                     });
  381.                     //两个形状都已选择,结束添加连线状态
  382.                     _isAddLink = false;
  383.                     toolStripStatusLabel1.Text = "";
  384.                     //重绘以显示连线
  385.                     DrawAll(panel1.CreateGraphics());
  386.                 }
  387.             }
  388.         }
  389.         private void panel1_MouseMove(object sender, MouseEventArgs e)
  390.         {
  391.             //当鼠标移动时
  392.             //如果处于添加连线时,则不移动形状
  393.             if (_isAddLink) return;
  394.             if (_isMouseDown)
  395.             {
  396.                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作
  397.                 //改变选中矩形的位置信息,随着鼠标移动而移动
  398.                 //计算鼠标位置变化信息
  399.                 var moveX = e.Location.X - _lastMouseLocation.X;
  400.                 var moveY = e.Location.Y - _lastMouseLocation.Y;
  401.                 //将选中形状的位置进行同样的变化
  402.                 if (_isRectMouseDown)
  403.                 {
  404.                     var oldXY = _selectedRectShape.Rect.Location;
  405.                     oldXY.Offset(moveX, moveY);
  406.                     _selectedRectShape.Rect = new Rectangle(oldXY, _selectedRectShape.Rect.Size);
  407.                 }
  408.                 else
  409.                 {
  410.                     var oldXY = _selectedEllipseShape.Rect.Location;
  411.                     oldXY.Offset(moveX, moveY);
  412.                     _selectedEllipseShape.Rect = new Rectangle(oldXY, _selectedEllipseShape.Rect.Size);
  413.                 }
  414.                 //记录当前鼠标位置
  415.                 _lastMouseLocation.Offset(moveX, moveY);
  416.                 //重绘所有矩形
  417.                 DrawAll(panel1.CreateGraphics());
  418.             }
  419.         }
  420.         private void panel1_MouseUp(object sender, MouseEventArgs e)
  421.         {
  422.             //当鼠标松开时
  423.             if (_isMouseDown)
  424.             {
  425.                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作
  426.                 //重置相关记录信息
  427.                 _isMouseDown = false;
  428.                 _lastMouseLocation = Point.Empty;
  429.                 _selectedRectShape = null;
  430.                 _selectedEllipseShape = null;
  431.             }
  432.         }
  433.         private void toolStripButton2_Click(object sender, EventArgs e)
  434.         {
  435.             _isAddLink = true;
  436.             _selectedStartRectShape = null;
  437.             _selectedEndRectShape = null;
  438.             _selectedStartEllipseShape = null;
  439.             _selectedEndEllipseShape = null;
  440.             toolStripStatusLabel1.Text = "请点击第1个形状";
  441.         }
  442.         private void toolStripButton3_Click(object sender, EventArgs e)
  443.         {
  444.             _isAddLink = false;
  445.             _selectedStartRectShape = null;
  446.             _selectedEndRectShape = null;
  447.             toolStripStatusLabel1.Text = "";
  448.             DrawAll(panel1.CreateGraphics());
  449.         }
  450.         private void toolStripButton4_Click(object sender, EventArgs e)
  451.         {
  452.             var rs = new EllipseShape()
  453.             {
  454.                 Id = "圆形" + (EllipseShapes.Count + 1),
  455.                 Rect = new Rectangle()
  456.                 {
  457.                     X = 50,
  458.                     Y = 50,
  459.                     Width = 100,
  460.                     Height = 100,
  461.                 },
  462.             };
  463.             EllipseShapes.Add(rs);
  464.             //重绘所有矩形
  465.             DrawAll(panel1.CreateGraphics());
  466.         }
  467.     }
  468. }
复制代码
五、结语

本一小节总体而言很简单,就是增加一个新的形状:圆形。但是在代码流程上却复杂很多,这也是我们后面课程使用抽象来优化的原因之一。
下节课我们就来到了显示效果优化部分了,除了显示质量外,还有拖动时闪烁的问题也会同步解决。敬请期待。
感谢大家的观看,本人水平有限,文章不足之处欢迎大家评论指正。
-[END]-

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册