今天来讨论一下“承载工作流设计器”的例子,此篇文章有疏忽错误的地方,请大家热心指正。
虽然Visual Studio已经包含了Windows Workflow Foundation工作流可视化设计器,但是有的时候我们需要在自己的制作的Windows应用程序中承载这种基于图形的工作流设计器,编写好Activity,然后由客户来设计灵活的工作流程。
在MSDN中,关于“承载工作流设计器”的文章:
http://msdn.microsoft.com/zh-cn/library/ms733614.aspx
这里叙述了承载工作流设计器的重点,以下所有的内容都是围绕着此篇文档,所以有必要将该文档理解透彻。
要想做到工作流设计器的承载,我们可能会用到下面这四个类:
DesignSurface 类:该类通过提供一个完全独立的设计图面来实现用户心目中的设计器。
(它负责为我们的设计器添加功能)
WorkflowView 类:该类显示在工作流标记中描述的工作流的可视化表示形式。
(它实现一个图形化的类设计器界面)
WorkflowDesignerLoader 类:该类支持对工作流设计器及其组件的加载进行自定义。
(这就是我们的设计器加载器了)
WorkflowDesignerMessageFilter 类,可以通过重写相应的虚拟方法从该类进行派生,以创建可处理拖动操作、布局和绘画操作等工作流设计器事件以及其他设计器事件的自定义消息筛选器。
(过滤或者自定义消息用的)
如果想给我们的设计器添加菜单功能,必须派生MenuCommandService类,写上自己的代码。这些内容,在MSDN《承载工作流设计器》中介绍得很详细,就不多写了。
下面来解释一个标准MSDN承载工作流设计器的示例:“WorkflowDesignerReHosting.sln”,通过对它的学习,我们便可以制作出满足自己需要的设计器。
示例所在位置:MSDN > MSDN Library > .NET 开发 > .NET Framework SDK > .NET Framework > 示例 > Windows Workflow Foundation 示例 > 技术示例 > 设计器宿主 > “基本设计器宿主”示例
示例链接:http://msdn.microsoft.com/zh-cn/library/ms741708.aspx
示例下载:http://go.microsoft.com/fwlink/?LinkId=87352
示例截图:
由于我们只研究承载工作流设计器的实现,所以不讨论与WinForm的相关代码,下载后打开工程中WorkflowLoader.cs文件,代码如下:
- namespace Microsoft.Samples.Workflow.WorkflowDesignerRehosting
- {
- using System;
- using System.IO;
- using System.ComponentModel.Design.Serialization;
- using System.Workflow.ComponentModel.Compiler;
- using System.Workflow.ComponentModel.Design;
- internal sealed class WorkflowLoader : WorkflowDesignerLoader
- {
- internal WorkflowLoader()
- {
- }
- protected override void Initialize()
- {
- base.Initialize();
- IDesignerLoaderHost host = LoaderHost; //LoaderHost:该方法返回为该设计器加载程序提供的加载程序宿主。
- if (host != null)
- {
- TypeProvider typeProvider = new TypeProvider(host);
- //将String类所在的程序集添加到typeProvider的程序集引用内部列表当中
- //C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
- typeProvider.AddAssemblyReference(typeof(string).Assembly.Location);
- }
- }
- public override void Dispose()
- {
- try
- {
- IDesignerLoaderHost host = LoaderHost;
- if (host != null)
- {
- host.RemoveService(typeof(ITypeProvider), true);
- //void System.ComponentModel.Design.ServiceContainer.RemoveService(System.Type serviceType, bool promote)
- //从服务容器中移除指定的服务
- //第一个参数:要移除的服务类型
- //第二个参数:是否可以从任何父服务容器中移除
- //异常:ArgumentNullException: serviceType is null.
- //有一个重载:void RemoveService(System.Type serviceType)
- }
- }
- finally
- {
- base.Dispose();
- }
- }
- public override string FileName
- {
- get
- {
- return string.Empty;
- }
- }
- //System.Workflow.ComponentModel.Design.WorkflowDesignerLoader 用来读指定的文件。
- public override TextReader GetFileReader(string filePath)
- {
- return new StreamReader(new FileStream(filePath, FileMode.Open));
- }
- //用来写指定的文件
- public override TextWriter GetFileWriter(string filePath)
- {
- return new StreamWriter(new FileStream(filePath, FileMode.OpenOrCreate));
- }
- }
- }
这里定义了一个内部的密封类WorkflowLoader,它继承自WorkflowDesignerLoader,重写了Initialize方法,先完成基类的Initialize(),然后将得到的加载程序宿主通过LoaderHost属性放在IDsignerLoaderHost接口类型的host中,目的是为服务TypeProvider提供初始化信息,其余的代码参见注释。
看下面这些类关系图,展示了WorkflowLoader类及IDesignerLoaderHost接口的继承关系:


这样,一个简单的工作流加载器就完成了,接下来查看DesignerShell.cs的代码。

此外代码被分成了四个部分,我们从后向前看。CustomMessageFilter类继承WorkflowDesignerMessageFilter,前面说过,WorkflowDesignerMessageFilter类用于过滤或者自定义消息,也就是说我们可以在这里拦截到WinForm的消息传递,写上自己的处理代码,还可以决定是否允许其他事件响应这个消息。它的最简单应用就是处理鼠标和键盘响应事件,如下面重写的键子按下响应代码,我们对按下DELETE键的行为进行了控制,让它只能删除我们允许的节点:
- protected override bool OnKeyDown(KeyEventArgs eventArgs)
- {
- if (eventArgs.KeyCode == Keys.Delete)
- {
- ISelectionService selectionService = (ISelectionService)serviceProvider.GetService(typeof(ISelectionService));
- if (selectionService != null && selectionService.PrimarySelection is CodeActivity)
- {
- CodeActivity codeActivityComponent = (CodeActivity)selectionService.PrimarySelection;
- CompositeActivity parentActivity = codeActivityComponent.Parent;
- if (parentActivity != null)
- {
- parentActivity.Activities.Remove(codeActivityComponent);
- this.ParentView.Update();
- }
- loader.RemoveActivityFromDesigner(codeActivityComponent);
- }
- }
- return true;
- }
通过返回值的真假告诉运行时是否结束该事件的消息,这有点像给Form类重写ProcessCmdKey方法,下面这个类关系图列出了我们可以在CustomMessageFilter类中重写的方法,使用它可以防止用户做一定的限制。

然后是WorkflowDesignSurface类的描述,它继承DesignSurface类,在构造函数中加入了我们的菜单服务。
- // Design Surface is used to provide services.
- internal sealed class WorkflowDesignSurface : DesignSurface
- {
- internal WorkflowDesignSurface(IServiceProvider serviceProvider)
- {
- this.ServiceContainer.AddService(typeof(IMenuCommandService), new MenuCommandService(this.ServiceContainer));
- TypeProvider typeProvider = new TypeProvider(serviceProvider);
- typeProvider.AddAssemblyReference(typeof(string).Assembly.Location);
- this.ServiceContainer.AddService(typeof(ITypeProvider), typeProvider, true);
- }
- }
在WorkflowViewPanel类中,我们定义了几个字段:
- private WorkflowDesignSurface designSurface;
- private WorkflowView workflowView;
- private SequentialWorkflowActivity rootActivity;
- private IDesignerHost designerHost;
- private DesignerShell parent;
它要实现IServiceProvider接口,并且做为Panel显示在WinForm界面上。WorkflowView是必须的,用于设计器的界面显示,DesignerShell是窗体,用来指定此类WorkflowViewPanel加入到的父容器(窗口)。其他的,我们定义的WorkflowDesignSurface、顺序工作流的根节点、IDesignerHost接口,在LoadDefaultWorkflow()方法中对workflowView进行了初始化,在初始化过程中别忘记了将我们的CustomMessageFilter用WorkflowView.AddDesignerMessageFilter()方法加入workflowView。
- internal void LoadDefaultWorkflow()
- {
- Clear();
- this.designSurface = new WorkflowDesignSurface(this);
- WorkflowLoader loader = new WorkflowLoader();
- this.designSurface.BeginLoad(loader);
- this.designerHost = GetService(typeof(IDesignerHost)) as IDesignerHost;
- if (this.designerHost != null)
- {
- this.rootActivity = (SequentialWorkflowActivity)this.designerHost.CreateComponent(typeof(SequentialWorkflowActivity));
- this.rootActivity.Name = "Service1";
- CodeActivity codeActivity1 = new CodeActivity();
- this.rootActivity.Activities.Add(codeActivity1);
- this.designerHost.RootComponent.Site.Container.Add(codeActivity1);
- this.workflowView = new WorkflowView(this.designSurface as IServiceProvider);
- this.workflowView.AddDesignerMessageFilter(new CustomMessageFilter(this.designSurface as IServiceProvider, this.workflowView, loader));
- Controls.Add(this.workflowView);
- this.designerHost.Activate();
- ISelectionService selectionService = (ISelectionService)GetService(typeof(ISelectionService));
- if (selectionService != null)
- {
- selectionService.SelectionChanged += new EventHandler(OnSelectionChanged);
- IComponent[] selection = new IComponent[] { rootActivity };
- selectionService.SetSelectedComponents(selection);
- }
- }
- }
至此,一个基本的承载工作流设计器的框架完成。当然它不仅仅支持类顺序工作流的创建,只不过修改LoadDefaultWorkflow()或者添加一个新的方法,用代码添加状态机工作流。然后还可以导出XOML文档,剩下的工作就非常简单了。博客园里WXWinter制作了一个工作流设计器,大家可以去研究一下: