Sunteți pe pagina 1din 78

Windows Workflow Foundation - FAQ

In this FAQ we will quickly run through and get a feel of how WWF (Windows Workflow foundation) will help you in making custom work flows in your project.

Windows workflow foundation FAQ


Introduction What is Windows Workflow Foundation? What is a Workflow? What are different types of Workflow in Windows Workflow foundation? when should we use a sequential workflow and when should we use state machines? How do we create workflows using designer? How do we specify conditions in Work flow? How do you handle exceptions in workflow? What is the use of XOML files. How can we pass parameters to workflow?

Introduction
In this FAQ we will quickly run through and get a feel of how WWF (Windows Workflow foundation) will help you in making custom work flows in your project. In case you like the article or your think I need improvements please send me a message at http://www.questpond.com . I am sure every one needs improvements. Enjoy

What is Windows Workflow Foundation?


WWF is a programming model for building workflow-enabled applications on windows. System.

Workflow namespace has all the necessary modules to develop any type of workflow.

What is a Workflow?
A Workflow is a set of activities, which is stored as model and they depict a process. Below figure depicts clearly the difference between Workflow and Activity. Every task is an activity and group of activity depicts a complete workflow. Workflow is run by the Workflow runtime engine.

Figure 1: - Work Flow Foundation Architecture Workflow model can be written in pure .NET code, pure XAML or Mix of XAML and .NET Code. A workflow model is compiled and can execute under windows, ASP.NET, Web services or windows services application.

What are different types of Workflow in Windows Workflow foundation?

There are two basics type of workflow Sequential Workflow and State machines workflow. A sequential workflow has clear start and finish boundaries. Workflow controls execution in Sequential workflow. In sequential execution, one task is executed after other. Sequential workflow is more rigid in format and execution path has a determistic nature. A State machine workflow is more dynamic in nature. Workflow has states and the state waits for events to help it move to next state. In State machine execution path is undetermestic nature. Below figure shows visual conceptualization of fundamentals. You can see in Sequential workflow the execution path is very determent. Shiv performs the entire task sequentially and these tasks are very determent. Now have a look at the second workflow. Every state goes to other state when it receives some external events. For instance when Shiv is seeing star trek there is an event of flashing news which triggers him to see the flashing new.

Figure 2: - Sequential and State machine workflow

when should we use a sequential workflow and when should we use state machines?

If the workflow is very rigid then you go for sequential workflow and if the workflow is dynamic then go for State machine workflow. For instance you have placed an order and the order will not pass until your supervisor approves is a rigid flow. Because your order has to be approved by, a supervisor or else it will not be approved. But what if your order moves from one place to other place. For instance, it moves from approval to waiting and then clarification a state machine workflow model is more appropriate. Below is a simple code snippet which shows practically how to use sequential workflow. Let try to understand step by step as marked in the figure:1 - First you need to select System. Workflow namespace. 2, 3, and 4 - In these three steps we created code object and linked them with activity. 5, 6, 7, and 8 - We start the workflow and create workflow instance object to run the sequential workflow. You can see the output in 8. Depending on how you add the activity in section 3, it executes sequentially. Because we have added codeactivity1, first it executes the first activity first. The sequence on how you add the activity to the activities collection the activities are run.

Figure: - 3 Code snippet for workflow Note: - The above code snippet was developed with out using designer. The whole point was to make you understand what happens behind the scenes. In real projects you will be dependent on designer rather than coding manually. You can find the above code in SimpleWorkFlowSampleManual folder.

How do we create workflows using designer?


As said previously it is very easy to design workflows using designer. So we will answer this question by actually doing a small sample. Below is the code snippet and image snapshot which shows how we can use the designer to create workflows. So lets understand all the below numbered snapshot. 1 - First select a sequential workflow project. In this case, we have selected Sequential workflow console application to keep the sample simple. 2 - When you are done with creating the project you will see the solution explorer as shown in the second snapshot. There are two files one the WorkFlow1.cs and the other Workflow1.designer.cs.If you click on the WorkFlow1.cs you will get a designer pane as shown in snapshot 3. If you double click on Workflow1.designer.cs, you will get behind code as shown in snapshot 4. 3 - So let us drag drop a code activity on the workflow designer and associate this activity with a method called as MyActivity1. This association is done by entering the method name in Execute Code property. In MyActivity1, we have just displayed in the console that this is my first activity. Again, we have added one more code activity, which points to MyActivity2. If you see the designer pane we have sequenced code1 first and code2 next. So in short, code1 will execute first and the code2. This is clear from the output displayed below. 4 - This is the behind code of the workflow.

Figure 4 Sequential workflow using designer

How do we specify conditions in Work flow?


Yes, you can define conditions in workflow by using conditionedActivitygroup. Below is the numbered snapshot, which shows how to use conditionedActivitygroup. 1 - You can see in this snapshot we have defined a conditionedActivitygroup with two conditions. The two boxes inside the group define two conditions. 2 - You can select one of the condition box and define the condition using the When Conditions property. If this condition is true you need to specify in the execute code which method to execute.

For instance in the current snapshot we have said that old1 method should execute if age > 21. The same procedure we need to follow for the second condition box. In the second condition box we have specified to execute young1 method if age < 21. Currently the second condition is not visible in the below snapshot. 3 - Workflow editor also provides a cool interface called as Rule Condition Editor, which can be used to specify conditions. Age is a public property in the behind code. You can also get the Age in the intelligence of rule condition editor. 4 - Both the condition will execute inside the condition activity group. We need to also specify when this conditionactivitygroup should exit. Therefore, we have made a function called as exit. If the user inputs age as -1 it will exit from the loop or else it will take inputs from user and continue evaluating depending on the two conditions.

Figure 5:- Sequential workflow with conditions

How do you handle exceptions in workflow?


Exception handling in Workflow is somewhat different than how we do in normal .NET application. Below is the numbered snapshot of how we can handle exceptions in Workflow. 1 - We have small tab, which says view exceptions. If you click on view exception, you will be redirected to a workflow design only for exception as shown in numbered snapshot 2. 2 - This is the workflow which will execute incase we have exceptions. We have put a code

activity, which points to a method called as raise Exception. Incase of exception in the workflow this path will be followed.

Figure 6:- Workflow with exception handling

What is the use of XOML files.


Twist: - How can we serialize workflows? Windows Workflow Foundation gives developers a declarative way to create workflows by using XAML. See WPF chapter for more details about XAML. These markup files are Stored with XOML (Extensible Object Markup Language) extension. In the below snapshot you can see Workflow1.xoml file created by designer. Markup file can also have code behind. The whole concept of having code behind for XOML file is to separate the presentation from logic files. In the below code snapshot we have made a simple XOML sample. Below is the explanation

number wise:1 In order to create a XOML file you need to add sequential workflow with separation. Which means that XOML file will be created with a behind code. 2 Currently we have two activity code3 and code1. Below is the XOML file contents
<?Mapping XmlNamespace="ComponentModel" ClrNamespace="System.Workflow.ComponentModel" Assembly="System.Workflow.ComponentModel" ?> <?Mapping XmlNamespace="Compiler" ClrNamespace="System.Workflow.ComponentModel.Compiler" Assembly="System.Workflow.ComponentModel" ?> <?Mapping XmlNamespace="Activities" ClrNamespace="System.Workflow.Activities" Assembly="System.Workflow.Activities" ?> <?Mapping XmlNamespace="RuleConditions" ClrNamespace="System.Workflow.Activities.Rules" Assembly="System.Workflow.Activities" ?> <SequentialWorkflow x:Class="WorkflowSeq.Workflow1" x:CompileWith="Workflow1.xoml.cs" ID="Workflow1" xmlns:x="Definition" xmlns="Activities"> <Code ExecuteCode="Mycode3" ID="code3" /> <Code ExecuteCode="Mycode1" ID="code1" /> </SequentialWorkflow>

See the above snippet of the XOML file. You can see how the behind code is linked using the Compile With attribute. Code forms the element of the Sequential Workflow tag. One of the best thing with Markup is we can change the sequence just by changing the XOML file we do not need to compile the whole application again.

Figure 7:- XOML in action In the above snapshot, one of the things to now is 3, 4, and 5 numbered sections. These sections are not linked with the sample. But just to make you aware you can create serialize any workflow and deserialize them again using the text writer object.

How can we pass parameters to workflow?


When you call the start workflow function, you can pass as name / value pairs using the dictionary object.

Figure 8:- Passing value to workflow

Workflow Foundation 4.0 - Activity Designer


One of the major changes in Windows Workflow Foundation 4.0 is the Activity Designer. The new Activity Designer is based on WPF.

Download

Download source code for Workflow Foundation 4.0 - Activity Designer

Introduction
Visual Studio 2010 released with workflow foundation 4.0. Workflow Foundation 4.0 is introduced a significant amount of change from the previous versions of the technology. The change varies from the core of the workflow foundation, runtime and tools. Workflow foundation 4.0 re-architected to improve the performance and productivity of the WF. One of the major changes is in Activity Designer. The new Activity Designer is based on WPF [Windows Presentation Framework], which gives lot of flexibility to the developer. Creation of a custom look and feel using the new Activity Designer is very easy.

Activity Designer
Before starting with the new Activity Designer, let us create an Activity Designer Library. Create a project of type Activity Designer Library.

Right click on the solution and Add an Activity Designer item to the project.

Activity Designer will create two file; one .xaml file and another .xaml.cs file. Designer level customizations are mainly performed in .xaml file. We can do some customization like, when a property change, change the value of another property by tracking the designer events in .xaml.cs file. In this article, we will look into the customization using .xaml file only. After adding the Activity Designer, add the custom activity class to the project. Following is the sample activity class derived from CodeActivity
[Designer(typeof(ActivityDesigner1))] public class ActivityDesigner : CodeActivity { protected override void Execute(CodeActivityContext context) { throw new NotImplementedException(); } }

The Designer attribute specified for the class bind the Activity with the specified Activity

Designer. Build the Project and add the Custom Activity to either FlowChart or Sequential workflow. The default design of the activity is as follows

Activity with an Icon


We will start our customization by changing the icon associated with the Activity Designer. Specify the Icon under the ActivityDesigner.Icon tab and draw the icon using ImageDrawing.
<sap:ActivityDesigner.Icon> <DrawingBrush> <DrawingBrush.Drawing> <ImageDrawing> <ImageDrawing.Rect> <Rect Location="0,0" Size="16,16" ></Rect> </ImageDrawing.Rect> <ImageDrawing.ImageSource> <BitmapImage UriSource="..\Resources\activityIcon.ico" ></BitmapImage> </ImageDrawing.ImageSource> </ImageDrawing> </DrawingBrush.Drawing> </DrawingBrush> </sap:ActivityDesigner.Icon>

The new Activity Designer with custom Icon will look like

Activity with one Child Activity


Let us move to the next step, where we will create an activity which holds another activity. Our custom activity will work as a container for another activity. For defining a custom activity as container, we can use the WorkflowItemPresenter defined in System.Activities.Presentation namespace. Inside the activity designer, define the WorkflowItemPresenter as

<sap:WorkflowItemPresenter AllowDrop="True" Name="InsideItem" HintText="Drop Item here" Item="{Binding Path=ModelItem.Child}"> </sap:WorkflowItemPresenter>

Item attribute is bind to the child property of the corresponding Activity. Please refer the attached code for more details on the activity code. Prefix sap is defined as below
xmlns:sap="clrnamespace:System.Activities.Presentation;assembly=System.Activities.Presentat ion"

When you drag the custom activity, it will look like

Drag any built-in or custom activity to the newly created custom activity. Here we dropped one WriteLine activity into our custom activity.

Activity with Multiple Child Activities


There are many scenarios where we need to execute a set of activities in a sequence. For defining a custom activity just like Sequence activity to hold a number of other activities, we can use the WorkflowItemsPresenter defined under System.Activities.Presentation. Below code segment define the WorkflowItemsPresenter with vertical orientation. It uses one small rectangle to separate two activities. Also the child activities dropped on the WorkflowItemsPresenter is bind to the Activities property defined in the activity.
<sap:WorkflowItemsPresenter Items="{Binding Path=ModelItem.Activities}" HintText="Insert Activities Here"> <sap:WorkflowItemsPresenter.SpacerTemplate>

<DataTemplate> <Rectangle Fill="#FF1F6F6F" Width="10" Height="10" /> </DataTemplate> </sap:WorkflowItemsPresenter.SpacerTemplate> <sap:WorkflowItemsPresenter.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical"/> </ItemsPanelTemplate> </sap:WorkflowItemsPresenter.ItemsPanel> </sap:WorkflowItemsPresenter>

When you drag the custom activity, it will look like

In the following example, we added two activities to our Custom activity; one Assign Activity and another WriteLine Activity.

Activity with User Control


As the activity designer is based on WPF, we can add any WPF user control to our activity. We have a user control called WPFUserControl, which is included in the activity designer using the following code.
<local:WPFUserControl x:Name="customControl"></local:WPFUserControl>

For more details about the user control, please refer the attached code sample. Now the activity designer will look like

Activity with Custom Look and Feel


Here, we are going to re-define the look and feel of the activity designer. In the following example, we defined a custom activity designer with rounded rectangle shape. Inside the custom activity, we placed one double Arrow and a TextBlock. The main code used by the activity designer is as follows. Please refer the attached code sample for more details.
<ControlTemplate x:Key="CustomTemplate"> <Border x:Name="border" BorderThickness="1" BorderBrush="#FF582222" Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" CornerRadius="4" Focusable="True" > <Grid> <Canvas Width="80" Height="30" HorizontalAlignment="Center" Margin="8,8,8,8"> <Polygon Points="4,20 14,0 14,10 94,10 94,0 104,20 94,40 94,30 14,30 14,40" Stroke="Black" StrokeThickness="3" StrokeLineJoin="Round" HorizontalAlignment="Center" VerticalAlignment="Center" Fill="#FF3C56AD"></Polygon> </Canvas> <TextBlock Text=" CustomActivity" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#FFECEE5F" FontStretch="UltraCondensed" TextTrimming="CharacterEllipsis" FontWeight="Bold" /> </Grid> </Border> </ControlTemplate> <sap:ActivityDesigner.Style> <Style TargetType="{x:Type sap:ActivityDesigner}"> <Style.Triggers> <DataTrigger Binding="{Binding Path=ShowExpanded}" Value="false"> <Setter Property="Template" Value="{StaticResource CustomTemplate}"/> </DataTrigger>

</Style.Triggers> </Style> </sap:ActivityDesigner.Style>

The new custom activity designer will look like

Conclusion
Workflow Foundation 4.0 is giving the full flexibility to redefine the look and feel of a custom activity. You can define the activity to represent the functionality of the activity. This will make the custom activities more readable and understandable in the workflow. Also, the workflow design looks more readable and clear.

Workflow Foundation 4.0 - Custom Activity


Workflow Foundation 4.0 is introduced a significant amount of change from the previous versions of the technology. This article will be the first in a series of articles. In this article I will be discussing about the introduction to Custom Activity Development. In next articles, I will be concentrating more on the different aspects of Custom Activity Development.

Custom Activity
WF4.0 supports the custom activity development. Custom Activity development involves two parts- designer development and execution logic implementation. I already discussed about the designer development in Activity Designer in Workflow Foundation 4.0 article. Part I : Introduction to Custom Activity Development Part II : Bookmark For developing the custom activity, you need to inherit from

NativeActivity base class of custom activities, which can have full control on runtime features. Code Activity- base class of custom activities, which dont have access to runtime features. AsynchCodeActivity Asynchronous Code execution Also, we have NativeActivity<T>, CodeActivity<T> and AsynchCodeActivity<T> base classes. In our sample custom activity, we will use the NativeActivity as base class
public class SampleCustomActivity:NativeActivity { protected override void Execute(NativeActivityContext context) { throw new NotImplementedException(); } }

How to Link Designer to the Activity? As I specified, activity designer is created separately using WPF. For binding the activity designer to the custom activity, we can use the following attribute on top of the activity
[Designer(typeof(ActivityDesigner.SampleCustomActivityDesigner))] public class SampleCustomActivity:NativeActivity

How to Prevent the expand/collapse nature? Drag our custom activity to the workflow designer and double click on the activity; it will expand to the next level. The built-in activities, like WriteLine, Delay and Assign, doesnt have expand and collapse behavior. If we need to implement the same behavior for our custom activity, which is not a container activity for other activities, use the following attribute
[ActivityDesignerOptions(AllowDrillIn = false)] [Designer(typeof(ActivityDesigner.SampleCustomActivityDesigner))] public class SampleCustomActivity:NativeActivity

How to pass data to the Activity? For passing data to the Activity, either we can use the InArguments or class level Properties. InArguments are special type of properties used for passing data to the Activity. InArguments is

related to an instance of the Activity and it required the particular activity instances context for accessing the data from InArgument. In the following Code we are defining an InArgument of type string with name Message
public class SampleCustomActivity:NativeActivity { public InArgument<string> Message{get;set;} protected override void Execute(NativeActivityContext context) { throw new NotImplementedException(); } }

When you drag and drop the custom activity to workflow designer, you can see the InArgument in the property grid.

We can get the value of the InArgument using any of the following statements. Variables message and message2 will contain the same value.
protected override void Execute(NativeActivityContext context) { string message = Message.Get(context); string message2=context.GetValue(Message); }

How to pass data out of the Activity?

There are multiple ways to pass data from an activity to other activities or workflow. In the first example, we are defining an activity, which can return only one value. If an activity needs to return, only a single value, then inherit it from CodeActivity or NativeActivity or AsynchCodeActivity. Once you derive the custom activity from any of these classes, it defines an OutArgument with name as Result. We can set the value of this built-in OutArgument as specified below
public class SampleCustomActivity:NativeActivity<string> { public InArgument<string> Message { get; set; } protected override void Execute(NativeActivityContext context) { string message = Message.Get(context);

Result.Set(context,"Message from Custom Activity " + message); //context.SetValue(Result, "Message from Custom Activity " + message); } }

How to access the OutArgument Outside? Following picture shows, how we can access the OutArgument from outside the custom activity. Define a variable, which can hold the OutArgument; specify the same against the OutArgument in the property grid; use the variable in another activity.

This is the simple way of communication between activities or activity to host. There are other ways of communication using Extension methods, Workflow Properties, Bookmarks, WCF Services, etc. How to define an OutArgument? OutArgument is used for passing data out of an activity. We can define our own OutArguments as specified below
public class SampleCustomActivity:NativeActivity { public InArgument<string> Message { get; set; } public OutArgument<string> MessageFromActivity { get; set; }

protected override void Execute(NativeActivityContext context) { string message = Message.Get(context); context.SetValue(MessageFromActivity, "Message from Custom Activity " + message); } }

Conclusion
Here, we discussed about the custom activity development in Windows Workflow Foundation 4.0. This is an introduction to the custom activity development. In next articles, we will discuss more on the custom activity development and associated topics.

Workflow Foundation 4.0 - BookMark


Workflow Foundation 4.0 is introduced a significant amount of change from the previous versions of the technology. This article will be the second in a series of articles. In this article we will be discussing about the Bookmark and related topics used in Custom Activity Development.

Part I : Introduction to Custom Activity Development Part II : Bookmark

Bookmark
In WF4.0, we use different ways of communication between Host application and workflow. There are different approaches like Bookmark, Extension methods, WCF Workflow services, workflow level arguments, etc. Here, we will discuss about the simple way of communication between host application and workflow using Bookmarks. Bookmark is a pointer we create in the workflow execution. Bookmarks can be used for handling various events from Host application and also as data communication between Host and workflow. Bookmarks can be Non-blocking, None or Multi-Resume. Type of the Bookmark can be set using the BookmarkOptions enumerator. None: By default the bookmark will be of blocking type. When a bookmark encountered, the

execution of the workflow will go to the idle state. Workflow execution will wait for the resumption of one of the bookmarks. Non-blocking: Here, the execution of the workflow continues. Multi-Resume: This bookmark is of blocking type. We can resume the bookmark multiple times.

How to Create a Bookmark?


When you create a bookmark, the custom activity should be derived from the NativeActivity. As I specified in Part I of the article, when we are processing the workflow runtime, we need to use the NativeActivity. Following custom activity will create a Next bookmark.
public class BookmarkSampleActivity:NativeActivity { protected override bool CanInduceIdle { get { return true; } } protected override void Execute(NativeActivityContext context) { context.CreateBookmark("Next"); } }

CanInduceIdle: Creation of Bookmark means, we are making the workflow idle. When your custom activity needs to make the workflow idle, we need to override the property CanInduceIdle and return true. By default the value of the CanInduceIdle is false. If we are not overriding this property, at runtime the custom activity will throw the exception.

How to Resume the Bookmark?


When the workflow goes idle, we need to resume the bookmark to restart the execution.

WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow()); workflowApp.Run(); workflowApp.ResumeBookmark("Next", null);

How to pass data to the Bookmark?


For passing data to the bookmark, define the BookmarkCallBack method, which will get executed when the ResumeBookmark method executes.
Custom Activity Code

There are few modification in our custom activity code.


1. Added an InArgument as EventName. This value is used for creating the Bookmark. As the value is passed to the activity, we can use the same activity to create different bookmarks. 2. Added an OutArgument called Data. OutArgument is used to pass data out of the activity. In this case we are setting the OutArgument with the data passed from the host application to the BookmarkCallBack method. 3. Added the BookmarkCallbackMethod called HandleEvent. When the host application calls the ResumeBookmark, BookmarkCallbackMethod will get executed. Inside the callback method, we are getting the value from the host application as the last parameter.
public class BookmarkSampleActivity:NativeActivity { protected override bool CanInduceIdle { get { return true; } }

public InArgument<string> EventName { get; set; }

public OutArgument<string> Data { get; set; }

protected override void Execute(NativeActivityContext context) { context.CreateBookmark(EventName.Get(context), new BookmarkCallback(HandleEvent)); }

private void HandleEvent(NativeActivityContext context, Bookmark bookmark, object obj) { if (obj != null) { Data.Set(context, obj.ToString()); } } }

Workflow

We defined the following workflow using our BookmarkSampleActivity. Specified the EventName as Complete and saved the output data to the Message variable. WriteLine activity will display the data from the Message variable.

Host Application

From the host application, we pass the data to the callback method using ResumeBookmak method. Here, we are passing a string data Hello from Host.
WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow()); workflowApp.Run(); workflowApp.ResumeBookmark("Complete", "Hello from Host"); Console.ReadLine();

Result

How to get data from activity after ResumeBookmark?


We can get the data back to the host using the same BookmarkCallback method.
Custom Activity

Changed the Callback method to handle a Dictionary and add some value back to the dictionary.
private void HandleEvent(NativeActivityContext context, Bookmark bookmark, object obj) { if (obj != null) { Dictionary<string, object> values = obj as Dictionary<string, object>; values.Add("second", "Hello from Bookmark callback method."); } }

Host Application

From host, we are passing the dictionary to the callback method.


WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow());

workflowApp.Run(); Dictionary<string, object> Data = new Dictionary<string, object>(); Data.Add("first", "Hello from Host.");

workflowApp.ResumeBookmark("Complete", Data);

foreach (string key in Data.Keys) { Console.WriteLine(key + " : " + Data[key]); } Console.ReadLine();

Once, it resumed, means the callback method executed, we will get the data back to the host application through the dictionary.
Result

But, this approach is having some issues. Once the workflow goes idle, then only the ResumeBookmark() method will get executed.

Consider the above scenario with following Host application code


WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow());

Dictionary<string, object> Data = new Dictionary<string, object>(); Data.Add("first", "Hello from Host."); workflowApp.Run(); workflowApp.ResumeBookmark("Complete", Data);

if (Data.ContainsKey("second")) { Console.WriteLine("second : " + Data["second"]); } Console.ReadLine();

Change here is instead of looping through the Data dictionary, I am checking whether the dictionary is having the second value or not. As the checking is happening immediately after the ResumeBookmark, before the workflow went to idle mode, we wont get the data back to the Host application. The output will be empty. Here, we need to wait for the completion of the call back method before proceeding to the next statement. We can achieve it using one of the methods listed down.
1.Using AutoResetEvent

Using the AutoResetEvent, we can synchronize the execution of both the Host application and workflow application.
WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow()); AutoResetEvent synchEvent = new AutoResetEvent(false); workflowApp.Completed = (WorkflowApplicationCompletedEventArgs) => { synchEvent.Set(); };

workflowApp.Run();

Dictionary<string, object> Data = new Dictionary<string, object>(); Data.Add("first", "Hello from Host."); workflowApp.ResumeBookmark("Complete", Data); synchEvent.WaitOne();

if (Data.ContainsKey("second")) { Console.WriteLine("second : " + Data["second"]); } Console.ReadLine();

Create the AutoResetEvent and block the current thread after the ResumeBookmark. Once the workflow completes, signal the event. Here, we are receiving the data from the call back method after completion of the workflow.
2. Using delegate

Another way is to pass a delegate to the callback method and trigger it from the callback method.
Host Application
public delegate void BookmarkResumeDelegate(Dictionary<string, object> Data); class Program { static WorkflowApplication workflowApp; [STAThread] static void Main(string[] args) { workflowApp = new WorkflowApplication(new TestWorkflow()); workflowApp.Run();

BookmarkResumeDelegate myDelegate = new BookmarkResumeDelegate(BookmarkResumeHandler); Dictionary<string, object> Data = new Dictionary<string, object>(); Data.Add("first", "Hello from Host."); Data.Add("myDelegate", myDelegate); workflowApp.ResumeBookmark("Complete", Data); }

static void BookmarkResumeHandler(Dictionary<string, object> Data) { if (Data.ContainsKey("second")) { Console.WriteLine("second : " + Data["second"]); } Console.ReadLine(); }

Custom Activity

Changed the Callback method to handle the delegate send from the Host application
private void HandleEvent(NativeActivityContext context, Bookmark bookmark, object obj) { if (obj != null) { Dictionary<string, object> values = obj as Dictionary<string, object>; values.Add("second", "Hello from Bookmark callback method."); if (values.ContainsKey("myDelegate")) {

BookmarkResumeDelegate myDelegate = values["myDelegate"] as BookmarkResumeDelegate; myDelegate(values); } } }

3. Using Event

Same as delegates, we can pass an event to the call back method and raise the same. Conclusion Here, we are discussed about the Bookmark used in custom activity development in Windows Workflow Foundation 4.0. Bookmarks are used for handling various events from Host application and for data communication between Host and workflow.

Workflow Foundation 4.0 WorkflowDataContext


Workflow Foundation 4.0 is introduced a significant amount of change from the previous versions of the technology. This article will be the third in a series of articles. In this article we will be discussing about how to pass data from host application to workflow without using bookmarks.

Part I : Introduction to Custom Activity Development Part II : Bookmark Part III : WorkflowDataContext

WorkflowDataContext
As specified in Part II, we can pass data from host application to custom activities using Bookmarks. Bookmarks are event driven mechanism. Whenever we define a bookmark, workflow will go for idle state and wait for the resumption of the bookmark. Here, we will discuss about another way of passing common data to all child activities defined in a workflow. If you want to pass some common data to all the activities defined in the workflow, we can use the WorkflowDataContext associated with the workflow. Custom Activity In Custom Activity, we can get the workflow level data using WorkflowDataContext. Properties associated with the WorkflowDataContext represent the workflow level Arguments and variables. In the following example, we extracted the properties from WorkflowDataContext and assigned to our OutArgument called DataFromHost.
public class CustomActivity:NativeActivity {

protected override bool CanInduceIdle { get { return true; } }

public OutArgument<string> DataFromHost { get; set; }

protected override void Execute(NativeActivityContext context) { string dataFromHost = string.Empty; WorkflowDataContext dataContext = context.DataContext; PropertyDescriptorCollection propertyDescriptorCollection = dataContext.GetProperties(); foreach (PropertyDescriptor propertyDesc in propertyDescriptorCollection) { dataFromHost += propertyDesc.Name + " : " + propertyDesc.GetValue(dataContext) + "\n"; }

DataFromHost.Set(context, dataFromHost); } }

WorkFlow Sample workflow using our CustomActivity is defined as

Host Application Define a dictionary for passing data from host to workflow. Add key value pairs to the dictionary. The keys should be same as the InArguments defined in the workflow. In our case, it will be data1 and data2.
Dictionary<string, object> Data = new Dictionary<string, object>(); Data.Add("data1", "Hello from Host.");

Data.Add("data2", "Color.Blue");

WorkflowApplication workflowApp = new WorkflowApplication(new TestWorkflow(), Data); workflowApp.Run();

Console.ReadLine();

Dictionary of data is specified along with the workflow object for creating the WorkflowApplication. Dictionary values are added to the WorkflowDataContext and this can be accessed by any child Activity using the context.DataContext. Result

You may be noted that, even though we passed only two values, data1 and data2, it displaying a third key called dataFromHost. As I specified, WorkflowDataContext contains all Arguments and Variables defined in workflow level. dataFromHost is the variable defined in the Workflow for handling output from our CustomActivity.

Conclusion
Here, we discussed about another way of passing data from host application to workflow. This method is used for passing common value to all child activities defined in the workflow.

Workflow Foundation 4.0 - Extension


Workflow Foundation 4.0 is introduced a significant amount of change from the previous versions of the technology. In WF4.0, we are using Extensions for communication between the host and workflow, the workflow and its child activities and between child activities. There are two types of extensions in WF4.0, in this article we will look into the normal extensions.

Extension
Extension is a normal class with public methods and properties. Once we add the extension class to the workflow application, we can use the same in any child activities. For each instance of the workflow application will have one instance of its extension class. Using the context associated with the custom activity, we can access the extensions.

Sample Extension
For our sample, I am using a simple extension with one property and a Method. The dictionary property is used to pass data from host application to child activity. Same is used for communicating to another child activity.
public class SimpleExtension { private Dictionary<string, object> data; public Dictionary<string, object> Data { get { if (data == null) {

data = new Dictionary<string, object>(); } return data; } }

public void AddData(string key, object value) { if (string.IsNullOrEmpty(key)) return; Data.Add(key, value); } }

Custom Activity to Access the Extension


Here, we are creating a custom activity, which will access the extension and pass values to the extension property. Our custom activity will have an InArgument called Message. Custom activity will access the extension using the Context object. In the following code, we are extracting the data passed by the host application and combining the same with the Message of the custom activity and updating the same back to the extension.
public class CustomActivity:NativeActivity { public InArgument<string> Message { get; set; } protected override void Execute(NativeActivityContext context) {

SimpleExtension extensionObj = context.GetExtension<SimpleExtension>(); if (extensionObj != null) { // extract the data added from host application

object data=null; if (extensionObj.Data.ContainsKey("Host")) { data = extensionObj.Data["Host"]; }

//Pass data to the extension extensionObj.AddData(this.DisplayName, Message.Get(context)+data); } } }

Define the Workflow


Now we are creating a new workflow using the custom activity. Our sample workflow will look like

Attaching to Workflow Application


After creating the workflow application object, add the extension object to the workflow applications Extensions collection. The code used by the Host application is
static void Main(string[] args) { // Create workflowapplication object WorkflowApplication wfApp = new WorkflowApplication(new SampleWorkflow());

//Create the extension object SimpleExtension extensionObj = new SimpleExtension(); extensionObj.AddData("Host", " Host Application Data ");

//Attach the extension to the workflow application wfApp.Extensions.Add(extensionObj);

//When the workflow complete, display the data from extension wfApp.Completed=delegate(WorkflowApplicationCompletedEventArgs a) { foreach (string key in extensionObj.Data.Keys) { Console.WriteLine("{0} : {1}", key, extensionObj.Data[key].ToString()); } };

// Start the workflow wfApp.Run();

Console.Read(); }

Result
From the result screen, you can observe the messages from both the custom activities and host application. Custom activity message is appended by the host application message.

Adding Extensions from Custom Activity


Extensions can be added using the CacheMetadata of the custom activity. In the following example, we add a second extension to our workflow using the custom activity.
protected override void CacheMetadata(NativeActivityMetadata metadata) { base.CacheMetadata(metadata); metadata.AddDefaultExtensionProvider(() => new SecondExtension()); }

Conclusion
Here, we discussed about the Extension used in custom activity development in Windows Workflow Foundation 4.0. Extensions are used for communication between the host and workflow, communication between the workflow and its child activities and communication between child activities.

Workflow Foundation 4.0 - Persistence

Workflow Foundation 4.0 is introduced a significant amount of changes from the previous versions of the technology. One of the main features of workflow is Persistence. In this article we will be discussing about Persistence.

Persistence
Persistence means storing the current state of a workflow. For better understanding, we will consider a document processing scenario. In document processing, one user uploads the document and the workflow gets started. Then the document will go to the reviewer. Reviewer will review the document, either accepts it or reject with comments. In this scenario, reviewing of the document can happen in a different application or can be done after many months. When we deal with this kind of scenario, we will go for persistence. After uploading the document, the corresponding workflow state is stored in an InstanceStore. When the review starts, we can reload and continue execution of the workflow from the last executed state. In our scenario, the reviewer can use another application, which can reload the workflow from the InstanceStore and continue execution. Also, as we are storing the workflow to InstanceStore, we can manage the long running workflow effectively.

How to Setup InstanceStore


By default, workflow is providing support for database based InstanceStore. For setting a database as InstanceStore, run the following SQL scripts in your database

SqlWorkflowInstanceStoreSchema.sql SqlWorkflowInstanceStoreLogic.sql

You can find these two scripts under C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en We can create our own custom InstanceStore by inheriting from System.Runtime.DurableInstancing.InstanceStore class. We will discuss about the custom InstanceStore latter.

How to Persist a Workflow


The steps for Persisting a workflow to our InstanceStore is
1. Create the workflow Application object
WorkflowApplication app = new WorkflowApplication(new ApprovalFlow());

2. Create the InstanceStore object


InstanceStore store = new SqlWorkflowInstanceStore(@"Data Source=.;Initial Catalog=SqlWorkflowInstanceStore;Integrated Security=True");

3. Set the InstanceStore of the workflow application object.


app.InstanceStore = store;

4. Run the workflow


app.Run();

5. Persist the workflow


app.Persist();

Note that, if you want to reload the workflow after sometime, we need to store the workflowId.
Guid persistId = app.Id;

In our document processing scenario, we can save the workflow Id as part of the assigned person Id and when the user login, reload the workflow. One more thing to note, Persist () will persists the current state of the workflow to the specified InstanceStore. It wont reload your workflow from the memory. If you have some critical data or state transition, call the Persist to save the current state to InstanceStore and continue execution. But, in our scenario, where the reviewer will login in a separate application or will login after one or two months, we can remove the workflow from memory. In this scenario, we need to call the Unload method instead of Persist.
app.Unload();

Unload will persists the current state of the workflow to InstanceStore and unload the workflow object from memory too.

How to Reload the Workflow


For reloading a persisted workflow, we need to follow the steps

1. Create the workflow Application object


WorkflowApplication app = new WorkflowApplication(new ApprovalFlow());

2. Create the InstanceStore object


InstanceStore store = new SqlWorkflowInstanceStore(@"Data Source=.;Initial Catalog=SqlWorkflowInstanceStore;Integrated Security=True");

3. Set the InstanceStore of the workflow application object.


app.InstanceStore = store;

Load the workflow


app.Load(persistId);

Where, persistId is the workflow Id of the workflow persisted to the InstanceStore.

Conclusion
Here, we discussed about the Persistence, one of the main feature of workflow which implements the concept of separating the When and How of the logic. We can use persistence to run a workflow across multiple channels and across multiple users.

Workflow Foundation 4.0 - WCF Integration


Workflow Foundation 4.0 is integrated with WCF and WPF. WF 4.0 uses WPF for activity designers and gives a very good support for custom activity designers with full WPF functionality. In this article we will look into the WCF integration to WF4.0.

WCF Integration
When we create a new project, under the workflow project templates, we have the option to select the WCF Workflow Service Application.

Once, we create a WCF Workflow Service application, it will create a default WCF integrated workflow. Note that the extension of our new workflow file is not .xaml, it is .xamlx. The default WCF workflow will look like

Custom WCF workflow


Now, let us add our own custom FlowChart workflow to the WCF workflow.
1. Drag and drop a receive activity 2. Specify the OperationName, which will act as the WCF operation or method name. 3. Set the CanCreateInstance property of the Receive activity. For running the workflow as WCF service, the current operation should be marked as CanCreateInstance=true.

4. Click on the Define clause against the Content. The Content Definition window will open. Define your method parameters here. In our sample, I defined a variable called Name, which I am passing to the operation GetMessage.

5. Now right click on the Receive activity and select Create SendReply option.

This will create the corresponding SendReply activity. Also, workflow will add a new variable for correlating both Receive and Send activities and will link both the activities. SendReply will act as the method which sends the result to the caller. 6. Again, click on the Define clause next to the Content of SendReply activity and define the result. Here I specified a message concatenating my input Name.

7. Added one Assign activity in between the Receive and Send to manipulate our data. Now, our Custom workflow is ready.

8. Let us verify it using the WCF Test client. Here I passed the value Susan to Name parameter and observed the result as "Hello Mr/Ms. Susan". Mr/Ms. Clause is added to the Name by the Assign activity and the Hello is added by the Send activity.

Host Application
Now, let us see, how we can use the workflow in a host application. Add the WCF workflow reference as the service reference to the host application. We can create the service client object and call the operation just like any other WCF service.

static void Main(string[] args) { ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client(); string name="Susan"; obj.GetMessage(ref name); Console.WriteLine(name);

Console.Read(); }

Result

Also, just like any other WCF service, we can browse the WCF workflow in browser.

Conclusion
Here, we discussed about the WCF integration with workflow foundation 4.0, how we can test a WCF workflow using WCF Test client and how we can call the WCF workflow from a host application.

Workflow Foundation 4.0 PropertyValueEditor


Workflow Foundation 4.0 has very good support for custom activity development. We can have our own custom activity with very good look and feel. When you define a custom activity, one of the main requirements is how to manage the associated properties in the property grid.

We can either use the TypeConverter or PropertyValueEditor for managing the properties and associated editors in PropertyGrid. TypeConverters are same as those in WPF. PropertyValueEditors are similar to the PropertyEditors in WPF, which provides custom editor for our property. In this article we will discuss about the PropertyValueEditor.

PropertyValueEditor
WF4.0 contains mainly three types of editors defined under System.Activities.Presentation.PropertyEditing namespace.
1. PropertyValueEditor 2. DialogPropertyValueEditor 3. ExtendedPropertyValueEditor

For our sample and understanding, I am using the DialogPropertyValueEditor with Simple implementation. In another article we will look into how we can use the DialogPropertyValueEditor to show a dictionary of values and also will have a discussion on another type of editors.

Template
For defining any kind of Editors, we need one Template file which define the look and feel appear in the Propertygrid and the class file contains the editor logic. For our sample, I am using the following template with one textbox and an EditModeSwitchButton. We can define the DataTemplate in a ResourceDictionary file or as part of a UserControl.

<DataTemplate x:Key="FileBrowserInlineEditorTemplate"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBox Grid.Column="0" Text="{Binding StringValue}"/> <PropertyEditing:EditModeSwitchButton Grid.Column="1"/> </Grid>

</DataTemplate>

PropertyValueEditor
Next is the implementation of the Custom PropertValueEditor. Custom propertyValueEditor should be inherited from one of the above three classes. In our sample, we are inheriting our Custom PropertyValueEditor from the DialogPropertyValueEditor class. Specify the InlineEditorTemplate in the Constructor. In our example, FileEditorResources is the ResourceDictionary, where we defined the template for the PropertyValueEditor. Override the ShowDialog method and implement our custom PropertyValueEditor behavior. In our example, we are using the OpenFileDialog to select an XML file.
class FileBrowserDialogPropertyValueEditor : System.Activities.Presentation.PropertyEditing.DialogPropertyValueEditor { private FileEditorResources res = new FileEditorResources();

public FileBrowserDialogPropertyValueEditor() { this.InlineEditorTemplate = res["FileBrowserInlineEditorTemplate"] as DataTemplate; }

public override void ShowDialog(PropertyValue propertyValue, IInputElement commandSource) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "XML files (*.xml)|*.xml"; ofd.Title = "Select XML file"; ofd.Multiselect = false;

if (ofd.ShowDialog() == true) {

propertyValue.StringValue = ofd.FileName; } } }

Test Activity
Let us use our Custom PropertyValueEditor in our Custom Activity. Attach the Editor attribute with the custom activity property.
public class TestActivity:CodeActivity { private string myFile;

[Browsable(true)] [Editor(typeof(FileBrowserDialogPropertyValueEditor), typeof(DialogPropertyValueEditor))] public string MyFile { get { return myFile; } set { myFile = value; }

protected override void Execute(CodeActivityContext context)

{ MessageBox.Show(MyFile); } }

Result
Drag and drop the custom activity to workflow and observe the properties associated with it. Our MyFile property will appear with a textbox & Selection button. Once we click on the button, it will open the File Open dialog with title as Select XML file and filtered by XML files.

Conclusion
Window workflow foundation 4.0 is having full support for custom activity development. In custom activity development, we are dealing with various kinds of properties. A good Property value editor will increase the user friendliness of the custom activity. Here, we discussed about a simple custom property value editor. We will look more on the editors and type converters in another article.

Workflow Foundation 4.0 PersistenceParticipant


We already discussed about Persistence and Extensions in workflow foundation 4.0. Persistence is the way of storing the current state of a workflow into an instance store. Extensions are special classes for sharing data among child activities, workflow and Host application. In case of normal Extension, the data shared using the Extension wont be stored to the instance store as part of the Persistence.

Introduction
We already discussed about Persistence and Extensions in workflow foundation 4.0. Persistence is the way of storing the current state of a workflow into an instance store. Extensions are special classes for sharing data among child activities, workflow and Host application. In case of normal Extension, the data shared using the Extension wont be stored to the instance store as part of the Persistence. In real business scenario, we may need to store the Extension data as part of the workflow state. For this purpose, workflow foundation 4.0 is providing a special kind of Extension. In this article we will discuss about this special extension and how we can use the same.

PersistenceParticipant Extension
This special extension is a class which inherited from the System.Activities.Persistence.PersistenceParticipant base class. For implementing the PersistenceParticipant, we need to override two methods CollectValues and PublishValues. Here, for our sample we have small Persistence Participant extension which stores a collection of strings in an Items list. Same way we have a normal extension class to store the data in an Items list.
Normal Extension

For more details on Extension, please refer Workflow Foundation 4.0 - Extension
public class NormalExtension { private List<object> items = new List<object>();

public List<object> Items { get { if (items == null) { items = new List<object>(); } return items; } } }

PersistenceParticipant Extension

As the Items list is a read write property, in CollectValues method, we need to assign the Item value to readWriteValues and assign null to writeOnlyValues. In PublishValues method, extract the Items value from readwriteValues dictionary and assign to Items property.

public class MyPersistenceParticipant:PersistenceParticipant { static XNamespace myNamespace = XNamespace.Get("MyApp"); static XName currentItems = myNamespace.GetName("Items");

private List<object> items = new List<object>();

public List<object> Items { get

{ if (items == null) { items = new List<object>(); } return items; } }

protected override void CollectValues(out IDictionary<XName, object> readWriteValues, out IDictionary<XName, object> writeOnlyValues) { readWriteValues = new Dictionary<XName, object>(1) { { currentItems, this.items } }; writeOnlyValues = null; }

protected override void PublishValues(IDictionary<XName, object> readWriteValues) { object loadedData; if (readWriteValues.TryGetValue(currentItems, out loadedData)) { items = (List<object>)loadedData; } }

Workflow

For understanding the difference between the normal extension and Persistence participant, we have a small workflow.

Here Close_Bookmark is a custom activity to create bookmark with name Close. For more details on Bookmark, please refer Workflow Foundation 4.0 Bookmark.

Host application
For verifying our Persistence Participant extension, we will use the following Host application code. Create an Instance of the workflow application and set the Instance Store. After that, add the objects of Normal as well as PersistenceParticipant extensions with one Item added to the Items property to the Extension collection of our workflow application. For our sample, we need to unload the instance completely from memory, so called the Unload method instead of Persist method. This will store the workflow state to instance store and remove the workflow application object from memory. Now, for reloading the workflow from instance store, create the workflow application object, setup instance store and add extension objects to the workflow application object. After reloading, loop through the Items collection of both normal extension and PersistenceParticipant extension and display the items.
static void Main(string[] args) { WorkflowApplication wfApp = new WorkflowApplication(new Workflow1()); InstanceStore store = new SqlWorkflowInstanceStore(@"Data Source=.;Initial Catalog=SqlWorkflowInstanceStore;Integrated Security=True");

wfApp.InstanceStore = store;

NormalExtension normalObj = new NormalExtension(); normalObj.Items.Add("Normal Extension: Before Persist");

MyPersistenceParticipant obj = new MyPersistenceParticipant(); obj.Items.Add("Persistence Participant Extension: Before Persist");

wfApp.Extensions.Add(normalObj); wfApp.Extensions.Add(obj);

Guid persistId = wfApp.Id; wfApp.Run();

wfApp.Unload();

WorkflowApplication wfApp1 = new WorkflowApplication(new Workflow1()); wfApp1.InstanceStore = store;

NormalExtension normalObjAfter = new NormalExtension(); MyPersistenceParticipant objAfter = new MyPersistenceParticipant();

wfApp1.Extensions.Add(normalObjAfter); wfApp1.Extensions.Add(objAfter);

wfApp1.Load(persistId);

foreach (string s in normalObjAfter.Items)

{ Console.WriteLine(s); }

foreach (string s in objAfter.Items) { Console.WriteLine(s); }

wfApp1.ResumeBookmark("Close", null);

Console.Read(); }

Result
From the result screen, we can observe that the data added to the PersistenceParticipant extension before Persist operation persisted as part of workflow state information. And the data added to the normal extension before Persist operation is not available after reloading of the workflow.

Conclusion
PersistenceParticipant is a special extension for storing additional data along with the workflow state to the Instance store on Persisting a workflow. We can use normal extension for sharing data across different activities, workflow and host application. If the data needs to be persisted along with workflow state, then use the PersistenceParticipant Extension.

Workflow Foundation 4.0 - Type Converter


Workflow Foundation 4.0 is introduced a significant amount of change in the Activity Designer. In WF4.0, Activity Designers are defined using WPF. Property management and display is one of the important aspects of a custom Activity Designer.

Introduction
Workflow Foundation 4.0 is introduced a significant amount of change in the Activity Designer. In WF4.0, Activity Designers are defined using WPF. Property management and display is one of the important aspects of a custom Activity Designer. We can use the PropertyValueEditor for providing meaningful editor for the Property. Also, we can use the TypeConverters for providing the expected values of a property.

Wf4.0 is using the WPF Type Converters. In this article we will discuss about the WPF TypeConverter and how we can use the same in WF4.0 custom activity.

Type Converter
Type Converters are used to pre-populate the expected values of a Property. We can either allow the user to edit new values or can restrict the user to only select from the given values using Type Converter. Let us look into a simple Type converter to populate the WorkingDay property. Our Type Converter is inherited from StringConverter and we override three methods. As we override the GetStandardValuesExclusive() method and return true, this Type Converter restrict the user only to select values from the list. If it return false or not overridden, then it display the list and also allow the user to enter another value.
Type Converter
public class WorkingDayConverter : StringConverter { private static StandardValuesCollection svc;

public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; }

public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; }

public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { ArrayList values = new ArrayList(); values.Add("Monday");

values.Add("Tuesday"); values.Add("Wednesday"); values.Add("Thursday"); values.Add("Friday");

svc = new StandardValuesCollection(values); return svc; } }

Our TestActivity looks like


public class TestActivity:CodeActivity { private string day; [TypeConverterAttribute(typeof(WorkingDayConverter))] [BrowsableAttribute(true)] public string WorkingDay { get { return this.day; } set { this.day = value; } } protected override void Execute(CodeActivityContext context) {

MessageBox.Show(WorkingDay); } }

Workflow Designer will look like

Extracting Workflow Data


In the next sample, we will how to extract another activity data. For our sample Type Converter, I am populating the names of all WriteLine activities exist in our workflow. As I am not overriding the GetStandardValuesExclusive method, we can enter the values against the Property too.
Type Converter
internal class WriteLineConverter : StringConverter { public override bool GetStandardValuesSupported(ITypeDescriptorContext context)

{ return true; } public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { ModelService modelService = (ModelService)context.GetService(typeof(ModelService)); IEnumerable<ModelItem> activityCollection = modelService.Find(modelService.Root, typeof(WriteLine));

ArrayList writeLines = new ArrayList(); foreach (ModelItem shv in activityCollection) { writeLines.Add((shv.GetCurrentValue() as WriteLine).DisplayName); } return new StandardValuesCollection(writeLines); } }

Property in Test Activity


private string wrLine; [TypeConverterAttribute(typeof(WriteLineConverter))] [BrowsableAttribute(true)] public string WriteLinenames { get { return this.wrLine; } set {

this.wrLine = value; } }

Workflow Designer

Conclusion
WF4.0 Activity designers are defined using WPF. WPF integration gives full support for customization of our custom designer. In this article we discussed about the Type Converters used as part of custom properties in Activity Designer.

Workflow Foundation 4.0 - Validation

Workflow Foundation 4.0 has very good support for custom activity development. One of the areas in custom activity development is validating the properties and its values in design time. In this article we will discuss about various validation options available with workflow foundation 4.0.

Argument Validation

As we discussed earlier, the data model in WF4 is based on InArguments, OutArguments and InOutArguments. Here, we will discuss about the various ways to do the argument validation. RequiredArgument If the InArgument is a required argument, decorate the same with RequiredArgument attribute. Custom Activity
public class CustomActivity:NativeActivity { [RequiredArgument()] public InArgument<string> InputData { get; set; }

protected override void Execute(NativeActivityContext context) { } }

Observe the design time validation error on the activity as well as in error window.

Note: Validation errors are not compilation errors. Project with validation errors will compile successfully. OverloadGroup When you want to group the arguments use the OverloadGroup attribute. This will group the attributes and only one group of attribute should be valid. If you specify value for attributes belonging to two groups, it will throw validation error. Custom Activity
public class CustomActivity:NativeActivity {

[OverloadGroup("GroupedData")] public InArgument<string> InputData { get;

set; }

[OverloadGroup("GroupedData")] public InArgument<string> TextData { get; set; }

[OverloadGroup("Data")] public InArgument<string> Data { get; set; }

protected override void Execute(NativeActivityContext context) { } }

Now, we specify the values for InputData and Data arguments, which belong to two groups. Observe the validation error.

Property Validation
Even though the WF4 data model is based on arguments, sometimes we may need to use properties. Property validations are mainly done using the CacheMetadata method. Override the cacheMetadata method and perform the validation Custom Activity
public class CustomActivity:NativeActivity { public string Data { get; set;

protected override void CacheMetadata(NativeActivityMetadata metadata) { base.CacheMetadata(metadata);

////Validation if (string.IsNullOrEmpty(this.Data)) { metadata.AddValidationError("Data should not be blank."); } }

protected override void Execute(NativeActivityContext context) { } }

We can add any kind of validations inside CacheMetadata. In our example, verified whether the property value is not null or empty. If it is empty, throw the validation error.

Conclusion
WF4.0 has various methods for performing the design time validation for arguments and properties.

S-ar putea să vă placă și