• 利用XtraDiagram.DiagramControl进行流程图形的绘制和控制


    🚀 优质资源分享 🚀

    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

    DevExpress提供了一个比较强大的图形绘制工具,可以用于绘制各种图形,如流程图、组织机构图等等,本篇随笔介绍XtraDiagram.DiagramControl的使用,以及利用代码对其属性进行控制,以及利用图形模具的自定义操作,实现一些简单流程图形的绘制和处理。

    DiagramControl是类似Visio的绘图控件,以前我2006年的就接触使用Visio的二次开发,当时开始还是利用VB6 + VIsio2003进行二次开发的,后来把它改良为C# + Visio进行二次开发,DiagramControl的对象模型很类似Visio的相关对象模型,如对于工具栏的形状,称之为模具(Stencil),Visio也是称之为Stencil, DiagramControl里面的很多接口名称依旧采用Stencil进行命名,因此估计也是借鉴了很多Visio的对象设计知识,如果您对Visio二次开发感兴趣,可以参考我的随笔文章《Visio二次开发》,里面有很多相关的内容。

    而如果想了解这个控件的相关知识和使用,参考官网的案例和说明应该是比较好的教程(https://docs.devexpress.com/WindowsForms/118290/controls-and-libraries/diagrams/getting-started )。

    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);
    
    • 1
    • 2
    • 3

    2)模具形状窗口的显示或隐藏

    模具形状的窗口,它是放在一个面板里面,我们只需要通过控制该面板的显示或者隐藏就可以了,如下代码所示。

    //切换模具形状窗口的显示或关闭
    var status = diagramToolboxDockPanel1.Visibility;
    diagramToolboxDockPanel1.Visibility = (status == DevExpress.XtraBars.Docking.DockVisibility.Visible ? DevExpress.XtraBars.Docking.DockVisibility.Hidden : DevExpress.XtraBars.Docking.DockVisibility.Visible);
    
    • 1
    • 2
    • 3

    或者通过控件的Toolbar属性进行控制,一样的效果。

    //切换模具形状窗口的显示或关闭
    var status = this.diagramControl1.OptionsView.ToolboxVisibility;
    this.diagramControl1.OptionsView.ToolboxVisibility = status == ToolboxVisibility.Closed ? ToolboxVisibility.Full : ToolboxVisibility.Closed;
    
    • 1
    • 2
    • 3

    3)放大缩小窗口的显示或者隐藏

    同样我们也可以控制放大缩小窗口的显示或者隐藏,它也是图形绘制的一个常见的窗口。我们只需要判断或者设置diagramControl1.OptionsView.ShowPanAndZoomPanel 属性就可以了,如下代码所示。

    //切换放大缩小窗口的显示或关闭
    var status = diagramControl1.OptionsView.ShowPanAndZoomPanel;
    diagramControl1.OptionsView.ShowPanAndZoomPanel = !status;
    
    • 1
    • 2
    • 3

    4)其他属性的处理

    另外,我们可以通过控制一些属性,实现对标尺、网格、只读视图等模式进行控制。

    //是否显示标尺
    this.diagramControl1.OptionsView.ShowRulers = this.chkRuler.Checked;
    //是否显示网格
    this.diagramControl1.OptionsView.ShowGrid = this.chkGrid.Checked;
    //是否只读视图
    this.diagramControl1.OptionsProtection.IsReadOnly = this.chkReadOnly.Checked;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当然,我们也可以通过对鼠标行为的分析来进行控制,如果鼠标悬停或者放置在图形上,就自动切换模式为连接线模式,否则为点选模式,那么只需要判断鼠标的移动行为即可自动处理,如下代码所示。

            /// 
            /// 实现对图形自动切换到连接点模式
     /// 
            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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    另外图形的保存xml、PNG、PDF处理和加载代码如下所示。

    /// 
    /// 保存XML和图片文件
    /// 
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    private void btnLoadXml\_Click(object sender, EventArgs e)
    {
     var xml = FileDialogHelper.OpenXml();
     if(!string.IsNullOrEmpty(xml))
     {
     diagramControl1.LoadDocument(xml);
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最终案例的效果如下所示。

    3、注册自定义的形状

    在实际的图形绘制开发中,我们可以需要创建一些指定的形状模具,那么我们弄好后一般可以存放在XML中,然后进行加载到控件上来,如下代码就是注册自定义的形状的处理。

    /// 
    /// 注册自定义的形状。
    /// 自定义图形是以XML文件形式进行保存,图形需要按照规定XML格式进行绘制
    /// 
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们只需要设置选中的图形就可以了,其他有需要的可以从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"]);
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后我们可以注册创建自己的模具形状集合,如下代码所示。

    /// 
    /// 创建自定义的模具
    /// 
    /// 
    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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这两个流程开始,流程条件,我们直接是从 BasicFlowchartShapes 集合中借用过来,构建自己的自定义对象的,默认创建的对象是方形的。

    如果我们需要动态构建其他自定义类型,我们可以指定它的颜色等样式,从而构建不同类型的图形。

    //循环添加相关流程节点
    var procNames = new List<string> { "审批", "归档", "阅办", "会签", "领导批示分阅"};
    //定义几个初始化颜色顺序
    var colors = new List { 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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这样就有不同颜色的图形对象了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gis5ssDQ-1655959079592)(https://img2022.cnblogs.com/blog/8867/202206/8867-20220623110810457-1667010768.png)]

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

  • 相关阅读:
    音视频基本概念和FFmpeg的简单入门
    基于单片机的智能数字电子秤proteus仿真设计
    Java:ArrayList的基本使用(学习笔记)
    for循环中var声明导致定时器输出奇特现象的粗略解释
    问题 R: 超级楼梯(递推,基础DP)查表
    如何通过CRM系统做好客户的分级分类
    使用JMeter创建数据库测试
    设计一个简单HTML爵士音乐网页(HTML+CSS)
    uni-app:解决异步请求返回值问题
    毕业季,终于毕业了!
  • 原文地址:https://blog.csdn.net/qq_43479892/article/details/125424874