[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

一、前言

上一节我们实现了多个不同颜色的可拖动的矩形,那么这一节就来看一下如何将这些矩形连起来吧。

相信看完的你,一定会有所收获!

本文地址:https://www.cnblogs.com/lesliexin/p/18923105

二、先看效果

同样的,我们先来看一下本节所实现的效果,先有一个整体的印象。

通过视频我们可以看到,我们本节仍是分为三个步骤依次递进:

在两个矩形中添加一条直线,连接两个形状的中心
在【任意】两个矩形之间添加一条直线
在任意两个矩形之间添加【不同颜色】直线,连线去重,中止添加连线操作

下面我们就来依次讲解。

对了,这里说明一下,从本节课程开始,因为后续的课程都是在前一节课程的基础上实现的,所以原理图中会着重标识出来(一般使用绿色)这些新的点,同时代码实操部分仅会讲解对应的这部分代码。因为之前都已讲过,每次都从零讲篇幅会越来越长而且容易产生混乱。当然了,虽然仅讲重点,但是全部的代码时会贴到每小节最后的,大家可以自行查看和编译。

三、实现效果1:在两个矩形中添加一条直线

我们先来实现最简单的情况:在两个矩形中添加一条直线

(一)原理

原理很简单,在绘制矩形后,取两个矩形的中心点,然后绘制一条直线即可。

原理图如下,注意图中的绿色元素就是与上节课原理图的差异之处。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

顺便说一句:本文所有的原理图都是使用《LN流程图》制作的,而这也正是本系列课程最终的实现效果,相当于自产自销了是。

(二)代码实操

1,设计器界面

设计器界面没什么大的变化:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

2,添加连线代码

我们看下原理图中的绿色形状,我们要添加一条直线,以连接两个矩形的中心点。

那么首先就是计算两个矩形的中心点坐标到两个全局变量:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

之后就在绘制矩形后,添加上绘制连线的方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

3,鼠标移动事件

我们再来看下原理图中的另一个绿色形状说明,我们要在鼠标移动时,重新计算连线两点的坐标,并进行重绘,所以我们需要在MouseMove事件中添加计算两点坐标的方法。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果1了。

下面有完整的代码,大家尝试一下吧。

点击查看代码
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 FlowChartDemo {     public partial class FormDemo02V1 : FormBase     {         public FormDemo02V1()         {             InitializeComponent();             DemoTitle = "第03节随课Demo  Part1";             DemoNote = "效果:在两个矩形中添加【一条】直线,连接两个形状的中心";         }          /// <summary>         /// 矩形定义         /// </summary>         public class RectShape         {             /// <summary>             /// 矩形ID             /// </summary>             public string Id { get; set; }             /// <summary>             /// 矩形位置和尺寸             /// </summary>             public Rectangle Rect { get; set; }         }          /// <summary>         /// 当前界面矩形集合         /// </summary>         List<RectShape> Shapes = new List<RectShape>();          /// <summary>         /// 画一个矩形(不同颜色)         /// </summary>         /// <param name="g"></param>         /// <param name="shape"></param>         void DrawShape2(Graphics g, RectShape shape)         {             var index = Shapes.FindIndex(a => a.Id == shape.Id);             g.FillRectangle(GetBrush(index), shape.Rect);             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);         }          /// <summary>         /// 重新绘制当前所有矩形和连线         /// </summary>         /// <param name="g"></param>         void DrawAll(Graphics g)         {             g.Clear(panel1.BackColor) ;             foreach (var sp in Shapes)             {                 DrawShape2(g, sp);             }              if (!LinePointStart.IsEmpty&& !LinePointEnd.IsEmpty)             {                 //如果连线两个端点不是空的,则绘制连线                 g.DrawLine(Pens.Black, LinePointStart, LinePointEnd);             }         }          /// <summary>         /// 当前是否有鼠标按下,且有矩形被选中         /// </summary>         bool _isMouseDown = false;         /// <summary>         /// 最后一次鼠标的位置         /// </summary>         Point _lastMouseLocation = Point.Empty;         /// <summary>         /// 当前被鼠标选中的矩形         /// </summary>         RectShape _selectedShape = null;         /// <summary>         /// 连线起点         /// </summary>         Point LinePointStart = Point.Empty;         /// <summary>         /// 连线终点         /// </summary>         Point LinePointEnd = Point.Empty;          /// <summary>         /// 获取不同的背景颜色         /// </summary>         /// <param name="i"></param>         /// <returns></returns>         Brush GetBrush(int i)         {             switch (i)             {                 case 0: return Brushes.Red;                 case 1: return Brushes.Green;                 case 2: return Brushes.Blue;                 case 3: return Brushes.Orange;                 case 4: return Brushes.Purple;                 default: return Brushes.Red;             }         }          /// <summary>         /// 计算并生成连线的两个连接点         /// </summary>         void CreateLinePoint()         {              //计算连线开始点坐标             var line1X = Shapes[0].Rect.X + Shapes[0].Rect.Width / 2;             var line1Y = Shapes[0].Rect.Y + Shapes[0].Rect.Height / 2;             LinePointStart = new Point(line1X, line1Y);              //计算连线结束点坐标             var line2X = Shapes[1].Rect.X + Shapes[1].Rect.Width / 2;             var line2Y = Shapes[1].Rect.Y + Shapes[1].Rect.Height / 2;             LinePointEnd = new Point(line2X, line2Y);         }          private void toolStripButton1_Click(object sender, EventArgs e)         {             //此处仅显示两个形状             if (Shapes.Count != 0)             {                 MessageBox.Show("形状已添加,不可重复操作!");                 return;             }                          //添加2个矩形             var rs = new RectShape()             {                 Id = "矩形" + (Shapes.Count + 1),                 Rect = new Rectangle()                 {                     X = 50,                     Y = 50,                     Width = 100,                     Height = 100,                 },             };             Shapes.Add(rs);             rs = new RectShape()             {                 Id = "矩形" + (Shapes.Count + 1),                 Rect = new Rectangle()                 {                     X = 50,                     Y = 50,                     Width = 100,                     Height = 100,                 },             };             Shapes.Add(rs);              //生成连线的两个连接点             CreateLinePoint();              //重绘所有矩形             DrawAll(panel1.CreateGraphics());         }          private void panel1_MouseDown(object sender, MouseEventArgs e)         {             //当鼠标按下时              //取最上方的矩形,也就是最后添加的矩形             var sp = Shapes.FindLast(a => a.Rect.Contains(e.Location));             if (sp != null)             {                 //证明取到了矩形                  //设置状态及选中矩形                 _isMouseDown = true;                 _lastMouseLocation = e.Location;                 _selectedShape = sp;             }         }          private void panel1_MouseMove(object sender, MouseEventArgs e)         {             //当鼠标移动时              if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //改变选中矩形的位置信息,随着鼠标移动而移动                  //计算鼠标位置变化信息                 var moveX = e.Location.X - _lastMouseLocation.X;                 var moveY = e.Location.Y - _lastMouseLocation.Y;                  //将选中形状的位置进行同样的变化                 var oldXY = _selectedShape.Rect.Location;                 oldXY.Offset(moveX, moveY);                 _selectedShape.Rect = new Rectangle(oldXY, _selectedShape.Rect.Size);                  //记录当前鼠标位置                 _lastMouseLocation.Offset(moveX, moveY);                  //因为形状位置发生了变化,所以要重新计算连线的两个连接点                 CreateLinePoint();                  //重绘所有矩形                 DrawAll(panel1.CreateGraphics());             }          }          private void panel1_MouseUp(object sender, MouseEventArgs e)         {             //当鼠标松开时             if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //重置相关记录信息                 _isMouseDown = false;                 _lastMouseLocation = Point.Empty;                 _selectedShape = null;             }         }      }   }  

四、实现效果2:任意两个矩形之间添加一条直线

我们下面实现效果2,这个改动就比较多了,我们下面一点点讲解。

(一)原理

同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

可以看到,增加的节点流程还是挺多的,大家可以尝试先自行读一下原理图,有一个整体的印象,好看下面的实操部分。

(二)代码实操

1,设计器界面

因为要在任意两个矩形之间添加一条直线,所以就需要有一个连线状态控制及提示,所以界面也有了些改动:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

2,进入连线状态

我们要在任意两个矩形之间添加直线,肯定要依次选择两个矩形,这就需要有个“连线状态”,以标识我们现在点击矩形是为了添加连线,而不是移动矩形什么的。

我们用一个按钮和变量来控制,点击按钮后,设置变量,以标识现在进入了连线状态,并设置提示信息“请点击第1个矩形”:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

3,鼠标点击事件

我们通过原理图可以看到,变化最大地方就是这个MouseDown事件,在这个事件中,我们要额外判断是否处理连线状态,并判断已经选择的矩形等等控制。

好在逻辑很简单,我们依照原理图一步步写代码即可:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

4,鼠标移动事件

在MouseMove事件中,我们通过原理图可以看到,我们首先要判断下是否处于连线状态,防止在选择矩形时矩形发生移动等变化。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

5,生成连线

因为我们要在任意两个矩形间生成连线,所以就不能像上一小节中那样简单的定义两个坐标变量。

连线会有很多,所以我们先定义一个连线类:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

然后定义一个连线列表的全局变量:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

在生成连线,我们需要知道两个矩形ID对应的矩形的中心点,所以我实现根据矩形ID求矩形中心点的方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

之后就像绘制矩形的方法一样,我们写一个维护连线的方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

最后,我们在绘制所有矩形和连线的方法中,加上依次绘制所有连线的方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果2了。

下面有完整的代码,大家尝试一下吧。

点击查看代码
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 FlowChartDemo {     public partial class FormDemo02V2 : FormBase     {         public FormDemo02V2()         {             InitializeComponent();             DemoTitle = "第03节随课Demo  Part2";             DemoNote = "效果:在【任意】两个矩形之间添加【一条】直线,连接两个形状的中心";         }          /// <summary>         /// 矩形定义         /// </summary>         public class RectShape         {             /// <summary>             /// 矩形ID             /// </summary>             public string Id { get; set; }             /// <summary>             /// 矩形位置和尺寸             /// </summary>             public Rectangle Rect { get; set; }         }          /// <summary>         /// 直线连线定义         /// </summary>         public class LineLink         {             /// <summary>             /// 连线ID             /// </summary>             public string Id { get; set; }             /// <summary>             /// 连线开始形状ID             /// </summary>             public string StartShapeId { get; set; }             /// <summary>             /// 连线结束形状ID             /// </summary>             public string EndShapeId { get; set; }         }          /// <summary>         /// 当前界面矩形集合         /// </summary>         List<RectShape> Shapes = new List<RectShape>();         /// <summary>         /// 当前界面连线集合         /// </summary>         List<LineLink> Links = new List<LineLink>();          /// <summary>         /// 画一个矩形(不同颜色)         /// </summary>         /// <param name="g"></param>         /// <param name="shape"></param>         void DrawShape2(Graphics g, RectShape shape)         {             var index = Shapes.FindIndex(a => a.Id == shape.Id);             g.FillRectangle(GetBrush(index), shape.Rect);             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);         }          /// <summary>         /// 绘制一条连线         /// </summary>         /// <param name="g"></param>         /// <param name="line"></param>         void DrawLine(Graphics g,LineLink line)         {             //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标             var startPoint = GetCentertPoint(line.StartShapeId);             var endPoint = GetCentertPoint(line.EndShapeId);             //绘制一条直线             g.DrawLine(Pens.Black, startPoint, endPoint);         }          /// <summary>         /// 重新绘制当前所有矩形和连线         /// </summary>         /// <param name="g"></param>         void DrawAll(Graphics g)         {             g.Clear(panel1.BackColor) ;             //绘制所有形状             foreach (var sp in Shapes)             {                 DrawShape2(g, sp);             }             //绘制所有连线             foreach (var ln in Links)             {                 DrawLine(g, ln);             }         }          /// <summary>         /// 当前是否有鼠标按下,且有矩形被选中         /// </summary>         bool _isMouseDown = false;         /// <summary>         /// 最后一次鼠标的位置         /// </summary>         Point _lastMouseLocation = Point.Empty;         /// <summary>         /// 当前被鼠标选中的矩形         /// </summary>         RectShape _selectedShape = null;          /// <summary>         /// 添加连线时选中的第一个形状         /// </summary>         RectShape _selectedStartShape = null;         /// <summary>         /// 添加连线时选中的第二个形状         /// </summary>         RectShape _selectedEndShape = null;         /// <summary>         /// 是否正添加连线         /// </summary>         bool _isAddLink = false;          /// <summary>         /// 获取不同的背景颜色         /// </summary>         /// <param name="i"></param>         /// <returns></returns>         Brush GetBrush(int i)         {             switch (i)             {                 case 0: return Brushes.Red;                 case 1: return Brushes.Green;                 case 2: return Brushes.Blue;                 case 3: return Brushes.Orange;                 case 4: return Brushes.Purple;                 default: return Brushes.Red;             }         }          /// <summary>         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点         /// </summary>         /// <param name="shapeId"></param>         /// <returns></returns>         Point GetCentertPoint(string shapeId)         {             var sp = Shapes.Find(a => a.Id == shapeId);             if (sp != null)             {                 var line1X = sp.Rect.X + sp.Rect.Width / 2;                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;                 return new Point(line1X, line1Y);             }             return Point.Empty;         }           private void toolStripButton1_Click(object sender, EventArgs e)         {             var rs = new RectShape()             {                 Id = "矩形" + (Shapes.Count + 1),                 Rect = new Rectangle()                 {                     X = 50,                     Y = 50,                     Width = 100,                     Height = 100,                 },             };             Shapes.Add(rs);              //重绘所有矩形             DrawAll(panel1.CreateGraphics());         }          private void panel1_MouseDown(object sender, MouseEventArgs e)         {             //当鼠标按下时              //取最上方的矩形,也就是最后添加的矩形             var sp = Shapes.FindLast(a => a.Rect.Contains(e.Location));             if (sp != null)             {                 //证明取到了矩形                  //判断是否正在添加连线                 if (_isAddLink)                 {                     //正在添加连线                     if (_selectedStartShape == null)                     {                         //如果开始形状还没选择,则设置开始形状                         _selectedStartShape = sp;                         toolStripStatusLabel1.Text = "请点击第2个形状";                     }                     else if (_selectedEndShape == null)                     {                         //判断第2个形状是否是第1个形状                         if (_selectedStartShape.Id == sp.Id)                         {                             toolStripStatusLabel1.Text = "不可选择同一个形状,请重新点击第2个形状";                         }                         else                         {                             //如果结束形状还没选择,则设置结束形状                             _selectedEndShape = sp;                              //两个形状都设置了,便添加一条新连线                             Links.Add(new LineLink()                             {                                 Id = "连线" + (Links.Count + 1),                                 StartShapeId = _selectedStartShape.Id,                                 EndShapeId = _selectedEndShape.Id,                             });                             //两个形状都已选择,结束添加连线状态                             _isAddLink = false;                             toolStripStatusLabel1.Text = "";                             //重绘以显示连线                             DrawAll(panel1.CreateGraphics());                         }                     }                  }                 else                 {                     //如果没有在添加连线,则正常选中矩形                      //设置状态及选中矩形                     _isMouseDown = true;                     _lastMouseLocation = e.Location;                     _selectedShape = sp;                 }             }         }          private void panel1_MouseMove(object sender, MouseEventArgs e)         {             //当鼠标移动时              //如果处于添加连线时,则不移动形状             if (_isAddLink) return;              if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //改变选中矩形的位置信息,随着鼠标移动而移动                  //计算鼠标位置变化信息                 var moveX = e.Location.X - _lastMouseLocation.X;                 var moveY = e.Location.Y - _lastMouseLocation.Y;                  //将选中形状的位置进行同样的变化                 var oldXY = _selectedShape.Rect.Location;                 oldXY.Offset(moveX, moveY);                 _selectedShape.Rect = new Rectangle(oldXY, _selectedShape.Rect.Size);                  //记录当前鼠标位置                 _lastMouseLocation.Offset(moveX, moveY);                  //重绘所有矩形                 DrawAll(panel1.CreateGraphics());             }          }          private void panel1_MouseUp(object sender, MouseEventArgs e)         {             //当鼠标松开时             if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //重置相关记录信息                 _isMouseDown = false;                 _lastMouseLocation = Point.Empty;                 _selectedShape = null;             }         }          private void toolStripButton2_Click(object sender, EventArgs e)         {             _isAddLink = true;             _selectedStartShape = null;             _selectedEndShape = null;             toolStripStatusLabel1.Text = "请点击第1个形状";         }     }   }  

五、实现效果3:添加不同颜色直线,连线去重,中止添加连线

我们下面实现效果3,这次的效果改动很少,更多的是一些易用性上的优化,我们下面一点点讲解。

(一)原理

同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

可以看到,增加的节点流程就一个,判断是否已经添加过相同的连线。

(二)代码实操

1,设计器界面

本次的界面主要增加了中止连线的按钮。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

2,不同颜色的直线

我们先来看最简单的添加不同颜色的直线,这个和上一章不同颜色的矩形实现逻辑是一样的,获取不同颜色的画笔就行了:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

同样的,绘制直线方法名我们也增加个2,来作下区分,这里只是用来区分,在实现项目及后续教程中就是直接改原方法了,也是贯彻抽象的思路。

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

3,连线去重

我们的连线是连接两个矩形的中心点,如果多个连线的开始矩形和结束矩形都一样,是看不出效果的,是没必要的,所以我们这里添加下去重。

注:因为本篇教程连线是连接的两个矩形的中心点,所以有去重的必要。在后续章节,我们连接的就不是矩形的中心了,而是矩形(包括其它形状)的“连接点”了,连接点的概念很好理解,就是日常使用流程图时,连线都是到上下左右边的中间点(当然不止这些),我们后续也会这样实现。

我们参照原理图,在MouseDown事件中,添加去重判断的代码:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

3,中止添加连线操作

我们在添加连线时,可能点错了开始矩形,在上面小节中,我们是没办法结束的,只能继续选另一个矩形,所以我们增加上中止添加连线操作。

注:在后续的章节中,我们会讲解如何实现更符合操作逻辑的添加及中止连线方式:点按开始形状的连接点,移动到另一个形状的连接点上,完成连线操作操作。在移动时有虚线箭头提示,且松开鼠标自动取消连线。敬请期待。

实现代码很简单,我们只需要设置连线状态标志为false即可:

[原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!

好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果3了。

下面有完整的代码,大家尝试一下吧。

点击查看代码
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 FlowChartDemo {     public partial class FormDemo02V3 : FormBase     {         public FormDemo02V3()         {             InitializeComponent();             DemoTitle = "第04节随课Demo  Part1";             DemoNote = "效果:在【任意】两个矩形之间添加【不同颜色】直线,连线去重,中止添加连线功能";         }          //记得删除:中止功能是因为如果因去重而无法添加下一个形状,而无法移动形状          /// <summary>         /// 矩形定义         /// </summary>         public class RectShape         {             /// <summary>             /// 矩形ID             /// </summary>             public string Id { get; set; }             /// <summary>             /// 矩形位置和尺寸             /// </summary>             public Rectangle Rect { get; set; }         }          /// <summary>         /// 直线连线定义         /// </summary>         public class LineLink         {             /// <summary>             /// 连线ID             /// </summary>             public string Id { get; set; }             /// <summary>             /// 连线开始形状ID             /// </summary>             public string StartShapeId { get; set; }             /// <summary>             /// 连线结束形状ID             /// </summary>             public string EndShapeId { get; set; }         }          /// <summary>         /// 当前界面矩形集合         /// </summary>         List<RectShape> Shapes = new List<RectShape>();         /// <summary>         /// 当前界面连线集合         /// </summary>         List<LineLink> Links = new List<LineLink>();          /// <summary>         /// 画一个矩形(不同颜色)         /// </summary>         /// <param name="g"></param>         /// <param name="shape"></param>         void DrawShape2(Graphics g, RectShape shape)         {             var index = Shapes.FindIndex(a => a.Id == shape.Id);             g.FillRectangle(GetBrush(index), shape.Rect);             g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);         }          /// <summary>         /// 绘制一条连线         /// </summary>         /// <param name="g"></param>         /// <param name="line"></param>         void DrawLine(Graphics g, LineLink line)         {             //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标             var startPoint = GetCentertPoint(line.StartShapeId);             var endPoint = GetCentertPoint(line.EndShapeId);             //绘制一条直线             g.DrawLine(Pens.Black, startPoint, endPoint);         }          /// <summary>         /// 绘制一条连线(不同颜色)         /// </summary>         /// <param name="g"></param>         /// <param name="line"></param>         void DrawLine2(Graphics g, LineLink line)         {             //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标             var startPoint = GetCentertPoint(line.StartShapeId);             var endPoint = GetCentertPoint(line.EndShapeId);              var index = Links.FindIndex(a => a.Id == line.Id);             //绘制一条直线             g.DrawLine(GetPen(index), startPoint, endPoint);         }          /// <summary>         /// 重新绘制当前所有矩形和连线         /// </summary>         /// <param name="g"></param>         void DrawAll(Graphics g)         {             g.Clear(panel1.BackColor) ;             //绘制所有形状             foreach (var sp in Shapes)             {                 DrawShape2(g, sp);             }             //绘制所有连线             foreach (var ln in Links)             {                 DrawLine2(g, ln);             }         }          /// <summary>         /// 当前是否有鼠标按下,且有矩形被选中         /// </summary>         bool _isMouseDown = false;         /// <summary>         /// 最后一次鼠标的位置         /// </summary>         Point _lastMouseLocation = Point.Empty;         /// <summary>         /// 当前被鼠标选中的矩形         /// </summary>         RectShape _selectedShape = null;          /// <summary>         /// 添加连线时选中的第一个形状         /// </summary>         RectShape _selectedStartShape = null;         /// <summary>         /// 添加连线时选中的第二个形状         /// </summary>         RectShape _selectedEndShape = null;         /// <summary>         /// 是否正添加连线         /// </summary>         bool _isAddLink = false;          /// <summary>         /// 获取不同的背景颜色         /// </summary>         /// <param name="i"></param>         /// <returns></returns>         Brush GetBrush(int i)         {             switch (i)             {                 case 0: return Brushes.Red;                 case 1: return Brushes.Green;                 case 2: return Brushes.Blue;                 case 3: return Brushes.Orange;                 case 4: return Brushes.Purple;                 default: return Brushes.Red;             }         }         /// <summary>         /// 获取不同的画笔颜色         /// </summary>         /// <param name="i"></param>         /// <returns></returns>         Pen GetPen(int i)         {             return new Pen(GetBrush(i), 2);         }          /// <summary>         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点         /// </summary>         /// <param name="shapeId"></param>         /// <returns></returns>         Point GetCentertPoint(string shapeId)         {             var sp = Shapes.Find(a => a.Id == shapeId);             if (sp != null)             {                 var line1X = sp.Rect.X + sp.Rect.Width / 2;                 var line1Y = sp.Rect.Y + sp.Rect.Height / 2;                 return new Point(line1X, line1Y);             }             return Point.Empty;         }           private void toolStripButton1_Click(object sender, EventArgs e)         {             var rs = new RectShape()             {                 Id = "矩形" + (Shapes.Count + 1),                 Rect = new Rectangle()                 {                     X = 50,                     Y = 50,                     Width = 100,                     Height = 100,                 },             };             Shapes.Add(rs);              //重绘所有矩形             DrawAll(panel1.CreateGraphics());         }          private void panel1_MouseDown(object sender, MouseEventArgs e)         {             //当鼠标按下时              //取最上方的矩形,也就是最后添加的矩形             var sp = Shapes.FindLast(a => a.Rect.Contains(e.Location));             if (sp != null)             {                 //证明取到了矩形                  //判断是否正在添加连线                 if (_isAddLink)                 {                     //正在添加连线                     if (_selectedStartShape == null)                     {                         //如果开始形状还没选择,则设置开始形状                         _selectedStartShape = sp;                         toolStripStatusLabel1.Text = "请点击第2个形状";                     }                     else if (_selectedEndShape == null)                     {                         //判断第2个形状是否是第1个形状                         if (_selectedStartShape.Id == sp.Id)                         {                             toolStripStatusLabel1.Text = "不可选择同一个形状,请重新点击第2个形状";                         }                         else                         {                             //去重判断,防止添加重复的连线                             if (Links.Any(a => (a.StartShapeId == _selectedStartShape.Id && a.EndShapeId == sp.Id)                             //判断是否存在连线的开始是选中的第1个形状,结束是选中的第2个形状。                              || (a.EndShapeId == _selectedStartShape.Id && a.StartShapeId == sp.Id)))                              //判断是否存在连线的开始是选中的第2个形状,结束是选中的第1个形状。(相当于反向,但画出来是一条线)                             {                                 toolStripStatusLabel1.Text = "已有连线,请重新点击第2个形状";                             }                             else                             {                                 //如果结束形状还没选择,则设置结束形状                                 _selectedEndShape = sp;                                  //两个形状都设置了,便添加一条新连线                                 Links.Add(new LineLink()                                 {                                     Id = "连线" + (Links.Count + 1),                                     StartShapeId = _selectedStartShape.Id,                                     EndShapeId = _selectedEndShape.Id,                                 });                                 //两个形状都已选择,结束添加连线状态                                 _isAddLink = false;                                 toolStripStatusLabel1.Text = "";                                 //重绘以显示连线                                 DrawAll(panel1.CreateGraphics());                             }                         }                     }                  }                 else                 {                     //如果没有在添加连线,则正常选中矩形                      //设置状态及选中矩形                     _isMouseDown = true;                     _lastMouseLocation = e.Location;                     _selectedShape = sp;                 }             }         }          private void panel1_MouseMove(object sender, MouseEventArgs e)         {             //当鼠标移动时              //如果处于添加连线时,则不移动形状             if (_isAddLink) return;              if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //改变选中矩形的位置信息,随着鼠标移动而移动                  //计算鼠标位置变化信息                 var moveX = e.Location.X - _lastMouseLocation.X;                 var moveY = e.Location.Y - _lastMouseLocation.Y;                  //将选中形状的位置进行同样的变化                 var oldXY = _selectedShape.Rect.Location;                 oldXY.Offset(moveX, moveY);                 _selectedShape.Rect = new Rectangle(oldXY, _selectedShape.Rect.Size);                  //记录当前鼠标位置                 _lastMouseLocation.Offset(moveX, moveY);                  //重绘所有矩形                 DrawAll(panel1.CreateGraphics());             }          }          private void panel1_MouseUp(object sender, MouseEventArgs e)         {             //当鼠标松开时             if (_isMouseDown)             {                 //当且仅当:有鼠标按下且有矩形被选中时,才进行后续操作                  //重置相关记录信息                 _isMouseDown = false;                 _lastMouseLocation = Point.Empty;                 _selectedShape = null;             }         }          private void toolStripButton2_Click(object sender, EventArgs e)         {             _isAddLink = true;             _selectedStartShape = null;             _selectedEndShape = null;             toolStripStatusLabel1.Text = "请点击第1个形状";         }          private void toolStripButton3_Click(object sender, EventArgs e)         {             _isAddLink = false;             _selectedStartShape = null;             _selectedEndShape = null;             toolStripStatusLabel1.Text = "";             DrawAll(panel1.CreateGraphics());         }     }   }  

六、结语

本篇教程我们从零实现并完善了在任意两个矩形中添加矩形的功能,本章都是围绕这个核心功能进行讲解,扩展内容很少,学习起来也很容易。

下章我们会讲一下如何添加其它形状,如圆形,以及如何在矩形和圆形之间添加连线。后面还会讲一下如何优化显示效果,及拖动时闪烁的问题。敬请期待。

感谢大家的观看,本人水平有限,文章不足之处欢迎大家评论指正。

-[END]-

发表评论

评论已关闭。

相关文章