The ability to create portable, XOML-only workflows that you can store and transmit if it were simple data and then activate in a host application is pretty powerful. When you consider that you can use sequential workflows to drive thousands of different kinds of processes and you can use state machine workflows to model the most complex human-machine interaction workflows imaginabale - the ability to take those workflows and make them a distributable component is mind-blowingly cool.
Let's say you're designing an application that is a Windows Forms app that displays different forms at different times based on the logic and information contained within a workflow. To do this, you need to create an application that hosts the WF runtime, but you also need to create the data exchange interface and the data exchange class. The portable workflows, while they may not have any code-beside files, will reference the fully qualified namespace name of the Interface. The main issue here isn't a technical one its a question of design. How do you design a codeless workflow so that it can dictate what screen the host application is displaying at any given time? To keep things as loosely coupled as possible, I recommend adding an event to the Data Exchange service that can be fired by the workflow called something like OnScreenTransitionRequest. This event will signal the host application that the workflow would like the screen to transition from whatever is currently displayed to the indicated screen, using an indicated effect like a wipe, swirl, slide, or fade.
Here's the ISampleDataExchange interface and the SampleDataExchange class that implements the interface:
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow;
using System.Workflow.Activities;
namespace WorkflowConsoleApplication4
{
public delegate void SignalTransitionDelegate(string newXaml, string effect);
[ExternalDataExchange]
public interface ISampleDataExchange
{
event SignalTransitionDelegate OnSignalTransition;
void SignalPageTransition(string newPage, string effect);
}
public class SampleDataExchange : ISampleDataExchange
{
#region ISampleDataExchange Members
public event SignalTransitionDelegate OnSignalTransition;
public void SignalPageTransition(string newPage, string effect)
{
if (OnSignalTransition != null)
OnSignalTransition(newXaml, effect);
}
#endregion
}
}
The next thing you need to do is to be able to load the Workflow XOML from disk and hand it to the workflow runtime, as well as attach the Data Exchange Service in such a way that the workflow can properly find it:
#region Using directives
using System;
using System.Xml;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Runtime.Hosting;
using System.Workflow.ComponentModel.Compiler;
#endregion
namespace WorkflowConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted +=
delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();};
workflowRuntime.WorkflowTerminated +=
delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
XmlReader xr = XmlReader.Create(@"..\..\SampleWorkflowStandalone.xoml");
try
{
ExternalDataExchangeService eds = new ExternalDataExchangeService();
workflowRuntime.AddService(eds);
SampleDataExchange sampleService = new SampleDataExchange();
eds.AddService(sampleService);
WorkflowInstance instance = workflowRuntime.CreateWorkflow(xr);
sampleService.OnSignalTransition +=
new SignalTransitionDelegate(sampleService_OnSignalTransition);
instance.Start();
waitHandle.WaitOne();
Console.WriteLine("Workflow done.");
Console.ReadLine();
}
catch (WorkflowValidationFailedException ex)
{
StringBuilder errors = new StringBuilder();
foreach (ValidationError error in ex.Errors)
{
errors.AppendLine(error.ToString());
}
Console.WriteLine(errors.ToString());
Console.ReadLine();
}
}
}
static void sampleService_OnSignalTransition(string newPage, string effect)
{
Console.WriteLine("Workflow signaled page transition to {0} using {1} effect.",
newXaml, effect);
}
}
}
And finally here is the standalone workflow XOML that makes use of the data exchange interface to call the SignalPageTransition method after a time delay ( don't try and copy the XOML from here, I've added line breaks that will screw up the parser to make it easier to read on the blog):
<SequentialWorkflowActivity x:Name="SampleWorkflow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow"
xmlns:sampleapp="clr-namespace:WorkflowConsoleApplication4;
Assembly=WorkflowConsoleApplication4">
<DelayActivity TimeoutDuration="00:00:05" x:Name="pause1" />
<CallExternalMethodActivity x:Name="ShowNewPage"
InterfaceType="{x:Type sampleapp:ISampleDataExchange}"
MethodName="SignalPageTransition">
<CallExternalMethodActivity.ParameterBindings>
<WorkflowParameterBinding ParameterName="newPage">
<WorkflowParameterBinding.Value>
<ns0:String
xmlns:ns0="clr-namespace:System;Assembly=mscorlib,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">secondpage.xaml</ns0:String>
</WorkflowParameterBinding.Value>
</WorkflowParameterBinding>
<WorkflowParameterBinding ParameterName="effect">
<WorkflowParameterBinding.Value>
<ns0:String
xmlns:ns0="clr-namespace:System;Assembly=mscorlib,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">supercool-fadeorama</ns0:String>
</WorkflowParameterBinding.Value>
</WorkflowParameterBinding>
</CallExternalMethodActivity.ParameterBindings>
</CallExternalMethodActivity>
</SequentialWorkflowActivity>
And finally, here's the output of the application that dynamically loads the XOML workflow and hooks it up to a known data exchange service:
Workflow signaled page transition to secondpage.xaml using supercool-fadeorama effect.
Workflow done.
I think anybody reading this blog is probably going to realize the potential use for this technology and hopefully you'll go out and start writing some amazing code right now.
I mean it. Start coding.
Now.tags: xomlonly xoml workflow wf vista netfx3
links: digg this del.icio.us technorati reddit