[原(yuan)創]《C#高級GDI+實戰(zhan):從零開發(fa)一個流程圖》第08章:增加菱形(xing)(xing)、平(ping)行四邊形(xing)(xing)、圓角(jiao)矩(ju)形(xing)(xing),文本居中顯示
一、前言
前面的(de)(de)課(ke)程我們(men)已經完成了形(xing)狀和連線的(de)(de)抽象,并(bing)獨(du)立出了畫布控件(jian),基礎(chu)已經打(da)好,下面就要添磚加瓦了。我們(men)本(ben)(ben)節課(ke)程就來(lai)添加一些(xie)不同的(de)(de)形(xing)狀,如:菱(ling)形(xing)、平行四(si)邊形(xing)、圓(yuan)角(jiao)矩(ju)形(xing)等。而且(qie)我們(men)前面發現形(xing)狀內(nei)的(de)(de)文本(ben)(ben)都(dou)不是居中顯示的(de)(de),我們(men)也順便優化下。
相信看完的你,一定會有所收獲!
本文地址://www.xtjzw.net/lesliexin/p/18997090
二、先看效果
我們可以看到(dao)添加了不同的形狀(zhuang),且都支持拖動、連(lian)線。
看完本篇的代碼,我們會(hui)發現(xian)實現(xian)起來很簡單,只(zhi)需要(yao)給繼承形狀基類實現(xian)下(xia)就行了,主程序只(zhi)是添(tian)加個控件、生成個形狀類,調(diao)用畫布的添(tian)加形狀方法就行了。
所以(yi)本節的課程重點在于如(ru)何去(qu)用GDI+“畫(hua)”出(chu)這(zhe)些形狀。
三、菱形
像菱形及本文中(zhong)的形狀,就沒(mei)有(you)現(xian)在(zai)的GDI+方法來實現(xian)了(le),只(zhi)能(neng)通過各個子方法來組合繪制(zhi)出想要的形狀。
我(wo)們先看下圖的菱形圖示(shi):

我(wo)們的(de)所做(zuo)的(de)就是依次(ci)繪制菱形的(de)四(si)(si)個邊,這(zhe)四(si)(si)個頂點的(de)坐標怎么(me)來的(de)呢?
我們在前面的抽象出形狀基類那節講過,屬性Rect是指示形狀所在的矩形區域,所以我們就要在這個矩形區域內,指定4個頂點并計算出坐標。
當有了坐標后,我可以使用(yong)GDI+的(de)AddPolygon方法來將多個坐標點添加成一個多邊形(xing),其MSDN的(de)解(jie)釋(shi)如下:

最后(hou)使用GDI+的FillPath將此多邊形繪(hui)制出來(lai),具體的代碼如下:

四、平行四邊形
同菱形,我(wo)們(men)也是使用類似的(de)方(fang)法求出四個頂點的(de)坐標(biao),這里我(wo)們(men)將傾斜(xie)距離設置(zhi)為1/5的(de)寬度:

代(dai)碼定義里直接按(an)圖(tu)示取值即可(ke):

五、圓角矩形
圓角矩形(xing)就和上面的兩個形(xing)狀不一樣(yang)了,因為不再是由直線組成,而是要有弧度:

這里(li)要使用(yong)一(yi)個新的(de)GDI+方(fang)法:AddArc,添(tian)加(jia)一(yi)段弧(hu)線,其(qi)MSDN的(de)解(jie)釋如下:

注意(yi)看最下面那段話:
如果圖(tu)中有上(shang)一(yi)條直線或曲線,則會添加一(yi)條線,用于將(jiang)上(shang)一(yi)段的(de)端點(dian)連接到弧(hu)線的(de)開(kai)頭。
所以我們并不(bu)需要添(tian)(tian)加4條(tiao)直(zhi)線(xian)4個弧(hu)(hu)線(xian),只(zhi)需要添(tian)(tian)加4個弧(hu)(hu)線(xian)就(jiu)行了(le),我們暫時將弧(hu)(hu)線(xian)所在圓的直(zhi)徑固(gu)定為20。
其中:
1,左上角

2,右上角

3,右下角

4,左下角

我們(men)參(can)照上(shang)圖(tu)的坐標及角(jiao)度(du)編寫代(dai)碼即可:

六、文本居中顯示
上面的形狀實現后,我(wo)們會發現文本(ben)(ben)位置都不統(tong)一(yi),我(wo)們下面就來讓文本(ben)(ben)統(tong)一(yi)居(ju)中顯(xian)示。
核心是(shi)使用(yong)GDI+的(de)DrawString的(de)一(yi)個(ge)重載方法(fa):

我們(men)像下面(mian)這樣寫就能讓文本居中顯示:

關于StringFormat的詳細(xi)講解,請參照教程(cheng):
//www.xtjzw.net/lesliexin/p/12879270.html
具體(ti)的代碼(ma)改造如(ru)下(xia),不再贅(zhui)述:
點擊查看代碼
/// <summary>
/// 菱形定義
/// </summary>
public class LozengeShapeV2 : ShapeBase
{
public override void Draw(Graphics g)
{
var x2 = Rect.X;
var y2 = Rect.Y;
var w2 = Rect.Width;
var h2 = Rect.Height;
var x = x2 + w2 / 2;
var y = y2 + h2 / 2;
//左-上-右-下
var r0 = new Point(x2, y);
var r1 = new Point(x, y2);
var r2 = new Point(x2 + w2, y);
var r3 = new Point(x, y2 + h2);
var path = new GraphicsPath();
path.Reset();
path.AddPolygon(new Point[]{ r0,r1,r2,r3});
path.CloseFigure();
g.FillPath(new SolidBrush(BackgroundColor), path);
g.DrawString(Text, TextFont, new SolidBrush(FontColor), Rect,
new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
}
}
/// <summary>
/// 平行四邊形定義
/// </summary>
public class ParallelogramShapeV2 : ShapeBase
{
public override void Draw(Graphics g)
{
var x2 = Rect.X;
var y2 = Rect.Y;
var w2 = Rect.Width;
var h2 = Rect.Height;
var f = w2 / 5;
//左-上-右-下
var r0 = new Point(x2 + f, y2);
var r1 = new Point(x2 + w2, y2);
var r2 = new Point(x2 + w2 - f, y2 + h2);
var r3 = new Point(x2, y2 + h2);
var path = new GraphicsPath();
path.Reset();
path.AddPolygon(new Point[]{ r0,r1,r2,r3});
path.CloseFigure();
g.FillPath(new SolidBrush(BackgroundColor), path);
g.DrawString(Text, TextFont, new SolidBrush(FontColor), Rect,
new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
}
}
/// <summary>
/// 圓角矩形定義
/// </summary>
public class RoundRectShapeV2 : ShapeBase
{
public override void Draw(Graphics g)
{
float diameter = 20;
var path = new GraphicsPath();
path.Reset();
RectangleF arc = new RectangleF(Rect.X, Rect.Y, diameter, diameter);
// 左上角
path.AddArc(arc, 180, 90);
// 右上角
arc.X = Rect.Right - diameter;
path.AddArc(arc, 270, 90);
// 右下角
arc.Y = Rect.Bottom - diameter;
path.AddArc(arc, 0, 90);
// 左下角
arc.X = Rect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
g.FillPath(new SolidBrush(BackgroundColor), path);
g.DrawString(Text, TextFont, new SolidBrush(FontColor), Rect,
new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
}
}
七、添加形狀方法抽象出泛型方法
我(wo)們看(kan)之前添加矩(ju)形和圓(yuan)形的方法:

會發(fa)現很類似,類似是因為矩形(xing)和(he)圓形(xing)都是形(xing)狀基類的(de)實(shi)現,那(nei)么我們(men)本節課(ke)程添加(jia)了(le)這三個新的(de)形(xing)狀,再這樣寫就太繁瑣(suo)了(le),我們(men)直接抽象出一個泛型方法來解(jie)決此(ci)問(wen)題(ti):

可以(yi)看到,就(jiu)是將生成矩形(xing)和圓形(xing)的方(fang)法使用泛型替代(dai)。
我們(men)再(zai)寫一(yi)個泛型方法來將形狀(zhuang)添加到(dao)畫布(bu):

好了(le)(le),到此(ci)我們(men)的代碼就(jiu)進一步簡(jian)化了(le)(le),添(tian)加不(bu)同形狀(zhuang)只(zhi)需要傳入對應(ying)的形狀(zhuang)類型就(jiu)行了(le)(le):

是不是很優雅~
完整代碼如下,大家可自行嘗試:
點擊查看代碼
using Elements;
using Elements.Links;
using Elements.Shapes;
using FlowChartCanvas;
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 FormDemo07V4 : FormBase
{
public FormDemo07V4()
{
InitializeComponent();
DemoTitle = "第09節隨課Demo Part4";
DemoNote = "效果:所有形狀內文本居中顯示。";
//添加畫布控件
_fcc = new FCCanvasV1();
_fcc.FCC_LinkColor += _fcc_FCC_LinkColor;
_fcc.FCC_LinkState += _fcc_FCC_LinkState;
_fcc.Dock = DockStyle.Fill;
panel1.Controls.Add(_fcc);
}
private void _fcc_FCC_LinkState(string obj)
{
toolStripStatusLabel1.Text = obj;
}
private Color _fcc_FCC_LinkColor()
{
return GetColor(_linkColorIndex++);
}
FCCanvasV1 _fcc;
/// <summary>
/// 形狀顏色序號
/// </summary>
int _shapeColorIndex = 0;
/// <summary>
/// 連線顏色序號
/// </summary>
int _linkColorIndex = 0;
/// <summary>
/// 獲取不同的背景顏色
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
Color GetColor(int i)
{
switch (i)
{
case 0: return Color.Red;
case 1: return Color.Green;
case 2: return Color.Blue;
case 3: return Color.Orange;
case 4: return Color.Purple;
default: return Color.Red;
}
}
//注:文章中說明:再次抽象
/// <summary>
/// 創建形狀
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="shapeText"></param>
/// <returns></returns>
T CreateShape<T>(string shapeText) where T:ShapeBase
{
var t = Activator.CreateInstance<T>();
t.Id = shapeText + Guid.NewGuid().ToString();
t.Rect = new Rectangle()
{
X = 50,
Y = 50,
Width = 100,
Height = 100,
};
t.FontColor = Color.White;
t.BackgroundColor = GetColor(_shapeColorIndex++);
t.Text = shapeText + _shapeColorIndex;
t.TextFont = Font;
return t;
}
/// <summary>
/// 創建指定類型的形狀并添加到當前流程圖畫布中。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="shapeText"></param>
void CreateShapeAndAddToFCCanvas<T>(string shapeText) where T : ShapeBase
{
var sp = CreateShape<T>(shapeText);
_fcc.FCC_AddShapes(new List<ShapeBase>() { sp });
_fcc.FCC_Refresh();
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
CreateShapeAndAddToFCCanvas<RectShapeV2>("矩形");
}
private void toolStripButton4_Click(object sender, EventArgs e)
{
CreateShapeAndAddToFCCanvas<EllipseShapeV2>("圓形");
}
private void toolStripButton5_Click(object sender, EventArgs e)
{
CreateShapeAndAddToFCCanvas<LozengeShapeV2>("菱形");
}
private void toolStripButton6_Click(object sender, EventArgs e)
{
CreateShapeAndAddToFCCanvas<ParallelogramShapeV2>("平行四邊形");
}
private void toolStripButton7_Click(object sender, EventArgs e)
{
CreateShapeAndAddToFCCanvas<RoundRectShapeV2>("圓角矩形");
}
private void toolStripButton2_Click(object sender, EventArgs e)
{
_fcc.FCC_StartLink();
}
private void toolStripButton3_Click(object sender, EventArgs e)
{
_fcc.FCC_StopLink();
}
}
}
八、結語
我們本節課添加(jia)了(le)多個不同的(de)(de)(de)(de)形(xing)狀(zhuang),這些形(xing)狀(zhuang)也是流程圖(tu)中常用的(de)(de)(de)(de)形(xing)狀(zhuang),有了(le)這些基礎,用戶可按自己的(de)(de)(de)(de)需求添加(jia)自己的(de)(de)(de)(de)形(xing)狀(zhuang)。當然現在的(de)(de)(de)(de)形(xing)狀(zhuang)屬性還(huan)很少,會(hui)隨著(zhu)課程的(de)(de)(de)(de)深入而(er)豐(feng)富(fu),以支持(chi)更多效果。
我們(men)還(huan)抽象(xiang)出一(yi)泛型(xing)方法來(lai)簡化添(tian)加形狀的操作,使用起來(lai)很是優雅。
現在基本的(de)形狀都有(you)了,我們下節課就來添加新的(de)連線(xian):貝塞爾曲(qu)線(xian),這個幾乎是最常(chang)見的(de)曲(qu)線(xian)。
同(tong)(tong)時,有了新的連(lian)線,我們(men)還(huan)會增(zeng)加(jia)不同(tong)(tong)的連(lian)接(jie)點用來連(lian)線,而不再(zai)只連(lian)接(jie)形狀(zhuang)的中(zhong)心點。
敬請期待。
感謝(xie)大家的觀看,本(ben)人水平有限(xian),文章不足之處歡(huan)迎(ying)大家評論指正(zheng)。
-[END]-
