[原創(chuàng)]《C#高級GDI+實(shí)戰(zhàn):從零開發(fā)一個(gè)流程圖》第05章:有鋸齒?拖動(dòng)閃爍?優(yōu)化!優(yōu)化!
一、前言
前面的課程我們實(shí)現(xiàn)了矩形、圓形的拖動(dòng),以及不同形狀間的連線,在實(shí)現(xiàn)的過程中,很多讀者都發(fā)現(xiàn)并提出來了存在顯示質(zhì)量差有鋸齒、拖動(dòng)不流暢還閃爍等問題,作為承上啟下的一節(jié)課程,我們本節(jié)就來看一上如何解決這些問題。
相信看完的你,一定會有所收獲!
本文地址:http://www.rzrgm.cn/lesliexin/p/18930941
二、先看效果
照例我們先來看一下實(shí)現(xiàn)效果:
第1個(gè)視頻我們只需要看最后一部分,這部分對照了優(yōu)化后的連線和形狀顯示質(zhì)量:
第2個(gè)視頻我們通過對照,看到了優(yōu)化前后拖動(dòng)流暢度及閃爍問題:
我們下面就來依次講解。
三、實(shí)現(xiàn)效果1:優(yōu)化鋸齒等顯示質(zhì)量
這部分其實(shí)是很簡單的,只需要設(shè)置GDI+的一些顯示屬性就行了,我們這里為了對照,直接將這些屬性都設(shè)置為最高:

這些屬性設(shè)置后,就實(shí)現(xiàn)了效果1,不再有鋸齒之類的問題了,是不是很簡單。
下面是完整代碼,大家可以自行編譯嘗試:
點(diǎn)擊查看代碼
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 FormDemo03V3 : FormBase
{
public FormDemo03V3()
{
InitializeComponent();
DemoTitle = "第05節(jié)隨課Demo Part3";
DemoNote = "效果:優(yōu)化顯示質(zhì)量,平滑圓形鋸齒、連線鋸齒等";
}
/// <summary>
/// 矩形定義
/// </summary>
public class RectShape
{
/// <summary>
/// 矩形ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 矩形位置和尺寸
/// </summary>
public Rectangle Rect { get; set; }
}
/// <summary>
/// 圓形定義
/// </summary>
public class EllipseShape
{
/// <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>
/// 開始形狀是否是矩形
/// </summary>
public bool StartShapeIsRect { get; set; }
/// <summary>
/// 結(jié)束開關(guān)是否是矩形
/// </summary>
public bool EndShapeIsRect { get; set; }
/// <summary>
/// 連線開始形狀I(lǐng)D
/// </summary>
public string StartShapeId { get; set; }
/// <summary>
/// 連線結(jié)束形狀I(lǐng)D
/// </summary>
public string EndShapeId { get; set; }
}
/// <summary>
/// 矩形集合
/// </summary>
List<RectShape> RectShapes = new List<RectShape>();
/// <summary>
/// 圓形集合
/// </summary>
List<EllipseShape> EllipseShapes = new List<EllipseShape>();
/// <summary>
/// 當(dāng)前界面連線集合
/// </summary>
List<LineLink> Links = new List<LineLink>();
/// <summary>
/// 畫一個(gè)矩形(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="shape"></param>
void DrawRectShape2(Graphics g, RectShape shape)
{
var index = RectShapes.FindIndex(a => a.Id == shape.Id);
g.FillRectangle(GetBrush(index), shape.Rect);
g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);
}
/// <summary>
/// 畫一個(gè)圓形(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="shape"></param>
void DrawEllipseShape2(Graphics g, EllipseShape shape)
{
var index = EllipseShapes.FindIndex(a => a.Id == shape.Id);
g.FillEllipse(GetBrush(index), shape.Rect);
g.DrawString(shape.Id, Font, Brushes.White, shape.Rect.X+20,shape.Rect.Y+20); //注:這里可以講一下,要+20,是顯示文本
}
/// <summary>
/// 繪制一條連線(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="line"></param>
void DrawLine2(Graphics g, LineLink line)
{
//通過連線的開始形狀I(lǐng)D和結(jié)束形狀I(lǐng)D,計(jì)算兩個(gè)形狀的中心點(diǎn)坐標(biāo)
var startPoint = line.StartShapeIsRect? GetCentertPointRect(line.StartShapeId): GetCentertPointEllipse(line.StartShapeId);
var endPoint =line.EndShapeIsRect? GetCentertPointRect(line.EndShapeId) : GetCentertPointEllipse(line.EndShapeId);
var index = Links.FindIndex(a => a.Id == line.Id);
//繪制一條直線
g.DrawLine(GetPen(index), startPoint, endPoint);
}
/// <summary>
/// 重新繪制當(dāng)前所有矩形和連線
/// </summary>
/// <param name="g"></param>
void DrawAll(Graphics g)
{
//設(shè)置顯示質(zhì)量
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.Clear(panel1.BackColor);
//繪制所有矩形
foreach (var sp in RectShapes)
{
DrawRectShape2(g, sp);
}
//繪制所有圓形
foreach (var sp in EllipseShapes)
{
DrawEllipseShape2(g, sp);
}
//繪制所有連線
foreach (var ln in Links)
{
DrawLine2(g, ln);
}
}
//注:文章中說明;此處不過于抽象,后續(xù)章節(jié)會有
/// <summary>
/// 當(dāng)前是否有鼠標(biāo)按下,且有矩形被選中
/// </summary>
bool _isMouseDown = false;
/// <summary>
/// 是否是矩形被選中,不是則是圓形
/// </summary>
bool _isRectMouseDown = true;
/// <summary>
/// 最后一次鼠標(biāo)的位置
/// </summary>
Point _lastMouseLocation = Point.Empty;
/// <summary>
/// 當(dāng)前被鼠標(biāo)選中的矩形
/// </summary>
RectShape _selectedRectShape = null;
/// <summary>
/// 當(dāng)前被鼠標(biāo)選中的圓形
/// </summary>
EllipseShape _selectedEllipseShape = null;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)是否是矩形,不是則是圓形
/// </summary>
bool _selectedStartIsRect = true;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)矩形
/// </summary>
RectShape _selectedStartRectShape = null;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)圓形
/// </summary>
EllipseShape _selectedStartEllipseShape = null;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)是否是矩形,不是則是圓形
/// </summary>
bool _selectedEndIsRect = true;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)矩形
/// </summary>
RectShape _selectedEndRectShape = null;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)圓形
/// </summary>
EllipseShape _selectedEndEllipseShape = 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>
/// 根據(jù)形狀I(lǐng)D獲取形狀的中心點(diǎn),以作為連線的起點(diǎn)或終點(diǎn)
/// </summary>
/// <param name="shapeId"></param>
/// <returns></returns>
Point GetCentertPointRect(string shapeId)
{
var sp = RectShapes.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;
}
/// <summary>
/// 根據(jù)形狀I(lǐng)D獲取形狀的中心點(diǎn),以作為連線的起點(diǎn)或終點(diǎn)
/// </summary>
/// <param name="shapeId"></param>
/// <returns></returns>
Point GetCentertPointEllipse(string shapeId)
{
var sp = EllipseShapes.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 = "矩形" + (RectShapes.Count + 1),
Rect = new Rectangle()
{
X = 50,
Y = 50,
Width = 100,
Height = 100,
},
};
RectShapes.Add(rs);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)按下時(shí)
//取最上方的矩形,也就是最后添加的矩形
var sp = RectShapes.FindLast(a => a.Rect.Contains(e.Location));
//取最上方的圓形,也就是最后添加的圓形
var ep = EllipseShapes.FindLast(a => a.Rect.Contains(e.Location));
if (!_isAddLink)
{
//注:說明,這里是簡化情況,因?yàn)槭莾蓚€(gè)LIST,無法判斷序號,就先判斷矩形
//當(dāng)前沒有處理連線狀態(tài)
if (sp != null)
{
//設(shè)置狀態(tài)及選中矩形
_isMouseDown = true;
_lastMouseLocation = e.Location;
_selectedRectShape = sp;
_selectedEllipseShape = null;
_isRectMouseDown = true;
}
else if (ep != null)
{
//設(shè)置狀態(tài)及選中圓形
_isMouseDown = true;
_lastMouseLocation = e.Location;
_selectedRectShape = null;
_selectedEllipseShape = ep;
_isRectMouseDown = false;
}
}
else
{
//正在添加連線
if (_selectedStartRectShape == null && _selectedStartEllipseShape == null)
{
//證明沒有矩形和圓形被選中則設(shè)置開始形狀
if (sp != null)
{
//設(shè)置開始形狀是矩形
_selectedStartRectShape = sp;
_selectedStartEllipseShape = null;
_selectedStartIsRect = true;
}
else if (ep != null)
{
//設(shè)置開始形狀是圓形
_selectedStartRectShape = null;
_selectedStartEllipseShape = ep;
_selectedStartIsRect = false;
}
toolStripStatusLabel1.Text = "請點(diǎn)擊第2個(gè)形狀";
}
else
{
//判斷第2個(gè)形狀是否是第1個(gè)形狀
if (sp != null)
{
//證明當(dāng)前選中的是矩形
if (_selectedStartRectShape != null)
{
//證明第1步選中的矩形
//判斷當(dāng)前選中的矩形是否是第1步選中的矩形
if (_selectedStartRectShape.Id == sp.Id)
{
toolStripStatusLabel1.Text = "不可選擇同一個(gè)形狀,請重新點(diǎn)擊第2個(gè)形狀";
return;
}
}
}
else if (ep != null)
{
//證明當(dāng)前選中的圓形
if (_selectedStartEllipseShape != null)
{
//證明第1步選中的矩形
//判斷當(dāng)前選中的矩形是否是第1步選中的矩形
if (_selectedStartEllipseShape.Id == ep.Id)
{
toolStripStatusLabel1.Text = "不可選擇同一個(gè)形狀,請重新點(diǎn)擊第2個(gè)形狀";
return;
}
}
}
//注:文章中說明:因?yàn)樘^復(fù)雜,且不是本節(jié)重點(diǎn),但不再進(jìn)行去重判斷
if (sp != null)
{
//設(shè)置結(jié)束形狀是矩形
_selectedEndRectShape = sp;
_selectedEndEllipseShape = null;
_selectedEndIsRect = true;
}
else if (ep != null)
{
//設(shè)置結(jié)束形狀是圓形
_selectedEndRectShape = null;
_selectedEndEllipseShape = ep;
_selectedEndIsRect = false;
}
else
{
return;
}
//兩個(gè)形狀都設(shè)置了,便添加一條新連線
Links.Add(new LineLink()
{
Id = "連線" + (Links.Count + 1),
StartShapeId =_selectedStartIsRect? _selectedStartRectShape.Id:_selectedStartEllipseShape.Id,
EndShapeId =_selectedEndIsRect? _selectedEndRectShape.Id:_selectedEndEllipseShape.Id,
StartShapeIsRect=_selectedStartIsRect,
EndShapeIsRect=_selectedEndIsRect,
});
//兩個(gè)形狀都已選擇,結(jié)束添加連線狀態(tài)
_isAddLink = false;
toolStripStatusLabel1.Text = "";
//重繪以顯示連線
DrawAll(panel1.CreateGraphics());
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)移動(dòng)時(shí)
//如果處于添加連線時(shí),則不移動(dòng)形狀
if (_isAddLink) return;
if (_isMouseDown)
{
//當(dāng)且僅當(dāng):有鼠標(biāo)按下且有矩形被選中時(shí),才進(jìn)行后續(xù)操作
//改變選中矩形的位置信息,隨著鼠標(biāo)移動(dòng)而移動(dòng)
//計(jì)算鼠標(biāo)位置變化信息
var moveX = e.Location.X - _lastMouseLocation.X;
var moveY = e.Location.Y - _lastMouseLocation.Y;
//將選中形狀的位置進(jìn)行同樣的變化
if (_isRectMouseDown)
{
var oldXY = _selectedRectShape.Rect.Location;
oldXY.Offset(moveX, moveY);
_selectedRectShape.Rect = new Rectangle(oldXY, _selectedRectShape.Rect.Size);
}
else
{
var oldXY = _selectedEllipseShape.Rect.Location;
oldXY.Offset(moveX, moveY);
_selectedEllipseShape.Rect = new Rectangle(oldXY, _selectedEllipseShape.Rect.Size);
}
//記錄當(dāng)前鼠標(biāo)位置
_lastMouseLocation.Offset(moveX, moveY);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)松開時(shí)
if (_isMouseDown)
{
//當(dāng)且僅當(dāng):有鼠標(biāo)按下且有矩形被選中時(shí),才進(jìn)行后續(xù)操作
//重置相關(guān)記錄信息
_isMouseDown = false;
_lastMouseLocation = Point.Empty;
_selectedRectShape = null;
_selectedEllipseShape = null;
}
}
private void toolStripButton2_Click(object sender, EventArgs e)
{
_isAddLink = true;
_selectedStartRectShape = null;
_selectedEndRectShape = null;
_selectedStartEllipseShape = null;
_selectedEndEllipseShape = null;
toolStripStatusLabel1.Text = "請點(diǎn)擊第1個(gè)形狀";
}
private void toolStripButton3_Click(object sender, EventArgs e)
{
_isAddLink = false;
_selectedStartRectShape = null;
_selectedEndRectShape = null;
toolStripStatusLabel1.Text = "";
DrawAll(panel1.CreateGraphics());
}
private void toolStripButton4_Click(object sender, EventArgs e)
{
var rs = new EllipseShape()
{
Id = "圓形" + (EllipseShapes.Count + 1),
Rect = new Rectangle()
{
X = 50,
Y = 50,
Width = 100,
Height = 100,
},
};
EllipseShapes.Add(rs);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
}
}
四、實(shí)現(xiàn)效果2:解決拖動(dòng)慢、閃爍等問題
本來未設(shè)置顯示質(zhì)量時(shí),拖動(dòng)形狀時(shí)就會閃爍,而經(jīng)過上面的設(shè)置后,拖動(dòng)變慢了,閃爍更嚴(yán)重了。
閃爍是因?yàn)槲覀兠看瓮蟿?dòng)都是整個(gè)控件清空再依次繪制所有形狀和連線,這些耗時(shí)就會導(dǎo)致閃爍問題。而在設(shè)置為高質(zhì)量顯示后,繪制更慢,閃爍便會更明顯,拖動(dòng)起來也感覺不跟手變慢了。
我們下面就來解決一下這個(gè)問題。
注:更詳細(xì)深入的圖文講解在這個(gè)教程里有說明,不再贅述:http://www.rzrgm.cn/lesliexin/p/16554752.html
1,開啟雙緩沖
可以說遇事不決,先開雙緩沖,如圖:

當(dāng)然,僅僅開啟雙緩沖效果并不大,還要搭配下面的處理才行。
2,使用內(nèi)存繪圖
內(nèi)存繪圖,沒什么神秘的,就是將所有形狀、連線繪制在一個(gè)位圖(Bitmap)對象上而已,當(dāng)繪制完成后,再將此位圖繪制到控件上,以提高繪制效率,達(dá)到去除閃爍的問題。

如上所示,我們先創(chuàng)建一個(gè)與控件尺寸一樣大的位圖對象,然后在此位圖上繪制所有連線,最后繪制到控件上。
好了,到此我們就實(shí)現(xiàn)了效果2,基本上就不會有拖動(dòng)慢和拖動(dòng)時(shí)閃爍的問題了。
下面是完整代碼,大家可以自行編譯嘗試:
點(diǎn)擊查看代碼
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 FormDemo04V1 : FormBase
{
public FormDemo04V1()
{
InitializeComponent();
DemoTitle = "第06節(jié)隨課Demo Part1";
DemoNote = "效果:優(yōu)化拖動(dòng)慢、拖動(dòng)時(shí)閃爍等問題";
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer,true);
}
/// <summary>
/// 矩形定義
/// </summary>
public class RectShape
{
/// <summary>
/// 矩形ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 矩形位置和尺寸
/// </summary>
public Rectangle Rect { get; set; }
}
/// <summary>
/// 圓形定義
/// </summary>
public class EllipseShape
{
/// <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>
/// 開始形狀是否是矩形
/// </summary>
public bool StartShapeIsRect { get; set; }
/// <summary>
/// 結(jié)束開關(guān)是否是矩形
/// </summary>
public bool EndShapeIsRect { get; set; }
/// <summary>
/// 連線開始形狀I(lǐng)D
/// </summary>
public string StartShapeId { get; set; }
/// <summary>
/// 連線結(jié)束形狀I(lǐng)D
/// </summary>
public string EndShapeId { get; set; }
}
/// <summary>
/// 矩形集合
/// </summary>
List<RectShape> RectShapes = new List<RectShape>();
/// <summary>
/// 圓形集合
/// </summary>
List<EllipseShape> EllipseShapes = new List<EllipseShape>();
/// <summary>
/// 當(dāng)前界面連線集合
/// </summary>
List<LineLink> Links = new List<LineLink>();
/// <summary>
/// 畫一個(gè)矩形(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="shape"></param>
void DrawRectShape2(Graphics g, RectShape shape)
{
var index = RectShapes.FindIndex(a => a.Id == shape.Id);
g.FillRectangle(GetBrush(index), shape.Rect);
g.DrawString(shape.Id, Font, Brushes.White, shape.Rect);
}
/// <summary>
/// 畫一個(gè)圓形(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="shape"></param>
void DrawEllipseShape2(Graphics g, EllipseShape shape)
{
var index = EllipseShapes.FindIndex(a => a.Id == shape.Id);
g.FillEllipse(GetBrush(index), shape.Rect);
g.DrawString(shape.Id, Font, Brushes.White, shape.Rect.X+20,shape.Rect.Y+20); //注:這里可以講一下,要+20,是顯示文本
}
/// <summary>
/// 繪制一條連線(不同顏色)
/// </summary>
/// <param name="g"></param>
/// <param name="line"></param>
void DrawLine2(Graphics g, LineLink line)
{
//通過連線的開始形狀I(lǐng)D和結(jié)束形狀I(lǐng)D,計(jì)算兩個(gè)形狀的中心點(diǎn)坐標(biāo)
var startPoint = line.StartShapeIsRect? GetCentertPointRect(line.StartShapeId): GetCentertPointEllipse(line.StartShapeId);
var endPoint =line.EndShapeIsRect? GetCentertPointRect(line.EndShapeId) : GetCentertPointEllipse(line.EndShapeId);
var index = Links.FindIndex(a => a.Id == line.Id);
//繪制一條直線
g.DrawLine(GetPen(index), startPoint, endPoint);
}
//注:文章中說明,參見下面的地址,講的很清楚。
Bitmap _bmp;
/// <summary>
/// 重新繪制當(dāng)前所有矩形和連線
/// </summary>
/// <param name="g"></param>
void DrawAll(Graphics g1)
{
//創(chuàng)建內(nèi)存繪圖,將形狀和連線繪制到此內(nèi)存繪圖上,然后再一次性繪制到控件上
_bmp = new Bitmap(panel1.Width, panel1.Height);
var g = Graphics.FromImage(_bmp);
//設(shè)置顯示質(zhì)量
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.Clear(panel1.BackColor);
//繪制所有矩形
foreach (var sp in RectShapes)
{
DrawRectShape2(g, sp);
}
//繪制所有圓形
foreach (var sp in EllipseShapes)
{
DrawEllipseShape2(g, sp);
}
//繪制所有連線
foreach (var ln in Links)
{
DrawLine2(g, ln);
}
//繪制內(nèi)存繪圖到控件上
g1.DrawImage(_bmp, new PointF(0, 0));
}
//注:文章中說明;此處不過于抽象,后續(xù)章節(jié)會有
/// <summary>
/// 當(dāng)前是否有鼠標(biāo)按下,且有矩形被選中
/// </summary>
bool _isMouseDown = false;
/// <summary>
/// 是否是矩形被選中,不是則是圓形
/// </summary>
bool _isRectMouseDown = true;
/// <summary>
/// 最后一次鼠標(biāo)的位置
/// </summary>
Point _lastMouseLocation = Point.Empty;
/// <summary>
/// 當(dāng)前被鼠標(biāo)選中的矩形
/// </summary>
RectShape _selectedRectShape = null;
/// <summary>
/// 當(dāng)前被鼠標(biāo)選中的圓形
/// </summary>
EllipseShape _selectedEllipseShape = null;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)是否是矩形,不是則是圓形
/// </summary>
bool _selectedStartIsRect = true;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)矩形
/// </summary>
RectShape _selectedStartRectShape = null;
/// <summary>
/// 添加連線時(shí)選中的第一個(gè)圓形
/// </summary>
EllipseShape _selectedStartEllipseShape = null;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)是否是矩形,不是則是圓形
/// </summary>
bool _selectedEndIsRect = true;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)矩形
/// </summary>
RectShape _selectedEndRectShape = null;
/// <summary>
/// 添加連線時(shí)選中的第二個(gè)圓形
/// </summary>
EllipseShape _selectedEndEllipseShape = 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>
/// 根據(jù)形狀I(lǐng)D獲取形狀的中心點(diǎn),以作為連線的起點(diǎn)或終點(diǎn)
/// </summary>
/// <param name="shapeId"></param>
/// <returns></returns>
Point GetCentertPointRect(string shapeId)
{
var sp = RectShapes.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;
}
/// <summary>
/// 根據(jù)形狀I(lǐng)D獲取形狀的中心點(diǎn),以作為連線的起點(diǎn)或終點(diǎn)
/// </summary>
/// <param name="shapeId"></param>
/// <returns></returns>
Point GetCentertPointEllipse(string shapeId)
{
var sp = EllipseShapes.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 = "矩形" + (RectShapes.Count + 1),
Rect = new Rectangle()
{
X = 50,
Y = 50,
Width = 100,
Height = 100,
},
};
RectShapes.Add(rs);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)按下時(shí)
//取最上方的矩形,也就是最后添加的矩形
var sp = RectShapes.FindLast(a => a.Rect.Contains(e.Location));
//取最上方的圓形,也就是最后添加的圓形
var ep = EllipseShapes.FindLast(a => a.Rect.Contains(e.Location));
if (!_isAddLink)
{
//注:說明,這里是簡化情況,因?yàn)槭莾蓚€(gè)LIST,無法判斷序號,就先判斷矩形
//當(dāng)前沒有處理連線狀態(tài)
if (sp != null)
{
//設(shè)置狀態(tài)及選中矩形
_isMouseDown = true;
_lastMouseLocation = e.Location;
_selectedRectShape = sp;
_selectedEllipseShape = null;
_isRectMouseDown = true;
}
else if (ep != null)
{
//設(shè)置狀態(tài)及選中圓形
_isMouseDown = true;
_lastMouseLocation = e.Location;
_selectedRectShape = null;
_selectedEllipseShape = ep;
_isRectMouseDown = false;
}
}
else
{
//正在添加連線
if (_selectedStartRectShape == null && _selectedStartEllipseShape == null)
{
//證明沒有矩形和圓形被選中則設(shè)置開始形狀
if (sp != null)
{
//設(shè)置開始形狀是矩形
_selectedStartRectShape = sp;
_selectedStartEllipseShape = null;
_selectedStartIsRect = true;
}
else if (ep != null)
{
//設(shè)置開始形狀是圓形
_selectedStartRectShape = null;
_selectedStartEllipseShape = ep;
_selectedStartIsRect = false;
}
toolStripStatusLabel1.Text = "請點(diǎn)擊第2個(gè)形狀";
}
else
{
//判斷第2個(gè)形狀是否是第1個(gè)形狀
if (sp != null)
{
//證明當(dāng)前選中的是矩形
if (_selectedStartRectShape != null)
{
//證明第1步選中的矩形
//判斷當(dāng)前選中的矩形是否是第1步選中的矩形
if (_selectedStartRectShape.Id == sp.Id)
{
toolStripStatusLabel1.Text = "不可選擇同一個(gè)形狀,請重新點(diǎn)擊第2個(gè)形狀";
return;
}
}
}
else if (ep != null)
{
//證明當(dāng)前選中的圓形
if (_selectedStartEllipseShape != null)
{
//證明第1步選中的矩形
//判斷當(dāng)前選中的矩形是否是第1步選中的矩形
if (_selectedStartEllipseShape.Id == ep.Id)
{
toolStripStatusLabel1.Text = "不可選擇同一個(gè)形狀,請重新點(diǎn)擊第2個(gè)形狀";
return;
}
}
}
//注:文章中說明:因?yàn)樘^復(fù)雜,且不是本節(jié)重點(diǎn),但不再進(jìn)行去重判斷
if (sp != null)
{
//設(shè)置結(jié)束形狀是矩形
_selectedEndRectShape = sp;
_selectedEndEllipseShape = null;
_selectedEndIsRect = true;
}
else if (ep != null)
{
//設(shè)置結(jié)束形狀是圓形
_selectedEndRectShape = null;
_selectedEndEllipseShape = ep;
_selectedEndIsRect = false;
}
else
{
return;
}
//兩個(gè)形狀都設(shè)置了,便添加一條新連線
Links.Add(new LineLink()
{
Id = "連線" + (Links.Count + 1),
StartShapeId =_selectedStartIsRect? _selectedStartRectShape.Id:_selectedStartEllipseShape.Id,
EndShapeId =_selectedEndIsRect? _selectedEndRectShape.Id:_selectedEndEllipseShape.Id,
StartShapeIsRect=_selectedStartIsRect,
EndShapeIsRect=_selectedEndIsRect,
});
//兩個(gè)形狀都已選擇,結(jié)束添加連線狀態(tài)
_isAddLink = false;
toolStripStatusLabel1.Text = "";
//重繪以顯示連線
DrawAll(panel1.CreateGraphics());
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)移動(dòng)時(shí)
//如果處于添加連線時(shí),則不移動(dòng)形狀
if (_isAddLink) return;
if (_isMouseDown)
{
//當(dāng)且僅當(dāng):有鼠標(biāo)按下且有矩形被選中時(shí),才進(jìn)行后續(xù)操作
//改變選中矩形的位置信息,隨著鼠標(biāo)移動(dòng)而移動(dòng)
//計(jì)算鼠標(biāo)位置變化信息
var moveX = e.Location.X - _lastMouseLocation.X;
var moveY = e.Location.Y - _lastMouseLocation.Y;
//將選中形狀的位置進(jìn)行同樣的變化
if (_isRectMouseDown)
{
var oldXY = _selectedRectShape.Rect.Location;
oldXY.Offset(moveX, moveY);
_selectedRectShape.Rect = new Rectangle(oldXY, _selectedRectShape.Rect.Size);
}
else
{
var oldXY = _selectedEllipseShape.Rect.Location;
oldXY.Offset(moveX, moveY);
_selectedEllipseShape.Rect = new Rectangle(oldXY, _selectedEllipseShape.Rect.Size);
}
//記錄當(dāng)前鼠標(biāo)位置
_lastMouseLocation.Offset(moveX, moveY);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
//當(dāng)鼠標(biāo)松開時(shí)
if (_isMouseDown)
{
//當(dāng)且僅當(dāng):有鼠標(biāo)按下且有矩形被選中時(shí),才進(jìn)行后續(xù)操作
//重置相關(guān)記錄信息
_isMouseDown = false;
_lastMouseLocation = Point.Empty;
_selectedRectShape = null;
_selectedEllipseShape = null;
}
}
private void toolStripButton2_Click(object sender, EventArgs e)
{
_isAddLink = true;
_selectedStartRectShape = null;
_selectedEndRectShape = null;
_selectedStartEllipseShape = null;
_selectedEndEllipseShape = null;
toolStripStatusLabel1.Text = "請點(diǎn)擊第1個(gè)形狀";
}
private void toolStripButton3_Click(object sender, EventArgs e)
{
_isAddLink = false;
_selectedStartRectShape = null;
_selectedEndRectShape = null;
toolStripStatusLabel1.Text = "";
DrawAll(panel1.CreateGraphics());
}
private void toolStripButton4_Click(object sender, EventArgs e)
{
var rs = new EllipseShape()
{
Id = "圓形" + (EllipseShapes.Count + 1),
Rect = new Rectangle()
{
X = 50,
Y = 50,
Width = 100,
Height = 100,
},
};
EllipseShapes.Add(rs);
//重繪所有矩形
DrawAll(panel1.CreateGraphics());
}
}
}
五、結(jié)語
本節(jié)課程很簡單,是基于當(dāng)前為止的需要,簡單的解決了顯示質(zhì)量和拖動(dòng)閃爍的問題,當(dāng)然上面的代碼并不非常完善,像資源釋放、全屏顯示時(shí)仍可能出現(xiàn)拖動(dòng)不跟手等問題都沒作處理,這些不是本節(jié)重點(diǎn),我們會在后面的課程里一一解決。
下節(jié)課程我們就來對矩形、圓形等形狀和連線做一個(gè)抽象,以解決前面課程遇到的增加一個(gè)新的形狀時(shí)代碼就要添加好多額外判斷代碼的問題,以及為后續(xù)的支持菱形、平行四邊形等任意形狀做基礎(chǔ)。當(dāng)然連線也是,后面不止有直線,還有貝塞爾曲線、正交連線等各種連線。
感謝大家的觀看,本人水平有限,文章不足之處歡迎大家評論指正。
-[END]-

浙公網(wǎng)安備 33010602011771號