Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this task, you use the IOrderingService interface that you created in Task 2 by implementing the interface in the Simple Order Form host application to create a local service for communicating with the running workflow.
When a user submits a new order, the host application raises the NewOrder event, which is processed by the OrderProcessingWorkflow. The OrderProcessingWorkflow calls the ItemStatusUpdate method of the IOrderingService. Then it calls that method in the host application to update the user interface.
Note
Although you are encouraged to follow the exercises in a linear manner, it is not required. You can start this exercise by opening the sample project and proceeding to the steps in the following section.
Implementing the Interface
Follow these steps to implement the interface that you created in the previous task.
To implement the IOrderingService interface
Add the following component references to the StateMachineHost project:
System.Workflow.Runtime
System.Workflow.Activities
System.Workflow.ComponentModel
Add the following references to the Mainform.cs file in the StateMachineHost project:
System.Workflow.Activities
System.Workflow.ComponentModel
System.Workflow.Runtime
StateMachineWorkflow
Imports System.Workflow.Activities Imports System.Workflow.ComponentModel Imports System.Workflow.Runtime Imports StateMachineWorkflowusing System.Workflow.Activities; using System.Workflow.ComponentModel; using System.Workflow.Runtime; using StateMachineWorkflow;In the Mainform source file in the StateMachineHost project, derive the
MainFormclass from theIOrderingServiceinterface so that the main Form itself can serve as the service class for communicating with the workflow.Public Class MainForm : Inherits Form : Implements IOrderingService End Classpublic class MainForm : Form, IOrderingServiceIn the
MainFormclass, create an array of type string that contains some inventory items used for creating orders.Private inventoryItems() As String = { "Apple", "Orange", "Banana", "Pear", "Watermelon", "Grapes" }private string[] inventoryItems = { "Apple", "Orange", "Banana", "Pear", "Watermelon", "Grapes" };In the
MainFormclass, create a delegate to handle item status updates.Private Delegate Sub ItemStatusUpdateDelegate(orderId As Guid, newStatus As String)private delegate void ItemStatusUpdateDelegate(Guid orderId, string newStatus);In the
MainFormclass, create a new public method namedItemStatusUpdatethat accepts a Guid namedorderIdand a String namednewStatusas parameters. This method is used to change the order status, which has the effect of changing the currently active state in the workflow.Public Sub ItemStatusUpdate(ByVal orderId As Guid, ByVal newStatus As String) Implements IOrderingService.ItemStatusUpdate End Subpublic void ItemStatusUpdate(Guid orderId, string newStatus) { }In the
MainFormclass, create a private WorkflowRuntime field namedworkflowRuntimeand initialize it tonull(Nothingin Visual Basic).In the
MainFormclass, create an ExternalDataExchangeService field namedexchangeServiceand initialize it tonull(Nothingin Visual Basic). This service serves as a container for the custom local data exchange service that is used to communicate with the workflow.private WorkflowRuntime workflowRuntime = null; private ExternalDataExchangeService exchangeService = null;In the
MainFormclass, create a private genericEventHandlerof theNewOrderEventArgstype namednewOrderEvent. This event is raised to create a new order, which has the effect of starting a new workflow instance to track the order.In the
MainFormclass, create a public genericEventHandlerevent property of theNewOrderEventArgstype namedNewOrder.In the
NewOrderevent property, create an Add and Remove accessor to add and remove event handlers from thenewOrderEventevent.private event EventHandler<NewOrderEventArgs> newOrderEvent; public event EventHandler<NewOrderEventArgs> NewOrder { add { newOrderEvent += value; } remove { newOrderEvent -= value; } }Create a private Dictionary field of type
string, List<string>calledorderHistory. This field is used to track the running workflow instances.Private orderHistory as Dictionary(Of String, List(Of String))private Dictionary<string, List<string>> orderHistory;Initialize the
orderHistoryobject in the constructor of theMainformclass to a new instance of aDictionaryobject.orderHistory = new Dictionary(Of String, List(Of String))orderHistory = new Dictionary<string, List<string>>();In the
MainFormconstructor, create an instance of the WorkflowRuntime object after the code that creates an instance of theorderHistoryobject.Create an instance of the ExternalDataExchangeService object after the code created in the previous step.
Add the ExternalDataExchangeService object to the WorkflowRuntime services by calling the AddService method and passing the
exchangeServiceobject as a parameter.Add the
MainFormobject to the ExternalDataExchangeService services by calling the AddService method and passing thethis(Mein Visual Basic) object as a parameter. This allows theMainformobject itself to act as a messaging service for the workflow run-time object.Start the WorkflowRuntime by calling the StartRuntime method.
this.workflowRuntime = new WorkflowRuntime(); this.exchangeService = new ExternalDataExchangeService(); this.workflowRuntime.AddService(this.exchangeService); this.exchangeService.AddService(this); this.workflowRuntime.StartRuntime();
Creating the Windows Form Controls
Add the following line to the Mainform.Designer file.
Imports System.Windows.Forms using System.Windows.Forms;Add the following code to the Mainform class in the Mainform.Designer file. This code serves as the logic for the user interface for controlling the application. Because the focus of this tutorial is on creating and using workflows rather than Windows Forms, it is not necessary to go into details about the workings of the form. Please see MSDN for information on the operation of Windows Forms.
Private label1 As System.Windows.Forms.Label Private itemsList As System.Windows.Forms.ComboBox Private label2 As System.Windows.Forms.Label Private itemQuantity As System.Windows.Forms.NumericUpDown Private submitButton As System.Windows.Forms.Button Private groupBox1 As System.Windows.Forms.GroupBox Private ordersIdList As System.Windows.Forms.ComboBox Private label3 As System.Windows.Forms.Label Private orderStatus As System.Windows.Forms.TextBox Private label4 As System.Windows.Forms.Labelprivate System.Windows.Forms.Label label1; private System.Windows.Forms.ComboBox itemsList; private System.Windows.Forms.Label label2; private System.Windows.Forms.NumericUpDown itemQuantity; private System.Windows.Forms.Button submitButton; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.ComboBox ordersIdList; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox orderStatus; private System.Windows.Forms.Label label4;Replace the code in the
InitializeComponentmethod in the Mainform.Designer.cs or Mainform.Designer.vb file (hidden by default in the “Windows Form Designer Code” section) with the following code.Me.label1 = New System.Windows.Forms.Label() Me.itemsList = New System.Windows.Forms.ComboBox() Me.label2 = New System.Windows.Forms.Label() Me.itemQuantity = New System.Windows.Forms.NumericUpDown() Me.submitButton = New System.Windows.Forms.Button() Me.groupBox1 = New System.Windows.Forms.GroupBox() Me.orderStatus = New System.Windows.Forms.TextBox() Me.label4 = New System.Windows.Forms.Label() Me.ordersIdList = New System.Windows.Forms.ComboBox() Me.label3 = New System.Windows.Forms.Label() (Me.itemQuantity As _ System.ComponentModel.ISupportInitialize).BeginInit() Me.groupBox1.SuspendLayout() Me.SuspendLayout() ‘ ‘ label1 ‘ Me.label1.AutoSize = true Me.label1.Location = New System.Drawing.Point(12, 9) Me.label1.Name = "label1" Me.label1.Size = New System.Drawing.Size(23, 13) Me.label1.TabIndex = 0 Me.label1.Text = "Item" ‘ ‘ Me.itemsList ‘ Me.itemsList.DropDownStyle = ComboBoxStyle.DropDownList Me.itemsList.FormattingEnabled = true Me.itemsList.Location = New System.Drawing.Point(13, 26) Me.itemsList.Name = "itemsList" Me.itemsList.Size = New System.Drawing.Size(161, 21) Me.itemsList.TabIndex = 0 ‘ ‘ label2 ‘ Me.label2.AutoSize = true Me.label2.Location = New System.Drawing.Point(12, 50) Me.label2.Name = "label2" Me.label2.Size = New System.Drawing.Size(42, 13) Me.label2.TabIndex = 2 Me.label2.Text = "Quantity" ‘ ‘ Me.itemQuantity ‘ Me.itemQuantity.Location = New System.Drawing.Point(13, 67) Me.itemQuantity.Minimum = New decimal(New int[] { _ 1, _ 0, _ 0, _ 0}) Me.itemQuantity.Name = "itemQuantity" Me.itemQuantity.Size = New System.Drawing.Size(80, 20) Me.itemQuantity.TabIndex = 1 Me.itemQuantity.Value = New decimal(New int[] { _ 1, _ 0, _ 0, _ 0}) ‘ ‘ Me.submitButton ‘ Me.submitButton.Location = New System.Drawing.Point(99, 64) Me.submitButton.Name = "submitButton" Me.submitButton.Size = New System.Drawing.Size(100, 23) Me.submitButton.TabIndex = 2 Me.submitButton.Text = "Submit Order" AddHandler Me.submitButton.Click, AddressOf Me.submitButton_Click ‘ ‘ groupBox1 ‘ Me.groupBox1.Controls.Add(Me.orderStatus) Me.groupBox1.Controls.Add(Me.label4) Me.groupBox1.Controls.Add(Me.ordersIdList) Me.groupBox1.Controls.Add(Me.label3) Me.groupBox1.Location = New System.Drawing.Point(13, 93) Me.groupBox1.Name = "groupBox1" Me.groupBox1.Size = New System.Drawing.Size(247, 193) Me.groupBox1.TabIndex = 3 Me.groupBox1.TabStop = false Me.groupBox1.Text = "Order Status" ‘ ‘ orderStatus ‘ Me.orderStatus.Location = New System.Drawing.Point(7, 82) Me.orderStatus.Multiline = true Me.orderStatus.Name = "orderStatus" Me.orderStatus.ReadOnly = true Me.orderStatus.Size = New System.Drawing.Size(230, 105) Me.orderStatus.TabIndex = 3 ‘ ‘ label4 ‘ Me.label4.AutoSize = true Me.label4.Location = New System.Drawing.Point(8, 65) Me.label4.Name = "label4" Me.label4.Size = New System.Drawing.Size(35, 13) Me.label4.TabIndex = 2 Me.label4.Text = "History" ‘ ‘ ordersIdList ‘ Me.ordersIdList.DropDownStyle = ComboBoxStyle.DropDownList Me.ordersIdList.FormattingEnabled = true Me.ordersIdList.Location = New System.Drawing.Point(7, 37) Me.ordersIdList.Name = "ordersIdList" Me.ordersIdList.Size = New System.Drawing.Size(230, 21) Me.ordersIdList.TabIndex = 0 AddHandler Me.ordersIdList.SelectedIndexChanged, AddressOf Me.ordersIdList_SelectedIndexChanged ‘ ‘ label3 ‘ Me.label3.AutoSize = true Me.label3.Location = New System.Drawing.Point(7, 20) Me.label3.Name = "label3" Me.label3.Size = New System.Drawing.Size(41, 13) Me.label3.TabIndex = 0 Me.label3.Text = "Order Id" ‘ ‘ MainForm ‘ Me.AcceptButton = Me.submitButton Me.AutoScaleDimensions = New System.Drawing.SizeF(6F, 13F) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(272, 298) Me.Controls.Add(Me.groupBox1) Me.Controls.Add(Me.submitButton) Me.Controls.Add(Me.itemQuantity) Me.Controls.Add(Me.label2) Me.Controls.Add(Me.itemsList) Me.Controls.Add(Me.label1) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D Me.MaximizeBox = false Me.MinimizeBox = false Me.Name = "MainForm" Me.Text = "Simple Order Form" (Me.itemQuantity As System.ComponentModel.ISupportInitialize).EndInit() Me.groupBox1.ResumeLayout(false) Me.groupBox1.PerformLayout() Me.ResumeLayout(false) Me.PerformLayout()this.label1 = new System.Windows.Forms.Label(); this.itemsList = new System.Windows.Forms.ComboBox(); this.label2 = new System.Windows.Forms.Label(); this.itemQuantity = new System.Windows.Forms.NumericUpDown(); this.submitButton = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.orderStatus = new System.Windows.Forms.TextBox(); this.label4 = new System.Windows.Forms.Label(); this.ordersIdList = new System.Windows.Forms.ComboBox(); this.label3 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.itemQuantity)).BeginInit(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(12, 9); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(23, 13); this.label1.TabIndex = 0; this.label1.Text = "Item"; // // this.itemsList // this.itemsList.DropDownStyle = ComboBoxStyle.DropDownList; this.itemsList.FormattingEnabled = true; this.itemsList.Location = new System.Drawing.Point(13, 26); this.itemsList.Name = "itemsList"; this.itemsList.Size = new System.Drawing.Size(161, 21); this.itemsList.TabIndex = 0; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(12, 50); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(42, 13); this.label2.TabIndex = 2; this.label2.Text = "Quantity"; // // this.itemQuantity // this.itemQuantity.Location = new System.Drawing.Point(13, 67); this.itemQuantity.Minimum = new decimal(new int[] { 1, 0, 0, 0}); this.itemQuantity.Name = "itemQuantity"; this.itemQuantity.Size = new System.Drawing.Size(80, 20); this.itemQuantity.TabIndex = 1; this.itemQuantity.Value = new decimal(new int[] { 1, 0, 0, 0}); // // this.submitButton // this.submitButton.Location = new System.Drawing.Point(99, 64); this.submitButton.Name = "submitButton"; this.submitButton.Size = new System.Drawing.Size(100, 23); this.submitButton.TabIndex = 2; this.submitButton.Text = "Submit Order"; this.submitButton.Click += new System.EventHandler(this.submitButton_Click); // // groupBox1 // this.groupBox1.Controls.Add(this.orderStatus); this.groupBox1.Controls.Add(this.label4); this.groupBox1.Controls.Add(this.ordersIdList); this.groupBox1.Controls.Add(this.label3); this.groupBox1.Location = new System.Drawing.Point(13, 93); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(247, 193); this.groupBox1.TabIndex = 3; this.groupBox1.TabStop = false; this.groupBox1.Text = "Order Status"; // // orderStatus // this.orderStatus.Location = new System.Drawing.Point(7, 82); this.orderStatus.Multiline = true; this.orderStatus.Name = "orderStatus"; this.orderStatus.ReadOnly = true; this.orderStatus.Size = new System.Drawing.Size(230, 105); this.orderStatus.TabIndex = 3; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(8, 65); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(35, 13); this.label4.TabIndex = 2; this.label4.Text = "History"; // // ordersIdList // this.ordersIdList.DropDownStyle = ComboBoxStyle.DropDownList; this.ordersIdList.FormattingEnabled = true; this.ordersIdList.Location = new System.Drawing.Point(7, 37); this.ordersIdList.Name = "ordersIdList"; this.ordersIdList.Size = new System.Drawing.Size(230, 21); this.ordersIdList.TabIndex = 0; this.ordersIdList.SelectedIndexChanged += new System.EventHandler(this.ordersIdList_SelectedIndexChanged); // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(7, 20); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(41, 13); this.label3.TabIndex = 0; this.label3.Text = "Order Id"; // // MainForm // this.AcceptButton = this.submitButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(272, 298); this.Controls.Add(this.groupBox1); this.Controls.Add(this.submitButton); this.Controls.Add(this.itemQuantity); this.Controls.Add(this.label2); this.Controls.Add(this.itemsList); this.Controls.Add(this.label1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "MainForm"; this.Text = "Simple Order Form"; ((System.ComponentModel.ISupportInitialize)(this.itemQuantity)).EndInit(); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout();Initialize the
itemsListcombo box in the constructor for the Mainform class.Foreach item As String In Me.inventoryItems) Me.itemsList.Items.Add(item) Next Me.itemsList.SelectedIndex = 0foreach (string item in this.inventoryItems) { this.itemsList.Items.Add(item); } this.itemsList.SelectedIndex = 0;Add the following methods to the MainForm class in the Mainform file.
Private Function GetOrderHistory(orderId As String) As String ‘ Retrieve the order status Dim itemHistory As New StringBuilder() Foreach status As String In Me.orderHistory(orderId) itemHistory.Append(status) itemHistory.Append(Environment.NewLine) End Foreach Return itemHistory.ToString() End Function Private Sub submitButton_Click(sender As object, e As EventArgs) End Sub Private Sub ordersIdList_SelectedIndexChanged(sender as object, e As EventArgs) this.orderStatus.Text = GetOrderHistory(this.ordersIdList.SelectedItem.ToString()) End Subprivate string GetOrderHistory(string orderId) { // Retrieve the order status StringBuilder itemHistory = new StringBuilder(); foreach (string status in this.orderHistory[orderId]) { itemHistory.Append(status); itemHistory.Append(Environment.NewLine); } return itemHistory.ToString(); } private void submitButton_Click(object sender, EventArgs e) { } private void ordersIdList_SelectedIndexChanged(object sender, EventArgs e) { this.orderStatus.Text = GetOrderHistory(this.ordersIdList.SelectedItem.ToString()); }
Enabling New Order Processing
Next, modify the Windows Form application to enable a user to submit a new order when the Submit button is clicked. To do this, create an instance of the OrderProcessingWorkflow and run it passing in the parameters (read from the form) required to process the new order.
To submit a new order
In the
submitButton_Clickmethod, create a String variable nameditemand set its value equal to theSelectedItemproperty of theitemsListcontrol.Create a Int32 variable named
quantityand set its value equal to theValueproperty of theitemQuantitycontrol.string item = this.itemsList.SelectedItem.ToString(); int quantity = (int)this.itemQuantity.Value;Create a Type variable named
orderWorkflowand set its value equal to theOrderProcessingWorkflowtype.Create a WorkflowInstance variable named
workflowInstanceand set its value equal to the return value of the CreateWorkflow method defined in theworkflowRuntimeobject. Pass theorderWorkflowobject as a parameter to the CreateWorkflow method call to create the workflow.Type orderWorkflow = typeof(OrderProcessingWorkflow); WorkflowInstance workflowInstance = this.workflowRuntime.CreateWorkflow(orderWorkflow);Create a new generic
Listobject of the String type and assign it to theorderHistoryDictionarycollection, using the InstanceId of theworkflowInstanceobject as the key. This allows the host to track the workflow instances that are created.For the value at the
orderHistorykey location created in the previous step, add a String whose value is the item name and quantity that the user submitted for the order by calling theAddmethod defined in theDictionaryclass.this.orderHistory[workflowInstance.InstanceId.ToString()] = new List<string>(); this.orderHistory[workflowInstance.InstanceId.ToString()].Add ("Item: " + item + "; Quantity:" + quantity);Call the Start method that is defined in the
workflowInstanceobject to start the workflow.Raise the
NewOrderevent, passingnull(Nothingin Visual Basic) as the first parameter, and a newNewOrderEventArgsobject that is initialized with the InstanceId of theworkflowInstanceobject, theitemvariable and thequantityvariable. This uses the local service to send a message to the workflow that a new order has been created.workflowInstance.Start(); newOrderEvent(null, new NewOrderEventArgs(workflowInstance.InstanceId, item, quantity));
Updating the User Interface
Finally, enable the workflow to update the user interface using status messages.
To update the user interface with status messages
In the
ItemStatusUpdatemethod, verify whether theInvokeRequiredproperty of theordersIdListobject istrue. You must perform this action because the workflow instance runs on a separate thread and updating the UI requires you to invoke operations from the UI thread.If this property is
true, create a newItemStatusUpdateDelegateobject namedstatusUpdateand a Object array namedargsthat contains theorderIdandnewStatusparameters.Call the
Invokemethod of theMainFormclass, passing thestatusUpdateandargsobjects as parameters.If Me.ordersIdList.InvokeRequired = True Then Dim statusUpdate As ItemStatusUpdateDelegate = _ New ItemStatusUpdateDelegate(AddressOf ItemStatusUpdate) Dim args As Object() = New Object(1) {orderId, newStatus} Me.Invoke(statusUpdate, args) Else End Ifif (this.ordersIdList.InvokeRequired == true) { ItemStatusUpdateDelegate statusUpdate = new ItemStatusUpdateDelegate(ItemStatusUpdate); object[] args = new object[2] { orderId, newStatus }; this.Invoke(statusUpdate, args); } else { }Note
The following steps should be included in the
Elsebody of theInvokeRequiredIfstatement that you created in the previous step.Create an
Ifstatement to make sure that theorderHistorycollection contains a value that uses theorderIdparameter as a key; this ensures that the status update is being sent to an extant list item.In the
Ifstatement created in the previous step, set the value at theorderIdkey of theorderHistorycollection equal to the current DateTime, with the value of thenewStatusparameter appended to it.Create a nested
Ifstatement in theIfstatement created in step 3 to verify whether theordersIdListcontains theorderIdin itsItemscollection.If it does not, add the value of the
orderIdto theItemscollection.Set the
SelectedItemproperty of theorderIdListequal to the String value of theorderIdparameter.Set the
Textproperty of theorderStatusobject equal to the return value of theGetOrderHistorymethod. Pass the String value of theorderIdobject as a parameter to theGetOrderHistorymethod.if (this.orderHistory.ContainsKey(orderId.ToString())) { this.orderHistory[orderId.ToString()].Add (DateTime.Now + " - " + newStatus); // Update order status data UI info if (this.ordersIdList.Items.Contains(orderId.ToString()) == false) { this.ordersIdList.Items.Add(orderId.ToString()); } this.ordersIdList.SelectedItem = orderId.ToString(); this.orderStatus.Text = GetOrderHistory(orderId.ToString()); }
Compiling the Code
For more information about compiling your code, see Compiling the Code.
In Task 4: Define the WaitingForOrder State, you define the first state in the OrderProcessingWorkflow by adding child activities.
See Also
Reference
WorkflowRuntime
StartRuntime
ExternalDataExchangeService
AddService
WorkflowInstance
InstanceId
Start
Concepts
Windows Workflow Foundation and Application Communication
State Machine Workflows
Other Resources
Task 4: Define the WaitingForOrder State
Tutorial: Create a State Machine Workflow
Communications
HostCommunication Sample
Ordering State Machine
Simple State Machine
Copyright © 2007 by Microsoft Corporation. All rights reserved.
Last Published: 2010-03-04