一、前言
上一节我们实现了多个不同颜色的可拖动的矩形,那么这一节就来看一下如何将这些矩形连起来吧。
相信看完的你,一定会有所收获!
本文地址:https://www.cnblogs.com/lesliexin/p/18923105
二、先看效果
同样的,我们先来看一下本节所实现的效果,先有一个整体的印象。
通过视频我们可以看到,我们本节仍是分为三个步骤依次递进:
在两个矩形中添加一条直线,连接两个形状的中心
在【任意】两个矩形之间添加一条直线
在任意两个矩形之间添加【不同颜色】直线,连线去重,中止添加连线操作
下面我们就来依次讲解。
对了,这里说明一下,从本节课程开始,因为后续的课程都是在前一节课程的基础上实现的,所以原理图中会着重标识出来(一般使用绿色)这些新的点,同时代码实操部分仅会讲解对应的这部分代码。因为之前都已讲过,每次都从零讲篇幅会越来越长而且容易产生混乱。当然了,虽然仅讲重点,但是全部的代码时会贴到每小节最后的,大家可以自行查看和编译。
三、实现效果1:在两个矩形中添加一条直线
我们先来实现最简单的情况:在两个矩形中添加一条直线
(一)原理
原理很简单,在绘制矩形后,取两个矩形的中心点,然后绘制一条直线即可。
原理图如下,注意图中的绿色元素就是与上节课原理图的差异之处。
顺便说一句:本文所有的原理图都是使用《LN流程图》制作的,而这也正是本系列课程最终的实现效果,相当于自产自销了是。
(二)代码实操
1,设计器界面
设计器界面没什么大的变化:
2,添加连线代码
我们看下原理图中的绿色形状,我们要添加一条直线,以连接两个矩形的中心点。
那么首先就是计算两个矩形的中心点坐标到两个全局变量:
之后就在绘制矩形后,添加上绘制连线的方法:
3,鼠标移动事件
我们再来看下原理图中的另一个绿色形状说明,我们要在鼠标移动时,重新计算连线两点的坐标,并进行重绘,所以我们需要在MouseMove事件中添加计算两点坐标的方法。
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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,这个改动就比较多了,我们下面一点点讲解。
(一)原理
同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。
可以看到,增加的节点流程还是挺多的,大家可以尝试先自行读一下原理图,有一个整体的印象,好看下面的实操部分。
(二)代码实操
1,设计器界面
因为要在任意两个矩形之间添加一条直线,所以就需要有一个连线状态控制及提示,所以界面也有了些改动:
2,进入连线状态
我们要在任意两个矩形之间添加直线,肯定要依次选择两个矩形,这就需要有个"连线状态",以标识我们现在点击矩形是为了添加连线,而不是移动矩形什么的。
我们用一个按钮和变量来控制,点击按钮后,设置变量,以标识现在进入了连线状态,并设置提示信息"请点击第1个矩形":
3,鼠标点击事件
我们通过原理图可以看到,变化最大地方就是这个MouseDown事件,在这个事件中,我们要额外判断是否处理连线状态,并判断已经选择的矩形等等控制。
好在逻辑很简单,我们依照原理图一步步写代码即可:
4,鼠标移动事件
在MouseMove事件中,我们通过原理图可以看到,我们首先要判断下是否处于连线状态,防止在选择矩形时矩形发生移动等变化。
5,生成连线
因为我们要在任意两个矩形间生成连线,所以就不能像上一小节中那样简单的定义两个坐标变量。
连线会有很多,所以我们先定义一个连线类:
然后定义一个连线列表的全局变量:
在生成连线,我们需要知道两个矩形ID对应的矩形的中心点,所以我实现根据矩形ID求矩形中心点的方法:
之后就像绘制矩形的方法一样,我们写一个维护连线的方法:
最后,我们在绘制所有矩形和连线的方法中,加上依次绘制所有连线的方法:
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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,这次的效果改动很少,更多的是一些易用性上的优化,我们下面一点点讲解。
(一)原理
同样的,我们先来看原理图,这个原理图是在上面小节原理图上增加了新的节点,并使用绿色进行了着重标注。
可以看到,增加的节点流程就一个,判断是否已经添加过相同的连线。
(二)代码实操
1,设计器界面
本次的界面主要增加了中止连线的按钮。
2,不同颜色的直线
我们先来看最简单的添加不同颜色的直线,这个和上一章不同颜色的矩形实现逻辑是一样的,获取不同颜色的画笔就行了:
同样的,绘制直线方法名我们也增加个2,来作下区分,这里只是用来区分,在实现项目及后续教程中就是直接改原方法了,也是贯彻抽象的思路。
3,连线去重
我们的连线是连接两个矩形的中心点,如果多个连线的开始矩形和结束矩形都一样,是看不出效果的,是没必要的,所以我们这里添加下去重。
注:因为本篇教程连线是连接的两个矩形的中心点,所以有去重的必要。在后续章节,我们连接的就不是矩形的中心了,而是矩形(包括其它形状)的"连接点"了,连接点的概念很好理解,就是日常使用流程图时,连线都是到上下左右边的中间点(当然不止这些),我们后续也会这样实现。
我们参照原理图,在MouseDown事件中,添加去重判断的代码:
3,中止添加连线操作
我们在添加连线时,可能点错了开始矩形,在上面小节中,我们是没办法结束的,只能继续选另一个矩形,所以我们增加上中止添加连线操作。
注:在后续的章节中,我们会讲解如何实现更符合操作逻辑的添加及中止连线方式:点按开始形状的连接点,移动到另一个形状的连接点上,完成连线操作操作。在移动时有虚线箭头提示,且松开鼠标自动取消连线。敬请期待。
实现代码很简单,我们只需要设置连线状态标志为false即可:
好了,到此我们依照原理图对代码改造完毕,也就可以实现视频的效果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]-