龙正平 发表于 2025-6-18 09:40:36

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

一、前言

上一节我们实现了多个不同颜色的可拖动的矩形,那么这一节就来看一下如何将这些矩形连起来吧。
相信看完的你,一定会有所收获!
本文地址:https://www.cnblogs.com/lesliexin/p/18923105
二、先看效果

同样的,我们先来看一下本节所实现的效果,先有一个整体的印象。
通过视频我们可以看到,我们本节仍是分为三个步骤依次递进:
在两个矩形中添加一条直线,连接两个形状的中心
在【任意】两个矩形之间添加一条直线
在任意两个矩形之间添加【不同颜色】直线,连线去重,中止添加连线操作
下面我们就来依次讲解。
对了,这里说明一下,从本节课程开始,因为后续的课程都是在前一节课程的基础上实现的,所以原理图中会着重标识出来(一般使用绿色)这些新的点,同时代码实操部分仅会讲解对应的这部分代码。因为之前都已讲过,每次都从零讲篇幅会越来越长而且容易产生混乱。当然了,虽然仅讲重点,但是全部的代码时会贴到每小节最后的,大家可以自行查看和编译。
三、实现效果1:在两个矩形中添加一条直线

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

原理很简单,在绘制矩形后,取两个矩形的中心点,然后绘制一条直线即可。
原理图如下,注意图中的绿色元素就是与上节课原理图的差异之处。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250611130940053-1029008326.png
顺便说一句:本文所有的原理图都是使用《LN流程图》制作的,而这也正是本系列课程最终的实现效果,相当于自产自销了是。
(二)代码实操

1,设计器界面

设计器界面没什么大的变化:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612155242747-543729884.png
2,添加连线代码

我们看下原理图中的绿色形状,我们要添加一条直线,以连接两个矩形的中心点。
那么首先就是计算两个矩形的中心点坐标到两个全局变量:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612155623192-940797346.png
之后就在绘制矩形后,添加上绘制连线的方法:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612160053388-148829608.png
3,鼠标移动事件

我们再来看下原理图中的另一个绿色形状说明,我们要在鼠标移动时,重新计算连线两点的坐标,并进行重绘,所以我们需要在MouseMove事件中添加计算两点坐标的方法。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612160324052-2091551294.png
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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节随课DemoPart1";            DemoNote = "效果:在两个矩形中添加【一条】直线,连接两个形状的中心";      }      ///         /// 矩形定义      ///         public class RectShape      {            ///             /// 矩形ID            ///             public string Id { get; set; }            ///             /// 矩形位置和尺寸            ///             public Rectangle Rect { get; set; }      }      ///         /// 当前界面矩形集合      ///         List Shapes = new List();      ///         /// 画一个矩形(不同颜色)      ///         ///         ///         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);      }      ///         /// 重新绘制当前所有矩形和连线      ///         ///         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);            }      }      ///         /// 当前是否有鼠标按下,且有矩形被选中      ///         bool _isMouseDown = false;      ///         /// 最后一次鼠标的位置      ///         Point _lastMouseLocation = Point.Empty;      ///         /// 当前被鼠标选中的矩形      ///         RectShape _selectedShape = null;      ///         /// 连线起点      ///         Point LinePointStart = Point.Empty;      ///         /// 连线终点      ///         Point LinePointEnd = Point.Empty;      ///         /// 获取不同的背景颜色      ///         ///         ///         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;            }      }      ///         /// 计算并生成连线的两个连接点      ///         void CreateLinePoint()      {            //计算连线开始点坐标            var line1X = Shapes.Rect.X + Shapes.Rect.Width / 2;            var line1Y = Shapes.Rect.Y + Shapes.Rect.Height / 2;            LinePointStart = new Point(line1X, line1Y);            //计算连线结束点坐标            var line2X = Shapes.Rect.X + Shapes.Rect.Width / 2;            var line2Y = Shapes.Rect.Y + Shapes.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,这个改动就比较多了,我们下面一点点讲解。
(一)原理

同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612160708778-596473477.png
可以看到,增加的节点流程还是挺多的,大家可以尝试先自行读一下原理图,有一个整体的印象,好看下面的实操部分。
(二)代码实操

1,设计器界面

因为要在任意两个矩形之间添加一条直线,所以就需要有一个连线状态控制及提示,所以界面也有了些改动:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612161034260-1594505608.png
2,进入连线状态

我们要在任意两个矩形之间添加直线,肯定要依次选择两个矩形,这就需要有个“连线状态”,以标识我们现在点击矩形是为了添加连线,而不是移动矩形什么的。
我们用一个按钮和变量来控制,点击按钮后,设置变量,以标识现在进入了连线状态,并设置提示信息“请点击第1个矩形”:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612164532634-736343813.png
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612164017921-347185764.png
3,鼠标点击事件

我们通过原理图可以看到,变化最大地方就是这个MouseDown事件,在这个事件中,我们要额外判断是否处理连线状态,并判断已经选择的矩形等等控制。
好在逻辑很简单,我们依照原理图一步步写代码即可:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250612165057778-1583737221.png
4,鼠标移动事件

在MouseMove事件中,我们通过原理图可以看到,我们首先要判断下是否处于连线状态,防止在选择矩形时矩形发生移动等变化。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613091814182-801221780.png
5,生成连线

因为我们要在任意两个矩形间生成连线,所以就不能像上一小节中那样简单的定义两个坐标变量。
连线会有很多,所以我们先定义一个连线类:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613092029575-507867470.png
然后定义一个连线列表的全局变量:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613092255798-86844573.png
在生成连线,我们需要知道两个矩形ID对应的矩形的中心点,所以我实现根据矩形ID求矩形中心点的方法:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613092410666-1782234966.png
之后就像绘制矩形的方法一样,我们写一个维护连线的方法:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613092529405-950822365.png
最后,我们在绘制所有矩形和连线的方法中,加上依次绘制所有连线的方法:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613092622573-1564013546.png
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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节随课DemoPart2";            DemoNote = "效果:在【任意】两个矩形之间添加【一条】直线,连接两个形状的中心";      }      ///         /// 矩形定义      ///         public class RectShape      {            ///             /// 矩形ID            ///             public string Id { get; set; }            ///             /// 矩形位置和尺寸            ///             public Rectangle Rect { get; set; }      }      ///         /// 直线连线定义      ///         public class LineLink      {            ///             /// 连线ID            ///             public string Id { get; set; }            ///             /// 连线开始形状ID            ///             public string StartShapeId { get; set; }            ///             /// 连线结束形状ID            ///             public string EndShapeId { get; set; }      }      ///         /// 当前界面矩形集合      ///         List Shapes = new List();      ///         /// 当前界面连线集合      ///         List Links = new List();      ///         /// 画一个矩形(不同颜色)      ///         ///         ///         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);      }      ///         /// 绘制一条连线      ///         ///         ///         void DrawLine(Graphics g,LineLink line)      {            //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标            var startPoint = GetCentertPoint(line.StartShapeId);            var endPoint = GetCentertPoint(line.EndShapeId);            //绘制一条直线            g.DrawLine(Pens.Black, startPoint, endPoint);      }      ///         /// 重新绘制当前所有矩形和连线      ///         ///         void DrawAll(Graphics g)      {            g.Clear(panel1.BackColor) ;            //绘制所有形状            foreach (var sp in Shapes)            {                DrawShape2(g, sp);            }            //绘制所有连线            foreach (var ln in Links)            {                DrawLine(g, ln);            }      }      ///         /// 当前是否有鼠标按下,且有矩形被选中      ///         bool _isMouseDown = false;      ///         /// 最后一次鼠标的位置      ///         Point _lastMouseLocation = Point.Empty;      ///         /// 当前被鼠标选中的矩形      ///         RectShape _selectedShape = null;      ///         /// 添加连线时选中的第一个形状      ///         RectShape _selectedStartShape = null;      ///         /// 添加连线时选中的第二个形状      ///         RectShape _selectedEndShape = null;      ///         /// 是否正添加连线      ///         bool _isAddLink = false;      ///         /// 获取不同的背景颜色      ///         ///         ///         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;            }      }      ///         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点      ///         ///         ///         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,这次的效果改动很少,更多的是一些易用性上的优化,我们下面一点点讲解。
(一)原理

同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613095139344-2144898906.png
可以看到,增加的节点流程就一个,判断是否已经添加过相同的连线。
(二)代码实操

1,设计器界面

本次的界面主要增加了中止连线的按钮。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613095158100-2058259288.png
2,不同颜色的直线

我们先来看最简单的添加不同颜色的直线,这个和上一章不同颜色的矩形实现逻辑是一样的,获取不同颜色的画笔就行了:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613095437119-2098445259.png
同样的,绘制直线方法名我们也增加个2,来作下区分,这里只是用来区分,在实现项目及后续教程中就是直接改原方法了,也是贯彻抽象的思路。
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613095559456-1669084427.png
3,连线去重

我们的连线是连接两个矩形的中心点,如果多个连线的开始矩形和结束矩形都一样,是看不出效果的,是没必要的,所以我们这里添加下去重。
注:因为本篇教程连线是连接的两个矩形的中心点,所以有去重的必要。在后续章节,我们连接的就不是矩形的中心了,而是矩形(包括其它形状)的“连接点”了,连接点的概念很好理解,就是日常使用流程图时,连线都是到上下左右边的中间点(当然不止这些),我们后续也会这样实现。
我们参照原理图,在MouseDown事件中,添加去重判断的代码:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613100104866-2025321410.png
3,中止添加连线操作

我们在添加连线时,可能点错了开始矩形,在上面小节中,我们是没办法结束的,只能继续选另一个矩形,所以我们增加上中止添加连线操作。
注:在后续的章节中,我们会讲解如何实现更符合操作逻辑的添加及中止连线方式:点按开始形状的连接点,移动到另一个形状的连接点上,完成连线操作操作。在移动时有虚线箭头提示,且松开鼠标自动取消连线。敬请期待。
实现代码很简单,我们只需要设置连线状态标志为false即可:
https://img2024.cnblogs.com/blog/1686429/202506/1686429-20250613100737938-673961163.png
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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节随课DemoPart1";            DemoNote = "效果:在【任意】两个矩形之间添加【不同颜色】直线,连线去重,中止添加连线功能";      }      //记得删除:中止功能是因为如果因去重而无法添加下一个形状,而无法移动形状      ///         /// 矩形定义      ///         public class RectShape      {            ///             /// 矩形ID            ///             public string Id { get; set; }            ///             /// 矩形位置和尺寸            ///             public Rectangle Rect { get; set; }      }      ///         /// 直线连线定义      ///         public class LineLink      {            ///             /// 连线ID            ///             public string Id { get; set; }            ///             /// 连线开始形状ID            ///             public string StartShapeId { get; set; }            ///             /// 连线结束形状ID            ///             public string EndShapeId { get; set; }      }      ///         /// 当前界面矩形集合      ///         List Shapes = new List();      ///         /// 当前界面连线集合      ///         List Links = new List();      ///         /// 画一个矩形(不同颜色)      ///         ///         ///         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);      }      ///         /// 绘制一条连线      ///         ///         ///         void DrawLine(Graphics g, LineLink line)      {            //通过连线的开始形状ID和结束形状ID,计算两个形状的中心点坐标            var startPoint = GetCentertPoint(line.StartShapeId);            var endPoint = GetCentertPoint(line.EndShapeId);            //绘制一条直线            g.DrawLine(Pens.Black, startPoint, endPoint);      }      ///         /// 绘制一条连线(不同颜色)      ///         ///         ///         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);      }      ///         /// 重新绘制当前所有矩形和连线      ///         ///         void DrawAll(Graphics g)      {            g.Clear(panel1.BackColor) ;            //绘制所有形状            foreach (var sp in Shapes)            {                DrawShape2(g, sp);            }            //绘制所有连线            foreach (var ln in Links)            {                DrawLine2(g, ln);            }      }      ///         /// 当前是否有鼠标按下,且有矩形被选中      ///         bool _isMouseDown = false;      ///         /// 最后一次鼠标的位置      ///         Point _lastMouseLocation = Point.Empty;      ///         /// 当前被鼠标选中的矩形      ///         RectShape _selectedShape = null;      ///         /// 添加连线时选中的第一个形状      ///         RectShape _selectedStartShape = null;      ///         /// 添加连线时选中的第二个形状      ///         RectShape _selectedEndShape = null;      ///         /// 是否正添加连线      ///         bool _isAddLink = false;      ///         /// 获取不同的背景颜色      ///         ///         ///         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;            }      }      ///         /// 获取不同的画笔颜色      ///         ///         ///         Pen GetPen(int i)      {            return new Pen(GetBrush(i), 2);      }      ///         /// 根据形状ID获取形状的中心点,以作为连线的起点或终点      ///         ///         ///         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());      }    }}六、结语

本篇教程我们从零实现并完善了在任意两个矩形中添加矩形的功能,本章都是围绕这个核心功能进行讲解,扩展内容很少,学习起来也很容易。
下章我们会讲一下如何添加其它形状,如圆形,以及如何在矩形和圆形之间添加连线。后面还会讲一下如何优化显示效果,及拖动时闪烁的问题。敬请期待。
感谢大家的观看,本人水平有限,文章不足之处欢迎大家评论指正。
--

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: [原创]《C#高级GDI+实战:从零开发一个流程图》第03章:画一个线,连接两个矩形!