[原創]《C#高級GDI+實戰:從零開發一個流程圖》第03章:畫一個線,連接兩個矩形!
一、前言
上一節我們實現了多個不同顏色的可拖動的矩形,那么這一節就來看一下如何將這些矩形連起來吧。
相信看完的你,一定會有所收獲!
本文地址:http://www.rzrgm.cn/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]-

浙公網安備 33010602011771號