流程设计器是编辑流程模版的工具,可视化的流程设计器能直观的编辑流程模版,所见即所得,如下图是我们要做的流程设计器:
流程模版存放流程流转所需要的信息,使用流程设计器来管理流程模版是比较常见的方式,我见过一些流程系统使用表格的形式来管理,那样很不直观。也许是因为技术的原因,该篇就介绍如何制作这样的流程设计器,用到的技术GDI+绘图,数据库存取。要了解GDI+绘图技术的可以先看一下http://www.cnblogs.com/stg609/archive/2008/03/16/1108333.html 这个博客的介绍,比较基础。
在开始之前,先介绍一下目前市场上流程设计器的几种形式,无非就是两种形式一种是B/S的,一种是C/S,我个人感觉这两种形式没必要苛求那一种,各有优缺点,B/S的安装方便一些,不用安装直接使用,但操作灵活性比不上C/S的,B/S的流程设计器目前有几种技术可以实现,一种是js,一种是Silverlight或者FLex,还有一种是用ActiveX插件。流程设计器一般是管理员、实施人员使用,用户群体比较单一固定,但使用要求比较高,必须方便灵活,所以我们的流程设计器采用C/S的方式,结合我们前面提供的WCF远程数据服务,可以实现远程流程模版管理,绝对不亚于B/S的流程设计器。
该篇内容比较多,先看一下要介绍的内容:
1、流程模版分类管理
2、如何使用GDI+画流程图
3、流程任务节点类型和属性
4、流程模版的导入导出
下面详细介绍每一部分:
1、流程模版分类管理
按照业务对流程模版进行分类,建立一个树形结构的分类机制,不限级数,如下图:
每个分类上可以配置管理界面,如下图:可以配置两种形式的管理界面,一种是web页面,一种是winform的窗体。这些管理界面是在业务平台上使用。
2、如何使用GDI+画流程图
GDI+:Graphics Device Interface Plus是2D图形处理的技术,在C#.NET中,使用GDI+处理二维(2D)的图形和图像,使用DirectX处理三维(3D)的图形图像,图形图像处理用到的主要命名空间是System . Drawing:提供了对GDI+基本图形功能的访问,主要有Graphics类、Bitmap类、从Brush类继承的类、Font类、Icon类、Image类、Pen类、Color类等。
了解了GDI+基本概念后,再来了解2D图像处理的原理,在窗体或者控件上显示图形或者图像必须使用OnPaint事件,把成像的代码放到该事件里,在窗体重画时才会保持显示状态,如果不放在该事件里,那么在窗体重画时图像就无法显示,所以OnPaint事件是图像显示的重要事件,每个窗体和窗体上的控件都是利用该事件来显示的,做过控件开发的应该很了解这个机制。
根据以上原理,流程图的原理也就很简单了,就是在画布上把表示各种类型的任务节点画出来,再用带箭头的连线把他们连接起来,把这个过程放到画布的OnPaint事件里面,这样流程图就可以展现出来,同时选中的节点画上选中标志。要实现可视化操作还需要解决一下问题:
a、读取流程模版
从数据库中把流程模版中的所有任务节点和连线读出来,放到全局的数组中,在OnPaint事件中把TaskItems 数组和LineItems数组中的图像画出来。如下图数组定义:
/// <summary>
/// 流程模板所有节点列表
/// </summary>
public ArrayList TaskItems = new ArrayList();
/// <summary>
/// 流程模板所有连线列表
/// </summary>
public ArrayList LineItems=new ArrayList();
由于重画事件执行频率比较高,所以该过程内的代码都是操作内存对象,读写数据库的代码不要放到这里面。
详细代码参见HF.WorkFlow.Component项目的WorkPlace.cs
b、如何选中任务节点和多选节点。
选中节点的原理是通过图像坐标来判断的,每个图形或者图像都有一个Rectangle区域,该区域记录着节点x,y轴坐标和长度宽度,只要鼠标落点在这个区域内视为选中该节点。这里有个处理技巧,一般任务节点的尺寸是实际看到的图片尺寸,但是在选中的时候鼠标点中图标周围也算选中,这是一种模糊处理的概念,很好用,如果不这么处理,点选任务节点的时候会很别扭,非得点中图片内才算选中。也可以这么理解,任务节点的有效区域范围是图片本身的尺寸加上一个边框,只要在边框内都算点中。我们在OnPaint事件中把选中的节点的边框以毛边的形式显示出来,并加上8个方向的方块句柄,这样选择的节点就很形象了。如下图:
判断节点是否选中的函数:
/// <summary>
/// 判断给定坐标是否在组件的边界矩形内
/// </summary>
/// <param name="thePoint">给定坐标</param>
/// <returns>是否选中</returns>
public bool Contains(Point thePoint)
{
if(!Selected)//没有选中
return bounds.Contains(thePoint);
else
{
Rectangle selectionRect=bounds;//节点图像的尺寸区域
selectionRect.Inflate(SystemInformation.FrameBorderSize);//通过各方向的给定值增加矩形的尺寸,加上选择边框
return selectionRect.Contains(thePoint);
}
}
然后把选中的节点添加到一个全局的数组中,供拖动或者删除使用。
/// <summary>
/// 选中的节点列表
/// </summary>
public SelectedItems SelectedItems = new SelectedItems();
多选的处理原理是判断所有节点的坐标是否在鼠标经过的区域内,如果在该区域内添加到SelectedItems 数组中,画布重画的时候会自动给SelectedItems数组的节点画上选中标志,即毛边和抓取句柄。下图是鼠标圈选时滑过的矩形橡皮圈,鼠标抬起时在该区域内的节点都添加到选中数组中。
c、如何移动选中的节点。
前面介绍了如何选中节点,移动节点就是改变选中节点的坐标,只要配合鼠标移动事件和鼠标抬起事件就可以。在鼠标移动时画出移动的轨迹很关键,这样更直观一些,如下图:
方框为移动经过时留下的轨迹,当鼠标抬起时确定选中节点的坐标。重新激活重画事件OnPaint,重画事件会重新在界面上画出任务节点和连线。这就达到了移动的目的。
d、保存流程模版
我们在读取模版的时候把任务节点和连线都存放到数组中,那么在保存的时候把数组中的任务节点和连线写入到数据库中即可。
上面是流程设计器的主要原理,下面来用代码实现一个简单的设计器。
a、先定义一个任务节点的基类
首选要确定有多少种类型的节点,每种类型的节点的功能是什么,使用什么样式的图标等,后面会介绍节点类型,这里我们先定义一个节点的基类BaseComponent,所有节点类型都继承该类。
基类的属性字段包括节点的坐标、图片、字体、名称、类型等基本属性。
基类的方法包括画节点的方法,选中节点的方法。
代码比较多复制到这里也不方便,具体代码参见BaseComponent.cs。
b、如何画节点的毛边和抓取句柄
前面见过毛边是任务节点图像周围的边框,详细定义参见Bounds.cs,八个方向的抓取句柄参见GrabHandles.cs。
c、如何对齐节点
节点的对齐操作参见Dragger.cs
d、如何定义一个多选时的橡皮圈
定义圈选时的橡皮圈轨迹,参见Rubberband.cs。
以上代码中都有详细的注释。
下一篇我们介绍节点类型和节点属性。