[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

一、前言

前面的课程我们已经完成了形状和连线的抽象,并独立出了画布控件,基础已经打好,下面就要添砖加瓦了。我们本节课程就来添加一些不同的形状,如:菱形、平行四边形、圆角矩形等。而且我们前面发现形状内的文本都不是居中显示的,我们也顺便优化下。

相信看完的你,一定会有所收获!

本文地址:https://www.cnblogs.com/lesliexin/p/18997090

二、先看效果

我们可以看到添加了不同的形状,且都支持拖动、连线。

看完本篇的代码,我们会发现实现起来很简单,只需要给继承形状基类实现下就行了,主程序只是添加个控件、生成个形状类,调用画布的添加形状方法就行了。

所以本节的课程重点在于如何去用GDI+“画”出这些形状。

三、菱形

像菱形及本文中的形状,就没有现在的GDI+方法来实现了,只能通过各个子方法来组合绘制出想要的形状。

我们先看下图的菱形图示:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

我们的所做的就是依次绘制菱形的四个边,这四个顶点的坐标怎么来的呢?

我们在前面的抽象出形状基类那节讲过,属性Rect是指示形状所在的矩形区域,所以我们就要在这个矩形区域内,指定4个顶点并计算出坐标。

当有了坐标后,我可以使用GDI+的AddPolygon方法来将多个坐标点添加成一个多边形,其MSDN的解释如下:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

最后使用GDI+的FillPath将此多边形绘制出来,具体的代码如下:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

四、平行四边形

同菱形,我们也是使用类似的方法求出四个顶点的坐标,这里我们将倾斜距离设置为1/5的宽度:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

代码定义里直接按图示取值即可:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

五、圆角矩形

圆角矩形就和上面的两个形状不一样了,因为不再是由直线组成,而是要有弧度:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

这里要使用一个新的GDI+方法:AddArc,添加一段弧线,其MSDN的解释如下:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

注意看最下面那段话:

如果图中有上一条直线或曲线,则会添加一条线,用于将上一段的端点连接到弧线的开头。

所以我们并不需要添加4条直线4个弧线,只需要添加4个弧线就行了,我们暂时将弧线所在圆的直径固定为20。

其中:

1,左上角

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

2,右上角

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

3,右下角

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

4,左下角

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

我们参照上图的坐标及角度编写代码即可:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

六、文本居中显示

上面的形状实现后,我们会发现文本位置都不统一,我们下面就来让文本统一居中显示。

核心是使用GDI+的DrawString的一个重载方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

我们像下面这样写就能让文本居中显示:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

关于StringFormat的详细讲解,请参照教程:

C# StringFormat详解之文本方向、对齐

https://www.cnblogs.com/lesliexin/p/12879270.html

具体的代码改造如下,不再赘述:

点击查看代码
     /// <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 });         }      } 

七、添加形状方法抽象出泛型方法

我们看之前添加矩形和圆形的方法:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

会发现很类似,类似是因为矩形和圆形都是形状基类的实现,那么我们本节课程添加了这三个新的形状,再这样写就太繁琐了,我们直接抽象出一个泛型方法来解决此问题:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

可以看到,就是将生成矩形和圆形的方法使用泛型替代。

我们再写一个泛型方法来将形状添加到画布:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

好了,到此我们的代码就进一步简化了,添加不同形状只需要传入对应的形状类型就行了:

[原创]《C#高级GDI+实战:从零开发一个流程图》第08章:增加菱形、平行四边形、圆角矩形,文本居中显示

是不是很优雅~

完整代码如下,大家可自行尝试:

点击查看代码
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();         }      }   }  

八、结语

我们本节课添加了多个不同的形状,这些形状也是流程图中常用的形状,有了这些基础,用户可按自己的需求添加自己的形状。当然现在的形状属性还很少,会随着课程的深入而丰富,以支持更多效果。

我们还抽象出一泛型方法来简化添加形状的操作,使用起来很是优雅。

现在基本的形状都有了,我们下节课就来添加新的连线:贝塞尔曲线,这个几乎是最常见的曲线。

同时,有了新的连线,我们还会增加不同的连接点用来连线,而不再只连接形状的中心点。

敬请期待。

感谢大家的观看,本人水平有限,文章不足之处欢迎大家评论指正。

-[END]-

发表评论

评论已关闭。

相关文章