玩转控件:GDI+动态绘制流程图
- 前言
今天,要跟大家一起分享是“GDI+动态生成流程图”的功能。别看名字高大上(也就那样儿--!),其实就是动态生成控件,然后GDI+绘制直线连接控件罢了。实际项目效果图如下:

- Talk is Cheap,Show me the Code
首先,人靠衣装马靠鞍!在绘制流程图之前,我们得有个高大上的背景来衬托,比如网格背景:

代码如下:
/// <summary>
/// 初始化网格
/// </summary>
private void InitGridLine()
{
pictureBox1.BorderStyle = BorderStyle.Fixed3D;
pictureBox1.Focus();
m_picture = pictureBox1.CreateGraphics();
Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics gp = Graphics.FromImage(canvas);
DrawGrid(gp);
pictureBox1.BackgroundImage = canvas;
pictureBox1.Refresh();
}
//绘制网格
private void DrawGrid(Graphics gp)
{
for (int i = 0; i < Row; i++)
{
gp.DrawLine(new Pen(Color.LightCyan), (i + 1) * pictureBox1.Width / Row, 0, (i + 1) * pictureBox1.Width / Row, pictureBox1.Height);
}
for (int i = 0; i < colums; i++)
{
gp.DrawLine(new Pen(Color.LightCyan), 0, (i + 1) * pictureBox1.Height / colums, pictureBox1.Width, (i + 1) * pictureBox1.Height / colums);
}
}
我们此处以PictureBox为画布,初始化好网格背景后,就可以开始创建流程标签了,效果如下:

代码如下:
/// <summary>
/// 绘制元素,此处以Label为例
/// </summary>
/// <returns></returns>
private Label createBlock(string lblName)
{
try
{
Label label = new Label();
label.AutoSize = false;
//TODO:如需动态生成每个标签元素位置,请根据实际情况,初始化标签的Location即可。此处默认X=150,Y 以75间隔递增
label.Location = new Point(150, iPosition);
iPosition = iPosition + 75;
label.Size = new Size(89, 36);
label.BackColor = Color.DarkOliveGreen;
label.ForeColor = Color.Black;
label.FlatStyle = FlatStyle.Flat;
label.TextAlign = ContentAlignment.MiddleCenter;
label.Text = lblName;
//TODO;可以绑定标签元素的右键事件
//label.ContextMenuStrip = contextBlock;
pictureBox1.Controls.Add(label);
//拖拽移动
MoveBlock(label);
return label;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return null;
}
实现动态生成的标签拖拽移动效果,方法如下:
//标签移动效果
private void MoveBlock(Label block, Label endBlock = null)
{
block.MouseDown += (ss, ee) =>
{
if (ee.Button == System.Windows.Forms.MouseButtons.Left)
fPoint = Control.MousePosition;
};
block.MouseMove += (ss, ee) =>
{
if (ee.Button == System.Windows.Forms.MouseButtons.Left)
{
Point temp = Control.MousePosition;
Point res = new Point(fPoint.X - temp.X, fPoint.Y - temp.Y); block.Location = new Point(block.Location.X - res.X,
block.Location.Y - res.Y);
fPoint = temp;
pictureBox1.Invalidate(); // <------- draw the new lines
}
};
}
生成好背景网格和标签,以及实现标签的拖拽后,就需要绘制直线按自己需求,实现连接了。本文我们用 Tuple 来实现两个标签的连接关系。
//用于存储需要直线连接的元素
List<Tuple<Label, Label>> lines = new List<Tuple<Label, Label>>();
绑定PictureBox的Paint事件,利用GDI+的DrawLine实现绘制直线。
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (Tuple<Label, Label> t in lines)
{
Point p1 = new Point(t.Item1.Left + t.Item1.Width / 2,
t.Item1.Top + t.Item1.Height / 2);
Point p2 = new Point(t.Item2.Left + t.Item2.Width / 2,
t.Item2.Top + t.Item2.Height / 2); e.Graphics.DrawLine(Pens.Black, p1, p2);
}
}
好了,所有工作都已完成,此时,只需要把想要连接的两个标签添加到当前集合中,即可完成直线的连接功能。效果如图

参考文献:
https://docs.microsoft.com/zh-cn/dotnet/api/system.tuple-2?view=netcore-3.1
https://stackoverflow.com/questions/31626027/how-to-connect-with-line-shapes-labels-on-runtime/31642448#31642448?newreg=de162494b077460383555e4da76bdd18
- 结束语
由于后续所有重写/重绘控件都在同一个项目使用,而且Dev系统引用文件较多,压缩后源码文件仍然很大,如果有需要源码的朋友,可以微信公众号回复:erp,即可获取Fucking ERP所有源码示例~!有疑问的也可以CALL我一起探讨。
最后,感谢您的耐心陪伴!如果觉得本篇博文对您或者身边朋友有帮助的,麻烦点个关注!赠人玫瑰,手留余香,您的支持就是我写作最大的动力,感谢您的关注,期待和您一起探讨!再会!
玩转控件:GDI+动态绘制流程图的更多相关文章
- 玩转控件:Fucking ERP之流程图
前言 首先,跟守护在作者公众号和私信作者催更的朋友们道个歉.疫情的原因,公司从年初到现在一直处于996+的高压模式,导致公众号更新频率较低.而且作者每更新一篇原创公众号,既要对自己沉淀知识负责,也要对 ...
- 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .
双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...
- MFC控件GDI编程
MFC控件GDI编程 一丶学习内容 1.了解常用的GDI函数绘图. 2.使用常用的画笔画刷. 二丶常用的GDI函数绘图 上方则为我们常用的GDI函数了. 画线 矩形. 以及圆 等等. 2.1 画线代码 ...
- 玩转控件:重绘DEVEXPRESS中DateEdit控件 —— 让DateEdit支持只选择年月 (提供源码下载)
前言 上一篇博文<玩转控件:重绘ComboBox —— 让ComboBox多列显示>中,根据大家的回馈,ComboBox已经支持筛选了,更新见博文最后最后最后面. 奇葩 这两天遇到 ...
- 玩转控件:重写/重绘Dev中MessageBox弹窗控件
很久没有更新博客了,本想着直接发一篇<手撕ERP>系列,从控件重写.重绘,到框架搭建,再到部分模块实现+业务的.但是每次动手的时候,都觉得难以下手.直接从数据库设计开始吧,模块设计还没定下 ...
- 玩转控件:扩展Dev中SimpleButton
何为扩展,顾名思义,就是在原有控件属性.事件的基础上拓展自己需要或实用的属性.事件等等.或者可以理解为,现有的控件已经不能完全满足我(的需求)了.好的扩展会使控件更加完善,实用,好用.不好的扩展,说白 ...
- 玩转控件:封装Dev的LabelControl和TextEdit
俗话说的好:"工欲善其事必先利其器",作为软件攻城狮也是同样道理,攻城狮开发的软件目的是简化客户的操作,让客户动动手指就可以完成很多事情,减少人力成本.这也是系统/软件存在的目的. ...
- 玩转控件:封装Dev的SearchLookupEdit
鸣谢 随着前面几个章节对控件封装与扩展的分享,不少小伙伴儿们在作者公众号上反馈,并联系作者,表示通过这些系列和源码能学到不少细节上的东西,并运用到了自己的实际项目当中,也有不少伙伴儿反馈更好更优的处理 ...
- 玩转控件:对Dev的GridControl控件扩展
缘由 一切实现来源于需求,目的在于不盲目造轮子,有小伙伴儿在看了<玩转控件:对Dev中GridControl控件的封装和扩展>文章后,私信作者说,因公司业务逻辑比较复杂,展示字段比较多,尤 ...
随机推荐
- [AngstromCTF 2019]Cookie Cutter
最近看到了一个国外高中生的CTF比赛,翻了一下往年的例题,发现有一道关于jwt session伪造的题比较有意思,记录一下 题目简介中给出了我们题目的地址和后端处理的源码,看看源码先代码审计一下: c ...
- Tomcat7.0.99集群使用Redis共享session方案
以前配置过给予多播的session共享方案,这回再配置一个redis共享session的. 先小小的炫耀一下: 相信大家要做Tomcat+Redis+session配置,遇到的头号麻烦就是编译的tom ...
- 如何将tensorflow1.x代码改写为pytorch代码(以图注意力网络(GAT)为例)
之前讲解了图注意力网络的官方tensorflow版的实现,由于自己更了解pytorch,所以打算将其改写为pytorch版本的. 对于图注意力网络还不了解的可以先去看看tensorflow版本的代码, ...
- oracle之二ASM 管理
Oracle ASM 管理(PPT-II:602-636) 16.1 什么是ASM 自动存储管理即ASM(Automatic Storage Management),是Oracle提供的一项管理磁盘的 ...
- JWT伪造攻击
JWT修改伪造攻击 什么是JWT? JSON Web Token(JSON Web令牌)是一种跨域验证身份的方案.JWT不加密传输的数据,但能够通过数字签名来验证数据未被篡改(但是做完下面的WebGo ...
- Gradle实战(01)--介绍与安装
前言 本章你将学习到 Gradle的介绍 Gradle的安装 Gradle的hello world 1 Gradle的介绍 Gradle是专注于灵活性和性能的开源构建自动化工具 Gradle构建脚本是 ...
- NuxtJS快速入门
服务器端渲染(SSR) 知识储备 ES6 Nodejs Vue React Angular 什么是服务器端渲染 前端渲染:html页面作为静态文件存在,前端请求时后端不对该文件做任何内容上的修改,直接 ...
- 后端开发实践:Spring Boot项目模板
在我的工作中,我从零开始搭建了不少软件项目,其中包含了基础代码框架和持续集成基础设施等,这些内容在敏捷开发中通常被称为"第0个迭代"要做的事情.但是,当项目运行了一段时间之后再来反 ...
- React手稿之State Hooks of Hooks
React Hooks React在16.7.0-alpha.0版本中提到了Hooks的概念,目前还是Proposal阶段. 官方也陈述,接下来的90%的工作会投入到React Hooks中. 从目前 ...
- 微信App支付接入步骤&支付中前后端交互流程
最近对微信App支付(App端集成微信支付SDK)申请步骤,以及终端在进行微信支付时商户App.商户Server.微信App.微信支付Server的交互流程进行了简单了解.这篇文章应该算是学习笔记,分 ...