1

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

 1 year ago
source link: https://www.cnblogs.com/wuhuacong/p/16404257.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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控件拖动到窗体中后,会自动增加一些属性窗口,上排的绘图工具中的按钮是我添加的,用来测试该控件的一些属性的控制。

8867-20220623105444183-1527447666.png

1)属性窗口的显示和隐藏(折叠)

这个通过控制diagramControl1.OptionsView.PropertiesPanelVisibility 属性就可以实现对这个属性窗口的控制了。

8867-20220623102922600-272089548.png

 里面显示一些系统位置和内容信息,以及一些自定义信息的窗口,后面我会介绍如何自定义处理这些模具的属性。

通过按钮处理的代码,我们可以实现对这个窗口的显示或者隐藏处理。

//切换属性窗口的显示或关闭
var status = diagramControl1.OptionsView.PropertiesPanelVisibility;
diagramControl1.OptionsView.PropertiesPanelVisibility = (status == PropertiesPanelVisibility.Visible ? PropertiesPanelVisibility.Collapsed : PropertiesPanelVisibility.Visible);

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

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

 

8867-20220623103348342-1942554781.png
//切换模具形状窗口的显示或关闭
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)其他属性的处理

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

8867-20220623104209123-2059009564.png

//是否显示标尺
this.diagramControl1.OptionsView.ShowRulers = this.chkRuler.Checked;
//是否显示网格
this.diagramControl1.OptionsView.ShowGrid = this.chkGrid.Checked;
//是否只读视图
this.diagramControl1.OptionsProtection.IsReadOnly = this.chkReadOnly.Checked;

2、绘图的处理事件

在绘制图形的时候,一般来说我们可能需要切换点选模式或者连接线模式,因此可以通过它的属性ActiveTool进行设置。在点选模式下,可以对图形进行拖动、放大缩小、旋转等处理,连接线模式下,则会加亮连接点,便于自动绘制连接线。

8867-20220623104604316-1714272173.png

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;
        }

8867-20220623105210631-1931520116.png

另外图形的保存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);
    }
}

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

8867-20220623105036523-1269004871.png

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);
}
8867-20220623105823199-267251075.png

 我们只需要设置选中的图形就可以了,其他有需要的可以从More Shapes中选择即可。

我们如果需要在属性窗口中显示自定义的属性,那么我们需要一些代码开发才能实现。

8867-20220623110108423-1560274567.png

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

8867-20220623110248743-1869964420.png

对自定义属性的处理,需要在事件中实现

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

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

8867-20220623110810457-1667010768.png

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

8867-20220623105036523-1269004871.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK