自定义工作流设计器-飞外

第一部分
基础工作,建立代码结构框架
1.创建项目WorkflowDesignerControl
1.1:WorkflowView和DesignSurface,workflowView是微软提供的工作流设计API,所有的操作都要在上面进行;DesignSurface是WorkflowView的设计时支持
WorkflowView需要从中获得一些资源。
1.2:工具箱和属性窗口也要放到WorkflowDesignerControl中

Toolboxtoolbox=newToolbox(this);
this.propertyGridSplitter.Panel1.Controls.Add(toolbox);
toolbox.Dock=DockStyle.Fill;
toolbox.BackColor=BackColor;
toolbox.Font=WorkflowTheme.CurrentTheme.AmbientTheme.Font;

2、在WorkflowDesignerControl加入ActivityLibrary的引用
DesignerHostingApplication中加入ActivityLibrary和WorkflowDesignerControl的引用
DesignerHostingApplication中加入System.Design,System.Drawing.Design,System.Workflow.Activities,System.Workflow.ComponentModel的引用

3.所有自定义的Activitie放入一个project,这样基本的代码架构就建立好了。

第二部分
winfrom中集成可编程的wf设计器
1.首先创建DesignerHostingApplication的解决方案
重命名from1.cs为DesignerShell.cs,并加入工具条
Toolstrip
Name: toolStrip
GripStyle: Hidden
RenderMode: System
Ensure the Dock property is set to Top
将WorkflowDesignControl加入toolStrip
Name: workflowDesignerControl
Dock: Fill
2.使设计器支持zooming

publicvoidProcessZoom(intzoomFactor)
{
this.workflowView.Zoom=zoomFactor;
this.workflowView.Update();
}

2.1:使用预定义的zoom levels 25%, 100% and 200%,zoom设计器in or out
在DesignerShell.cs中调用ProcessZoom

privatevoidzoomDropDownMenuItem_Click(objectsender,EventArgse)
{
if(senderisToolStripMenuItem)
{
ToolStripMenuItemmenuItem=(ToolStripMenuItem)sender;

intzoomFactor=0;

boolresult=Int32.TryParse(menuItem.Tag.ToString(),outzoomFactor);

if(result)
{
this.workflowDesignerControl.ProcessZoom(zoomFactor);
}
}
}

2.2:在toolstrip中加入dropdownButton并定义属性:
Name: zoomDropDown
DisplayStyle: Text
Text: Zoom
编辑DropDownButton,加入三个Item对应Text分别为:25% ,100%,200%
每个item分别定义name和tag,分别为;mni25PercentZoom,25;mni100PercentZoom,100;mni200PercentZoom,200;
并定义click事件

3如何在wf中添加Activities(能在workflow中添加或删除Activitie,并能修改activity的属性)
1.在WorkflowDesignerControl.cs中添加自定义的工具箱

publicWorkflowDesignerControl()
{
InitializeComponent();

Toolboxtoolbox=newToolbox(this);
this.propertyGridSplitter.Panel1.Controls.Add(toolbox);
toolbox.Dock=DockStyle.Fill;
toolbox.BackColor=BackColor;
toolbox.Font=WorkflowTheme.CurrentTheme.AmbientTheme.Font;

WorkflowTheme.CurrentTheme.ReadOnly=false;
WorkflowTheme.CurrentTheme.AmbientTheme.ShowConfigErrors=false;
WorkflowTheme.CurrentTheme.ReadOnly=true;

this.propertyGrid.BackColor=BackColor;
this.propertyGrid.Font=WorkflowTheme.CurrentTheme.AmbientTheme.Font;

AppDomain.CurrentDomain.AssemblyResolve+=newResolveEventHandler(CurrentDomain_AssemblyResolve);
}


2.使WorkflowDesignerControl中的工具箱运行时加载Activitie(可以是自定义的)
在ToolBoxItems.txt中注册
例如我有个自定义的basic Activitie,如果要把这个Activiti加入工具箱中可以在ToolBoxItems.txt中这样写
ActivityLibrary.MessageActivity, ActivityLibrary
运行后就可以看到工具箱中有MessageActivity
3.在设计器中选择Activity并编辑属性
在WorkflowDesignerControl.cs中注册事件SelectionChanged

ISelectionServiceselectionService=GetService(typeof(ISelectionService))asISelectionService;

if(selectionService!=null)
{
selectionService.SelectionChanged+=newEventHandler(OnSelectionChanged);
}

实现OnSelectionChanged事件

privatevoidOnSelectionChanged(objectsender,EventArgse)
{
ISelectionServiceselectionService=GetService(typeof(ISelectionService))asISelectionService;

if(selectionService!=null)
{
this.propertyGrid.SelectedObjects=newArrayList(selectionService.GetSelectedComponents()).ToArray();
}
}

这样,当WorkflowDesignerControl运行时,在右边下角会显示会选择所选择Activity的属性
4.使工作流设计器支持删除功能
在WorkflowDesignerControl.cs中加入方法DeleteSelected,支持在设计器中删除Activity.

publicvoidDeleteSelected()
{
ISelectionServiceselectionService=(ISelectionService)this.GetService(typeof(ISelectionService));

if(selectionService!=null)
{
if(selectionService.PrimarySelectionisActivity)
{
Activityactivity=(Activity)selectionService.PrimarySelection;

if(activity.Name!=this.WorkflowName)
{
activity.Parent.Activities.Remove(activity);
this.workflowView.Update();
}
}
}
}


在界面工具条上添加Button,属性如下
Name: btnDelete
DisplayStyle: Text
Text: “Remove Activity”
在click事件中写入对WorkflowDesignerControl中DeleteSelected方法的调用

privatevoidbtnDelete_Click(objectsender,EventArgse)
{
this.workflowDesignerControl.DeleteSelected();
}

5.使工作流设计器实现保存,编译,运行和打开已保存工作流的功能。
在工具条上再次添加按钮名称分别为打开,保存,编译和运行,在这些按钮的事件触发程序中调用workflowDesignerControl中已经定义好的方法
5.1在workflowDesignerControl中添加方法,加载已经存在的工作流模板文件

publicvoidLoadExistingWorkflow()
{
OpenFileDialogopenFileDialog=newOpenFileDialog();
openFileDialog.Filter="xomlfiles(*.xoml)|*.xoml|Allfiles(*.*)|*.*";
openFileDialog.FilterIndex=1;
openFileDialog.RestoreDirectory=true;

if(openFileDialog.ShowDialog()==DialogResult.OK)
{
using(XmlReaderxmlReader=XmlReader.Create(openFileDialog.FileName))
{
WorkflowMarkupSerializerserializer=newWorkflowMarkupSerializer();
this.workflow=(SequentialWorkflowActivity)serializer.Deserialize(xmlReader);
this.LoadWorkflow();

this.XomlFile=openFileDialog.FileName;
this.Text="DesignerHostingSample--["+openFileDialog.FileName+"]";
}
}
}

5.2:保存功能实现

privatevoidSaveFile()
{
if(this.XomlFile.Length!=0)
{
this.SaveExistingWorkflow(this.XomlFile);
}
else
{
SaveFileDialogsaveFileDialog=newSaveFileDialog();
saveFileDialog.Filter="xomlfiles(*.xoml)|*.xoml|Allfiles(*.*)|*.*";
saveFileDialog.FilterIndex=1;
saveFileDialog.RestoreDirectory=true;

if(saveFileDialog.ShowDialog()==DialogResult.OK)
{
this.SaveExistingWorkflow(saveFileDialog.FileName);
this.Text="DesignerHostingSample--["+saveFileDialog.FileName+"]";
}
}
}

5.3:编译功能实现

publicboolCompile()
{
returnthis.Compile(true);
}

publicboolCompile(boolshowMessage)
{
if(!this.Save(false))
{
returnfalse;
}

if(!File.Exists(this.XomlFile))
{
MessageBox.Show(this,"Cannotlocatexomlfile:"+Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location),XomlFile),this.Text,MessageBoxButtons.OK,MessageBoxIcon.Error);
returnfalse;
}

boolcompileOK=true;

Cursorcursor=this.Cursor;
this.Cursor=Cursors.WaitCursor;

try
{
//Compiletheworkflow
String[]assemblyNames={AdditionalAssembies};
WorkflowCompilercompiler=newWorkflowCompiler();
WorkflowCompilerParametersparameters=newWorkflowCompilerParameters(assemblyNames);
parameters.LibraryPaths.Add(Path.GetDirectoryName(typeof(ActivityLibrary.MessageActivity).Assembly.Location));
parameters.GenerateInMemory=true;
WorkflowCompilerResultscompilerResults=compiler.Compile(parameters,this.XomlFile);

inMemoryAssembly=compilerResults.CompiledAssembly;

StringBuildererrors=newStringBuilder();

foreach(CompilerErrorcompilerErrorincompilerResults.Errors)
{
errors.Append(compilerError.ToString()+'\n');
}

if(errors.Length!=0)
{
MessageBox.Show(this,errors.ToString(),this.Text,MessageBoxButtons.OK,MessageBoxIcon.Error);
compileOK=false;
}
elseif(showMessage)
{
MessageBox.Show(this,"Workflowcompiledsuccessfully.Compiledassembly:\n"+compilerResults.CompiledAssembly.GetName(),this.Text,MessageBoxButtons.OK,MessageBoxIcon.Information);
}
}
finally
{
this.Cursor=cursor;
}

returncompileOK;
}


注意:
parameters.GenerateInMemory = true;这时assembly只在内存中生成,不输出到文件
如果要生成.dll文件,需要修改为
parameters.GenerateInMemory = false;
parameters.OutputAssembly = string.Format("{0}.dll", this.WorkflowName);

6:在工具条的事件中调用这些方法
测试一下
如果还没有保存,运行后会先保存并编译。

补传本文所示代码:demo