[原創(chuàng)]《C#高級(jí)GDI+實(shí)戰(zhàn):從零開發(fā)一個(gè)流程圖》第08章:增加菱形、平行四邊形、圓角矩形,文本居中顯示
一、前言
前面的課程我們已經(jīng)完成了形狀和連線的抽象,并獨(dú)立出了畫布控件,基礎(chǔ)已經(jīng)打好,下面就要添磚加瓦了。我們本節(jié)課程就來添加一些不同的形狀,如:菱形、平行四邊形、圓角矩形等。而且我們前面發(fā)現(xiàn)形狀內(nèi)的文本都不是居中顯示的,我們也順便優(yōu)化下。
相信看完的你,一定會(huì)有所收獲!
本文地址:http://www.rzrgm.cn/lesliexin/p/18997090
二、先看效果
我們可以看到添加了不同的形狀,且都支持拖動(dòng)、連線。
看完本篇的代碼,我們會(huì)發(fā)現(xiàn)實(shí)現(xiàn)起來很簡(jiǎn)單,只需要給繼承形狀基類實(shí)現(xiàn)下就行了,主程序只是添加個(gè)控件、生成個(gè)形狀類,調(diào)用畫布的添加形狀方法就行了。
所以本節(jié)的課程重點(diǎn)在于如何去用GDI+“畫”出這些形狀。
三、菱形
像菱形及本文中的形狀,就沒有現(xiàn)在的GDI+方法來實(shí)現(xiàn)了,只能通過各個(gè)子方法來組合繪制出想要的形狀。
我們先看下圖的菱形圖示:

我們的所做的就是依次繪制菱形的四個(gè)邊,這四個(gè)頂點(diǎn)的坐標(biāo)怎么來的呢?
我們?cè)谇懊娴某橄蟪鲂螤罨惸枪?jié)講過,屬性Rect是指示形狀所在的矩形區(qū)域,所以我們就要在這個(gè)矩形區(qū)域內(nèi),指定4個(gè)頂點(diǎn)并計(jì)算出坐標(biāo)。
當(dāng)有了坐標(biāo)后,我可以使用GDI+的AddPolygon方法來將多個(gè)坐標(biāo)點(diǎn)添加成一個(gè)多邊形,其MSDN的解釋如下:

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

四、平行四邊形
同菱形,我們也是使用類似的方法求出四個(gè)頂點(diǎn)的坐標(biāo),這里我們將傾斜距離設(shè)置為1/5的寬度:

代碼定義里直接按圖示取值即可:

五、圓角矩形
圓角矩形就和上面的兩個(gè)形狀不一樣了,因?yàn)椴辉偈怯芍本€組成,而是要有弧度:

這里要使用一個(gè)新的GDI+方法:AddArc,添加一段弧線,其MSDN的解釋如下:

注意看最下面那段話:
如果圖中有上一條直線或曲線,則會(huì)添加一條線,用于將上一段的端點(diǎn)連接到弧線的開頭。
所以我們并不需要添加4條直線4個(gè)弧線,只需要添加4個(gè)弧線就行了,我們暫時(shí)將弧線所在圓的直徑固定為20。
其中:
1,左上角

2,右上角

3,右下角

4,左下角

我們參照上圖的坐標(biāo)及角度編寫代碼即可:

六、文本居中顯示
上面的形狀實(shí)現(xiàn)后,我們會(huì)發(fā)現(xiàn)文本位置都不統(tǒng)一,我們下面就來讓文本統(tǒng)一居中顯示。
核心是使用GDI+的DrawString的一個(gè)重載方法:

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

關(guān)于StringFormat的詳細(xì)講解,請(qǐng)參照教程:
C# StringFormat詳解之文本方向、對(duì)齊
http://www.rzrgm.cn/lesliexin/p/12879270.html
具體的代碼改造如下,不再贅述:
點(diǎn)擊查看代碼
/// <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 });
}
}
七、添加形狀方法抽象出泛型方法
我們看之前添加矩形和圓形的方法:

會(huì)發(fā)現(xiàn)很類似,類似是因?yàn)榫匦魏蛨A形都是形狀基類的實(shí)現(xiàn),那么我們本節(jié)課程添加了這三個(gè)新的形狀,再這樣寫就太繁瑣了,我們直接抽象出一個(gè)泛型方法來解決此問題:

可以看到,就是將生成矩形和圓形的方法使用泛型替代。
我們?cè)賹懸粋€(gè)泛型方法來將形狀添加到畫布:

好了,到此我們的代碼就進(jìn)一步簡(jiǎn)化了,添加不同形狀只需要傳入對(duì)應(yīng)的形狀類型就行了:

是不是很優(yōu)雅~
完整代碼如下,大家可自行嘗試:
點(diǎn)擊查看代碼
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節(jié)隨課Demo Part4";
DemoNote = "效果:所有形狀內(nèi)文本居中顯示。";
//添加畫布控件
_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>
/// 形狀顏色序號(hào)
/// </summary>
int _shapeColorIndex = 0;
/// <summary>
/// 連線顏色序號(hào)
/// </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>
/// 創(chuàng)建形狀
/// </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>
/// 創(chuàng)建指定類型的形狀并添加到當(dāng)前流程圖畫布中。
/// </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();
}
}
}
八、結(jié)語
我們本節(jié)課添加了多個(gè)不同的形狀,這些形狀也是流程圖中常用的形狀,有了這些基礎(chǔ),用戶可按自己的需求添加自己的形狀。當(dāng)然現(xiàn)在的形狀屬性還很少,會(huì)隨著課程的深入而豐富,以支持更多效果。
我們還抽象出一泛型方法來簡(jiǎn)化添加形狀的操作,使用起來很是優(yōu)雅。
現(xiàn)在基本的形狀都有了,我們下節(jié)課就來添加新的連線:貝塞爾曲線,這個(gè)幾乎是最常見的曲線。
同時(shí),有了新的連線,我們還會(huì)增加不同的連接點(diǎn)用來連線,而不再只連接形狀的中心點(diǎn)。
敬請(qǐng)期待。
感謝大家的觀看,本人水平有限,文章不足之處歡迎大家評(píng)論指正。
-[END]-

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