一、前言

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

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

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

二、先看效果

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

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

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

三、菱形

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

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

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

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

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

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

四、平行四边形

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

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

五、圆角矩形

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

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

注意看最下面那段话:

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

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

其中:

1,左上角

2,右上角

3,右下角

4,左下角

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

六、文本居中显示

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

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

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

关于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 });
} }

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

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

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

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

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

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

是不是很优雅~

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

点击查看代码
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]-

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

  1. 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)

    本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...

  2. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

  3. 【Nodejs】326- 从零开发一个node命令行工具

    本文由 IMWeb 社区授权转载自腾讯内部 KM 论坛.点击阅读原文查看 IMWeb 社区更多精彩文章. 什么是命令行工具? 命令行工具(Cmmand Line Interface)简称cli,顾名思 ...

  4. Django实战总结 - 快速开发一个数据库查询工具

    一.简介 Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 只要很少的代码就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 ...

  5. 如何从零开发一个NuGet软件包?

    作者:依乐祝 首发地址:https://www.cnblogs.com/yilezhu/p/14175019.html 我想目前每个.net开发人员都应该知道nuget.org和NuGet软件包吧.但 ...

  6. 【前端vue进阶实战】:从零打造一个流程图、拓扑图项目【Nuxt.js + Element + Vuex】 (一)

    本系列教程是用Vue.js + Nuxt.js + Element + Vuex + 开源js绘图库,打造一个属于自己的在线绘图软件,最终效果:topology.le5le.com .如果你觉得好,欢 ...

  7. 【原创】新手入门一篇就够:从零开发移动端IM

    一.前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响. 做为IM开发者或即将成为IM开发者的技术人员 ...

  8. 《IM开发新手入门一篇就够:从零开发移动端IM》

        登录 立即注册 TCP/IP详解 资讯 动态 社区 技术精选 首页   即时通讯网›专项技术区›IM开发新手入门一篇就够:从零开发移动端IM   帖子 打赏 分享 发表评论162     想开 ...

  9. 从零开始,开发一个 Web Office 套件(7):新的问题—— Click 事件的 z-index

    这是一个系列博客,最终目的是要做一个基于 HTML Canvas 的.类似于微软 Office 的 Web Office 套件(包括:文档.表格.幻灯片--等等). 博客园:<从零开始, 开发一 ...

  10. 从零开始,开发一个 Web Office 套件(9):拖动鼠标选中文字 Edge Case

    这是一个系列博客,最终目的是要做一个基于 HTML Canvas 的.类似于微软 Office 的 Web Office 套件(包括:文档.表格.幻灯片--等等). 博客园:<从零开始, 开发一 ...

随机推荐

  1. ASP.NET Core已有数据库,却新建项目

    ASP.NET Core已有数据库,却新建项目,只需要构造出相应的类,DbContext,然后直接add-migration init即可!!而不用执行update-database,执行后者会报错: ...

  2. 仓颉开发语言入门教程:常见UI组件介绍和一些问题踩坑

    幽蓝君发现一个问题,仓颉开发语言距离发布马上一年了,一些知名App已经使用仓颉开发了许多功能,但是网络上关于仓颉开发语言的教程少之又少,系统性的教程更是没有,仓颉官网的文档也远远不如ArkTS详尽. ...

  3. 第1讲、#PyTorch教学环境搭建与Tensor基础操作详解

    引言 PyTorch是当前深度学习领域最流行的框架之一,因其动态计算图和直观的API而备受开发者青睐.本文将从零开始介绍PyTorch的环境搭建与基础操作,适合各种平台的用户和深度学习初学者. 1. ...

  4. 如何从Docker image提取 Dockerfile

    参考链接:https://github.com/cucker0/dockerimage2df 参考链接:https://github.com/cucker0/docker/blob/main/md/由 ...

  5. helmfile使用

    说明 使用helmfile时,我们首先得了解helm的使用,以及如何开发一个helm chart. helm是kubernetes的包管理工具.在实际的使用场景中我们涉及同时部署多个chart.区分不 ...

  6. kubernetes之Ingress工作原理

    一.kubernetes集群外部访问的方式 在kubernetes集群中,如果外部的应用需要访问集群内部的服务,可以通过NodePort Service.LoadBalancer Service.In ...

  7. 使用wxWidgets进行跨平台GUI开发(附1)

    补充说明wxWidgets在Windows下使用CMake的配置 wxWidgets官方提供了一个在Windows下使用CMake来构建wxWidgets库的方法,这样便于你自己用CMake构建项目. ...

  8. mysql字符集插入中文报错

    org.apache.ibatis.exceptions.PersistenceException: ### Error updating database. Cause: java.sql.SQLE ...

  9. 用Java获取本机IP或者请求用户的真正IP地址

    一.在Web请求中获取请求用户的IP地址 public static String getUserRealIP(HttpServletRequest request) throws UnknownHo ...

  10. DRF之分页类源码分析

    DRF之分页类源码分析 [一]分页类介绍 Django REST framework(DRF)是一个用于构建Web API的强大工具,它提供了分页功能,使你能够控制API响应的数据量. 在DRF中,分 ...