Creating State Machine Workflows – Step by Step Windows Application – Part II
In Part I, we learnt how to create a state machine workflow from a restaurant story. Now let’s learn how to host this state machine in a Windows Forms application. Since a workflow by its own cannot work, it needs to be hosted in some type of application which can be a Windows, Web, or a Windows Service.
Let’s start:
1. Open Visual Studio, Load the same State Machine Workflow project which we made in Part I.
2. Right click on the solution and add a new Windows Project to the solution. Name its as OrderSystemUser.
3. Right click on the OrderSystemUser project’s Reference and click Add Reference and add the OrderSystemStateMachine we made in Part I. Also add System.Workflow.Activities, System.Workflow.ComponentModel, and System.Workflow.Runtime.
4. Now add a Windows Form to it by the Name OrderManager.
5. To the OrderManager Form add a DataViewGrid to view the workflows and design rest of the form as shown in the picture below.
Order Number Textbox is for putting a new OrderNumber and Place Order is for Placing the Order.
Set Order Processed is for the chef to click when he completes making the coffee.
Set Goods Delivered is for the Waiter to click when he completes delivering the coffee.
Now let’s look more in to the code:
Define a Object Reference to WorkflowRuntime and OrderSystem (the IOrderSystem implementation class) at the top of your class
In your Form load method instantiate workflowRuntime and OrderSystem.
Also define one ExternalDataExchangeService and add this service to your workflowruntime object and to this ExternalDataExchangeService add your OrderSystem service.
Now add all event handlers for:
WorkflowCompleted, WorkflowTerminated, WorkflowIdled, WorkflowCreated and so on which ever you are interested in.
How to Add persistence service to it:
1. First of run the Sql Scripts provided by the Runtime on you Sql database. (Find Sql Scripts in c:WindowsMicrosoft.NETFramework<Framework Version>Windows Workflow FoundationSQLEN)
2. Once every thing completes successfully, generate a connection string to your Sql Database.
3. Add the following line to your code:
workflowRuntime.AddService(new SqlWorkflowPersistenceService(connString, true, new TimeSpan(0,2,0), new TimeSpan(0,0,20)));
4. Now your workflows are persistable in Sql Server.
Once you are done with this just add handler to your buttons:
private void btnPlace_Click(object sender, EventArgs e)
{
int orderNumber;
try
{
orderNumber = Convert.ToInt32(txtOrderNumber.Text);
}
catch (FormatException)
{
MessageBox.Show("Not a valid Number. Try Again");
return;
}
Dictionary<string, object> wfArgs = new Dictionary<string, object>();
wfArgs.Add("OrderNumber", orderNumber);
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(
typeof(StateMachineLab.OrderWorkflow), wfArgs);
workflowInstance.Start();
}
private void btnOrderProcessed_Click(object sender, EventArgs e)
{
Guid workflowId =
(Guid)dgvOrders.CurrentRow.Cells["WorkflowId"].Value;
int orderNumber =
Convert.ToInt32(dgvOrders.CurrentRow.Cells["OrderNumber"].Value);
OrderEventArgs oe = new OrderEventArgs(workflowId, orderNumber);
os.OnOrderProcessed(oe);
}
private void btnGoodsDelivered_Click(object sender, EventArgs e)
{
Guid workflowId =
(Guid)dgvOrders.CurrentRow.Cells["WorkflowId"].Value;
int orderNumber =
Convert.ToInt32(dgvOrders.CurrentRow.Cells["OrderNumber"].Value);
OrderEventArgs oe = new OrderEventArgs(workflowId, orderNumber);
os.OnGoodsDelivered(oe);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (workflowRuntime != null)
{
workflowRuntime.Dispose();
}
base.OnFormClosing(e);
}
And you are done with your workflow Windows Host Application. Go ahead and run it.
Here is the full OrderManager Class for your convenience
public partial class OrderManager : Form
{
public OrderManager()
{
InitializeComponent();
}
WorkflowRuntime workflowRuntime;
OrderSystem os = new OrderSystem();
private void OrderManager_Load(object sender, EventArgs e)
{
workflowRuntime = new WorkflowRuntime();
ExternalDataExchangeService exchangeService = new ExternalDataExchangeService();
workflowRuntime.AddService(exchangeService);
string connString = string.Format("Initial Catalog={0};Data Source={1};
Integrated Security={2};","master", @"Sunil-LaptopSQLEXPRESS", "SSPI");
workflowRuntime.AddService(new SqlWorkflowPersistenceService(connString, true,
new TimeSpan(0,2,0), new TimeSpan(0,0,20)));
exchangeService.AddService(os);
workflowRuntime.WorkflowCompleted += new EventHandler(workflowRuntime_WorkflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler(workflowRuntime_WorkflowTerminated);
workflowRuntime.WorkflowIdled += new EventHandler(workflowRuntime_WorkflowIdled);
workflowRuntime.WorkflowCreated += new EventHandler(workflowRuntime_WorkflowCreated);
workflowRuntime.WorkflowPersisted += new EventHandler(workflowRuntime_WorkflowPersisted);
workflowRuntime.WorkflowLoaded += new EventHandler(workflowRuntime_WorkflowLoaded);
workflowRuntime.WorkflowUnloaded += new EventHandler(workflowRuntime_WorkflowUnloaded);
}
void workflowRuntime_WorkflowUnloaded(object sender, WorkflowEventArgs e)
{
StateMachineWorkflowInstance se = new StateMachineWorkflowInstance(workflowRuntime,
e.WorkflowInstance.InstanceId);
this.UpdateUI(e.WorkflowInstance.InstanceId, se.CurrentStateName, "Unloaded");
}
void workflowRuntime_WorkflowLoaded(object sender, WorkflowEventArgs e)
{
StateMachineWorkflowInstance se = new StateMachineWorkflowInstance(workflowRuntime,
e.WorkflowInstance.InstanceId);
this.UpdateUI(e.WorkflowInstance.InstanceId, se.CurrentStateName, "Loaded");
}
void workflowRuntime_WorkflowPersisted(object sender, WorkflowEventArgs e)
{
try
{
StateMachineWorkflowInstance se = new StateMachineWorkflowInstance(workflowRuntime,
e.WorkflowInstance.InstanceId);
this.UpdateUI(e.WorkflowInstance.InstanceId, se.CurrentStateName, "Persisted");
}
catch (InvalidOperationException)
{
this.UpdateUI(e.WorkflowInstance.InstanceId, "Completed", "Persisted");
}
}
void workflowRuntime_WorkflowCreated(object sender, WorkflowEventArgs e)
{
this.UpdateUI(e.WorkflowInstance.InstanceId, "Order Placed", "Created");
Guid workflowId = (Guid)dgvOrders.Rows[dgvOrders.Rows.Count-1].Cells["WorkflowId"].Value;
int orderNumber = Convert.ToInt32(dgvOrders.Rows[dgvOrders.Rows.Count-1].Cells
["OrderNumber"].Value);
OrderEventArgs oe = new OrderEventArgs(workflowId, orderNumber);
os.OnOrderReceived(oe);
}
void workflowRuntime_WorkflowIdled(object sender, WorkflowEventArgs e)
{
StateMachineWorkflowInstance se = new StateMachineWorkflowInstance(workflowRuntime,
e.WorkflowInstance.InstanceId);
this.UpdateUI(e.WorkflowInstance.InstanceId, se.CurrentStateName, "Idled");
}
void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
StateMachineWorkflowInstance se = new StateMachineWorkflowInstance(workflowRuntime,
e.WorkflowInstance.InstanceId);
this.UpdateUI(e.WorkflowInstance.InstanceId, se.CurrentStateName, "Terminated");
}
void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
this.UpdateUI(e.WorkflowInstance.InstanceId, "Completed", "Completed");
}
delegate void UIUpdater(Guid instanceId, string status, string workflowStatus);
void UpdateUI(Guid instanceId, string status, string workflowStatus)
{
if (this.InvokeRequired)
{
this.Invoke(new UIUpdater(UpdateUI), new object[] { instanceId, status,
workflowStatus});
}
else
{
bool found = false;
for (int i = 0; i < dgvOrders.Rows.Count; i++)
{
if (dgvOrders.Rows[i].Cells["WorkflowId"].Value.ToString() ==
instanceId.ToString())
{
found = true;
dgvOrders.Rows[i].Cells["CurrentState"].Value = status;
dgvOrders.Rows[i].Cells["WorkflowStatus"].Value = workflowStatus;
System.Threading.Thread.Sleep(2000);
}
}
if (!found)
{
dgvOrders.Rows.Add(new object[] { txtOrderNumber.Text, instanceId, status,
workflowStatus });
}
}
}
private void btnPlace_Click(object sender, EventArgs e)
{
int orderNumber;
try
{
orderNumber = Convert.ToInt32(txtOrderNumber.Text);
}
catch (FormatException)
{
MessageBox.Show("Not a valid Number. Try Again");
return;
}
Dictionary wfArgs = new Dictionary();
wfArgs.Add("OrderNumber", orderNumber);
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof
(StateMachineLab.OrderWorkflow), wfArgs);
workflowInstance.Start();
}
private void btnOrderProcessed_Click(object sender, EventArgs e)
{
Guid workflowId = (Guid)dgvOrders.CurrentRow.Cells["WorkflowId"].Value;
int orderNumber = Convert.ToInt32(dgvOrders.CurrentRow.Cells["OrderNumber"].Value);
OrderEventArgs oe = new OrderEventArgs(workflowId, orderNumber);
os.OnOrderProcessed(oe);
}
private void btnGoodsDelivered_Click(object sender, EventArgs e)
{
Guid workflowId = (Guid)dgvOrders.CurrentRow.Cells["WorkflowId"].Value;
int orderNumber = Convert.ToInt32(dgvOrders.CurrentRow.Cells["OrderNumber"].Value);
OrderEventArgs oe = new OrderEventArgs(workflowId, orderNumber);
os.OnGoodsDelivered(oe);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (workflowRuntime != null)
{
workflowRuntime.Dispose();
}
base.OnFormClosing(e);
}
}



