当前位置:网站首页>如何利用DevExpress控件绘制流程图?看完这篇文章就懂了!
如何利用DevExpress控件绘制流程图?看完这篇文章就懂了!
2022-08-01 11:17:00 【界面开发小八哥】
DevExpress提供了一个比较强大的图形绘制工具,可以用于绘制各种图形,如流程图、组织机构图等等,本篇随笔介绍XtraDiagram.DiagramControl的使用,以及利用代码对其属性进行控制,以及利用图形模具的自定义操作,实现一些简单流程图形的绘制和处理。
DiagramControl是类似Visio的绘图控件,以前我2006年的就接触使用Visio的二次开发,当时开始还是利用VB6 + VIsio2003进行二次开发的,后来把它改良为C# + Visio进行二次开发,DiagramControl的对象模型很类似Visio的相关对象模型,如对于工具栏的形状,称之为模具(Stencil),Visio也是称之为Stencil, DiagramControl里面的很多接口名称依旧采用Stencil进行命名,因此估计也是借鉴了很多Visio的对象设计知识。
1. DiagramControl控件的使用
DiagramControl是一个界面控件,类似Visio SDK里面的DrawingControl的存在,可以通过它进行图形的绘制,各种窗口的显示和隐藏,以及跟踪各种事件的处理。
DiagramControl控件拖动到窗体中后,会自动增加一些属性窗口,上排的绘图工具中的按钮是我添加的,用来测试该控件的一些属性的控制。

1)属性窗口的显示和隐藏(折叠)
这个通过控制diagramControl1.OptionsView.PropertiesPanelVisibility 属性就可以实现对这个属性窗口的控制了。

里面显示一些系统位置和内容信息,以及一些自定义信息的窗口,后面我会介绍如何自定义处理这些模具的属性。
通过按钮处理的代码,我们可以实现对这个窗口的显示或者隐藏处理。
//切换属性窗口的显示或关闭
var status = diagramControl1.OptionsView.PropertiesPanelVisibility;
diagramControl1.OptionsView.PropertiesPanelVisibility = (status == PropertiesPanelVisibility.Visible ? PropertiesPanelVisibility.Collapsed : PropertiesPanelVisibility.Visible);2)模具形状窗口的显示或隐藏
模具形状的窗口,它是放在一个面板里面,我们只需要通过控制该面板的显示或者隐藏就可以了,如下代码所示。

//切换模具形状窗口的显示或关闭
var status = diagramToolboxDockPanel1.Visibility;
diagramToolboxDockPanel1.Visibility = (status == DevExpress.XtraBars.Docking.DockVisibility.Visible ? DevExpress.XtraBars.Docking.DockVisibility.Hidden : DevExpress.XtraBars.Docking.DockVisibility.Visible);或者通过控件的Toolbar属性进行控制,一样的效果。
//切换模具形状窗口的显示或关闭
var status = this.diagramControl1.OptionsView.ToolboxVisibility;
this.diagramControl1.OptionsView.ToolboxVisibility = status == ToolboxVisibility.Closed ? ToolboxVisibility.Full : ToolboxVisibility.Closed;3)放大缩小窗口的显示或者隐藏
同样我们也可以控制放大缩小窗口的显示或者隐藏,它也是图形绘制的一个常见的窗口。我们只需要判断或者设置diagramControl1.OptionsView.ShowPanAndZoomPanel 属性就可以了,如下代码所示。
//切换放大缩小窗口的显示或关闭
var status = diagramControl1.OptionsView.ShowPanAndZoomPanel;
diagramControl1.OptionsView.ShowPanAndZoomPanel = !status;4)其他属性的处理
另外,我们可以通过控制一些属性,实现对标尺、网格、只读视图等模式进行控制。

//是否显示标尺
this.diagramControl1.OptionsView.ShowRulers = this.chkRuler.Checked;
//是否显示网格
this.diagramControl1.OptionsView.ShowGrid = this.chkGrid.Checked;
//是否只读视图
this.diagramControl1.OptionsProtection.IsReadOnly = this.chkReadOnly.Checked;2. 绘图的处理事件
在绘制图形的时候,一般来说我们可能需要切换点选模式或者连接线模式,因此可以通过它的属性ActiveTool进行设置。在点选模式下,可以对图形进行拖动、放大缩小、旋转等处理,连接线模式下,则会加亮连接点,便于自动绘制连接线。

private void btnPointerMode_Click(object sender, EventArgs e)
{
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.PointerTool;
}
private void btnConnectorMode_Click(object sender, EventArgs e)
{
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.ConnectorTool;
}当然,我们也可以通过对鼠标行为的分析来进行控制,如果鼠标悬停或者放置在图形上,就自动切换模式为连接线模式,否则为点选模式,那么只需要判断鼠标的移动行为即可自动处理,如下代码所示。
/// <summary>
/// 实现对图形自动切换到连接点模式
/// </summary>
private void diagramControl1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
return;
DiagramItem item = diagramControl1.CalcHitItem(e.Location);
if (item == null)
{
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.PointerTool;
return;
}
else if (item is DiagramConnector)
{
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.ConnectorTool;
return;
}
Rect itemBounds = new Rect(new Point(item.Position.X, item.Position.Y), new Size(item.Width, item.Height));
PointFloat documentPoint = diagramControl1.PointToDocument(new PointFloat(e.Location));
DiagramHitInfo[] hitInfo = diagramControl1.CalcHitInfo(documentPoint);
if (itemBounds.Contains(new Point(documentPoint.X, documentPoint.Y)))
{
itemBounds.Inflate(-5, -5);
if (!itemBounds.Contains(new Point(documentPoint.X, documentPoint.Y)))
{
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.ConnectorTool;
return;
}
}
diagramControl1.OptionsBehavior.ActiveTool = diagramControl1.OptionsBehavior.PointerTool;
}
另外图形的保存xml、PNG、PDF处理和加载代码如下所示。
/// <summary>
/// 保存XML和图片文件
/// </summary>
private void SaveXml()
{
var xml = Path.Combine(Application.StartupPath, "MyFlowShapes.xml");
diagramControl1.SaveDocument(xml);
var pngFile = Path.Combine(Application.StartupPath, "MyFlowShapes.png");
diagramControl1.ExportDiagram(pngFile);
}
private void btnLoadXml_Click(object sender, EventArgs e)
{
var xml = FileDialogHelper.OpenXml();
if(!string.IsNullOrEmpty(xml))
{
diagramControl1.LoadDocument(xml);
}
}最终案例的效果如下所示。

3. 注册自定义的形状
在实际的图形绘制开发中,我们可以需要创建一些指定的形状模具,那么我们弄好后一般可以存放在XML中,然后进行加载到控件上来,如下代码就是注册自定义的形状的处理。
/// <summary>
/// 注册自定义的形状。
/// 自定义图形是以XML文件形式进行保存,图形需要按照规定XML格式进行绘制
/// </summary>
private void LoadShapes2()
{
var projectName = "SmallExampleDemo.Examples.XtraDiagram";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(projectName + ".CustomContainers.xml"))
{
var stencil = DiagramStencil.Create(MyStencilId, MyStencilName, stream, shapeName => shapeName);
DiagramToolboxRegistrator.RegisterStencil(stencil);
}
diagramControl1.SelectedStencils = new StencilCollection(MyStencilId);//(MyStencilId, BasicShapes.StencilId);
}
我们只需要设置选中的图形就可以了,其他有需要的可以从More Shapes中选择即可。
我们如果需要在属性窗口中显示自定义的属性,那么我们需要一些代码开发才能实现。

我们首先需要继承一个DiagramShape的子类,然后实现自己自定义的属性定义,如下代码所示。

对自定义属性的处理,需要在事件中实现
diagramControl1.CustomGetEditableItemProperties += DiagramControl_CustomGetEditableItemProperties;通过对它进行判断可以实现自定义属性的显示处理
void DiagramControl_CustomGetEditableItemProperties(object sender, DiagramCustomGetEditableItemPropertiesEventArgs e)
{
if (e.Item is DiagramShapeEx)
{
e.Properties.Add(TypeDescriptor.GetProperties(typeof(DiagramShapeEx))["Status"]);
e.Properties.Add(TypeDescriptor.GetProperties(typeof(DiagramShapeEx))["TypeName"]);
}
}然后我们可以注册创建自己的模具形状集合,如下代码所示。
/// <summary>
/// 创建自定义的模具
/// </summary>
/// <returns></returns>
DiagramStencil CreateCustomDrawShapesStencil()
{
var stencilId = "CustomedFlowShape";
var stencilName = "流程图";
var shapeSizeSmall = new Size(100, 37.5);
var shapeSize = new Size(100, 75);
DiagramControl.ItemTypeRegistrator.Register(typeof(DiagramShapeEx));
var stencil = new DiagramStencil(stencilId, stencilName);
//流程类型
stencil.RegisterTool(new FactoryItemTool("StartEnd", () => "流程开始", diagram => {
var shape = new DiagramShapeEx(BasicFlowchartShapes.StartEnd, "流程开始");
shape.Appearance.BackColor = Color.Red;
return shape;
}, shapeSizeSmall));
stencil.RegisterTool(new FactoryItemTool("Decision", () => "流程条件", diagram => {
var shape = new DiagramShapeEx(BasicFlowchartShapes.Decision, "流程条件");
shape.Appearance.BackColor = Color.FromArgb(199, 115, 1);//Color.Red;
return shape;
}, shapeSize));这两个流程开始,流程条件,我们直接是从 BasicFlowchartShapes 集合中借用过来,构建自己的自定义对象的,默认创建的对象是方形的。
如果我们需要动态构建其他自定义类型,我们可以指定它的颜色等样式,从而构建不同类型的图形。
//循环添加相关流程节点
var procNames = new List<string> { "审批", "归档", "阅办", "会签", "领导批示分阅"};
//定义几个初始化颜色顺序
var colors = new List<Color> { Color.DeepSkyBlue, Color.ForestGreen, Color.Violet, Color.Yellow, Color.Blue, Color.Orange, Color.Indigo, Color.Purple, Color.Black, Color.Brown, Color.Pink };
int i = 0;
foreach (string name in procNames)
{
var shapeId = string.Format("Process_{0}", i++);
stencil.RegisterTool(new FactoryItemTool(shapeId, () => name, diagram =>
{
var shape = new DiagramShapeEx(name, Status.Inactive);
var index = procNames.IndexOf(name);
var color = colors[index % 10];//Color.Red;
var fontColor = (color == Color.Yellow) ? Color.Black : Color.White;
//没什么作用
//shape.ThemeStyleId = GetStyle(index); //从Accent1样式开始 DiagramShapeStyleId.Styles[index];//
shape.Appearance.BackColor = color;
shape.Appearance.BorderSize = 3;
shape.Appearance.Font = new Font("宋体", 12f, FontStyle.Bold);
shape.Appearance.ForeColor = fontColor;
return shape;
}, shapeSize));
}这样就有不同颜色的图形对象了。

根据这些我们就可以绘制出自己的各种流程图了,并且也可以根据数据库的信息,进行动态绘制展示。

DevExpress Universal Subscription拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。
屡获大奖的软件开发平台DevExpress Universal 2022年第一个重要版本——v22.1已正式发布,该版本拥有众多新产品和数十个具有高影响力的功能,可为桌面、Web和移动应用提供直观的解决方案,全面解决各种使用场景问题。
本文转载自:博客园 - 伍华聪
DevExpress技术交流群6:600715373 欢迎一起进群讨论
边栏推荐
- 回归预测 | MATLAB实现TPA-LSTM(时间注意力注意力机制长短期记忆神经网络)多输入单输出
- 稀疏表示--学习笔记
- How to Steal $100 Million from the Perfect Smart Contract
- MFC implementation road map navigation system
- 小程序毕设作品之微信美食菜谱小程序毕业设计成品(4)开题报告
- Promise学习(一)Promise是什么?怎么用?回调地狱怎么解决?
- R语言ggplot2可视化:使用ggpubr包的geom_exec函数执行geom_*函数(没有任何参数需要放置在aes中)
- 轮询和长轮询的区别
- 爱可可AI前沿推介(8.1)
- 表连接详解
猜你喜欢

jmeter

Promise学习(四)异步编程的终极解决方案async + await:用同步的方式去写异步代码

【随心笔记】假期快过去了,都干了点什么

【likeshop】回收租凭系统100%开源无加密 商城+回收+租赁

Endorsed in 2022 years inventory | product base, science and technology, guangzhou automobile group striding forward

Promise learning (4) The ultimate solution for asynchronous programming async + await: write asynchronous code in a synchronous way

大众碰到点评的一个字体反爬,落地技术也是绝了

Small application project works WeChat gourmet recipes applet graduation design of finished product (1) the development profile

Mini Program Graduation Works WeChat Food Recipes Mini Program Graduation Design Finished Products (4) Opening Report

Android Security and Protection Policy
随机推荐
R语言拟合ARIMA模型:使用forecast包中的auto.arima函数自动搜索最佳参数组合、模型阶数(p,d,q)、设置seasonal参数指定在模型中是否包含季节信息
退役划水
千万级乘客排队系统重构&压测方案——总结篇
稀疏表示--学习笔记
JWT
Browser shortcut keys
pgAdmin 4 v6.12 发布,PostgreSQL 开源图形化管理工具
复现assert和eval成功连接或失败连接蚁剑的原因
【无标题】
每日一题:连续子数组的最大和(动态规划)
C#/VB.NET 将PPT或PPTX转换为图像
Why Metropolis–Hastings Works
Solve vscode input! Unable to quickly generate skeletons (three methods for the new version of vscode to quickly generate skeletons)
小程序毕设作品之微信美食菜谱小程序毕业设计成品(4)开题报告
Promise to learn several key questions (3) the Promise - state change, execution sequence and mechanism, multitasking series, abnormal penetration, interrupt the chain of Promise
在线GC日志分析工具——GCeasy
Promise学习(三)Promise的几个关键性问题 -- 状态改变、执行顺序与机制、多任务串联、异常穿透、中断promise链
ModelArts-based object detection YOLOv3 practice [play with HUAWEI CLOUD]
万字解析:vector类
xss漏洞学习