Exercise 9: Custom Activities and Designers

As you have already seen, Windows Workflow allows you to build custom activities in code. Depending on your need, there are several base classes you can use.

Base Class

Used For

Activity

Activities that are composed of other activities

CodeActivity

Activities that want to control execution

AsyncCodeActivity

Activities that want to do async work during execution

NativeActivity

Activities that contain other activities or need advanced services from the workflow runtime

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 8. Alternatively, you can follow the following steps to begin with Exercise 9.

  1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.
  2. Open the starting solution for Exercise 9 located under \Source\Ex9-ActivityDesigner\Begin folder and use it as the starting point for this exercise.
  3. Press CTRL+SHIFT+B to build the solution.

Task 1 – Add Activity Designer Library

  1. Right click on the HelloWorkflow solution file and select Add / New Project…
  2. Select the Workflow templates and choose Activity Designer Library. Name the project HelloWorkflow.Activities.

    Figure 56

    Add a new Activity Designer Library project named HelloWorkflow.Activities (C#)

    Figure 57

    Add a new Activity Designer Library project named HelloWorkflow.Activities (VB)

    Note:
    Why add an Activity Designer Library instead of an Activity Library?

    If you intend to create designers using the Activity Designer Library project template is a better choice because it includes the necessary references to the WPF libraries in addition to the System.Activities and related libraries.

  3. Delete ActivityDesigner1.xaml, you will not need it for this lab.

Task 2 – Create Composite Activity in XAML

Activities can be created by composing other activities. In this task, you will create a XAML activity that consists of other activities.

  1. Right click on the HelloWorkflow.Activities project and select Add/New Item… and add a new activity using the following values

    Item

    Value

    Installed Templates

    Workflow

    Template

    Activity

    Name

    WriteTwoLines.xaml

    Figure 58

    Add a the WriteTwoLines activity (C#)

    Figure 59

    Add a the WriteTwoLines activity (VB)

  2. WriteTwoLines.xaml will be opened in the designer. To complete it do the following

    1. From the Control Flow toolbox category drop a Sequence activity
    2. Add two In arguments of type string named Line1 and Line2
    3. From the primitives group, drop two WriteLine activities
    4. Set the first activity Text to Line1 and the second to Line2

    Figure 60

    The completed WriteTwoLines activity

Task 3 – Add Existing Activities

In this task you will add some pre-built code activities

  1. Add the existing PrePostSequence and DiagnosticTrace files. To do this, in Solution Explorer right-click the HelloWorkflow.Activities project, and select Add / Existing Item... browse to Source\Assets folder of this lab (choosing the folder that matches the language of your preference) and select both the PrePostSequence and DiagnosticTrace files.
Note:
What do these activities do?

The PrePostSequence activity is like a Sequence activity but also includes a Pre activity that will execute before the body and a Post activity that will execute after the body.

The DiagnosticTrace activity outputs a message to System.Diagnostics.Trace as well as a CustomTracking record
  1. Press CTRL+SHIFT+B to build the solution
  2. The HelloWorkflow project will need a reference to the HelloWorkflow.Activities project. To add the reference, right click on the HelloWorkflow project and select Add Reference… in the projects tab select HelloWorkflow.Activities and click OK to add the reference. Then repeat and add another reference to the WPF library PresentationFramework
  3. Open HelloWorkflow /SayHello.xaml in the designer and notice that the toolbox now contains the new activities.

Task 4 – Observe Custom Activities without Designers

In this task you will see how custom activities behave without a designer. Some can be used without a designer while others may benefit from one or may require one for intended functionality.

Note:
You won't be saving SayHello.xaml in this task – we will close it without saving at the end. The point is just to observe the behavior.
  1. Drop the WriteTwoLines activity on the design surface after the last activity. Note that you can initialize the arguments for Line1 and Line2 using the property grid.

    Figure 61

    Initializing the arguments for WriteTwoLines using the property grid.

Note:
Do I have to create an activity designer?

No, you don’t. For many activities, the property grid will be sufficient allowing developers to initialize arguments to your activity. However, adding a designer can improve the experience of using your custom activity making it easier to understand and use.

  1. Drop the DiagnosticTrace activity on to the design surface after the last Assign activity. This activity is a little more complex with an enumerated value but you can still set the properties using the property grid. You will notice that this activity also provides some validation of the required argument "Text". This type of activity could benefit from a designer.

    Figure 62

    The property grid for the DiagnosticTrace activity

  2. Drop the PrePostSequence activity on to the surface after the last Assign activity. The activity will work but there is no way to set the body Activities it will execute. You need to create a custom designer to make this activity fully functional.

    Figure 63

    The PrePostSequence activity without a custom designer

  3. Close SayHello.xaml without saving it.

Task 5 – Explore Activity Designers

In this task, you will add existing custom activity designers for the DiagnosticTrace and PrePostSequence activity to your project and explore how they work.

  1. Add the existing DiagnosticTraceDesigner.xaml and PrePostSequenceDesigner.xaml files. To do this,
    1. Right click on the HelloWorkflow.Activities project and select Add/New Folder
    2. Name the folder Designers
    3. Right-click the Designers folder, and select Add / Existing Item... browse to Source\Assets folder of this lab (choosing the folder that matches the language of your preference) and select both XAML files. Be sure to select the file type XAML Files in the dialog as shown.

      Figure 64

      Add the existing XAML Designer files

  2. The HelloWorld.Activities project Designers folder should have the two designers added.

    Figure 65

    The HelloWorkflow.Activities folder with two activity designers

  3. Next let’s take a tour of these designers and learn about how they work. Open DiagnosticTraceDesigner.xaml
  4. Notice the DataTemplates – there is one for an Expanded view and one for a Collapsed view. The templates contain the body of your controls

    Figure 66

    The DataTemplates contain the body of the controls for the designer

  5. There is a lot to see in this designer but let’s focus on two things. The first is the ExpressionTextBox. This control allows the user to edit an InArgument<T> value with design time support for Intellisense (when used within Visual Studio) and compile checking of the expression. The DiagnosticTrace activity uses an InArgument<string> type for the Text property. Below you can see the Collapsed data template using the ExpressionTextBox

    XAML

    <DataTemplate x:Key="Collapsed">
    FakePre-100c0891a81b41e2ad4e9d54db7e7de6-61c3eafe03204e4c83d88c49e50fa4a7FakePre-fae73dc09bba47bc85d110da4976c111-975d18535a07463cb52346ccb8a04f90FakePre-9be1ae1857244268bc9dfb30b16bc279-bf08799027c3434d8560701a43be55b2FakePre-885d836e56fa446ab70d8c78ec177850-e7fc0668072740ddb7851bada32b4072FakePre-21e6d33ad3984456a42a29682a8ea3cd-ac01de20dd8f492ca1ec3700fcb05052FakePre-80bad54e2b494f03b5ea664254065b1c-87711069c204430b8d309538f8081e97FakePre-581d55835e2949a0839f9d144b21206e-e0803ceca195445eb67ab47f405966e1FakePre-a6da8f4c6c634462b488a76ae29271e3-0524c7ec686f4cd7875d3dacb982cb64 <sapv:ExpressionTextBox HintText="Text" Expression="{Binding Path=ModelItem.Text, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In }" ExpressionType="s:String" OwnerActivity="{Binding Path=ModelItem}" Width="300" Margin="0,5" MaxLines="1" />FakePre-7d10f1fa4015425fb7972d4e22cfa2fb-eb2c447fc1c34d5b952a1aad04502a87FakePre-29c518f4ebcb45fcb8ed2bc12f512066-19f9b873136f4017b9fe68dc80cad975

  6. The next thing to note is found in the Expanded data template. It contains a ComboBox that will display the list of trace values. The DiagnosticTrace activity uses a CLR property named “Level” for this trace value. To bind to a CLR property you need to use ModelItem.(property name) and the ModelToObjectValueConverter as you see below.

    XAML

    <ComboBox 
    FakePre-9c51337fba53438f82909d290d848154-8c6383e882ee46fc8d4507e10515075aFakePre-19c6f28283ed445e8fd735c02f822aff-a8524586986b44d4a7ac871311e1630aFakePre-5d738349613849e2b3651a1052049873-14f5b817210342caa370a5c49579e565FakePre-3dfe1420540d4033b3931c7cd5eea039-17d923c2d72a4b56ba9cdcd272b1dd7fFakePre-f90ea395743c42f8b3b458c4d48e8aac-86ac34ce98a2473390ee0fef0828d3fbFakePre-847154d3cddb4bdc90f9afbc9dd37ee5-d2992f93fd494119b97e54aac2f9316dFakePre-2b7ce57023a4458f9c031b861590ac56-70b51cbcdd964c84a3e410e433ffa215

  7. The PrePostSequence designer is more complex. Because it is a composite activity, it supports dropping other activities on the design surface. Open PrePostDesigner.XAML
  8. This activity designer uses data binding to bind to the properties of the PrePostSequence class. The Pre and Post properties are a single activity so the designer uses WorkflowItemPresenter to enable a design surface for them which allows the user to drop a single activity.

    XAML

    <sap:WorkflowItemPresenter Margin="3" Item="{Binding Path=ModelItem.Pre, Mode=TwoWay}" HintText="Drop activity here"/>

  9. The Activities collection uses the WorkflowItemsPresenter to create a design surface that can hold a collection of activities. In the XAML below you see how you can create a design surface that looks similar to the Sequence.

    XAML

    <sap:WorkflowItemsPresenter Margin="3" Items="{Binding Path=ModelItem.Activities}" HintText="Drop activities here"> <sap:WorkflowItemsPresenter.SpacerTemplate> <DataTemplate> <Grid> <Path Fill="Red" Width="20" Height="30" HorizontalAlignment="Center" Data="M 0,10 l 20,0 l -10,10 Z" /> </Grid> </DataTemplate> </sap:WorkflowItemsPresenter.SpacerTemplate> <sap:WorkflowItemsPresenter.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical"/> </ItemsPanelTemplate> </sap:WorkflowItemsPresenter.ItemsPanel> </sap:WorkflowItemsPresenter>

  10. Press CTRL+SHIFT+B to build the solution

Task 6 – Linking the Activity Designers to the Activity

Now you have designers for your activities but they will not be used until you link the designers to your custom activities. There are two ways to do this, a declarative way and an imperative way. We will use the declarative approach.

  1. Open DiagnosticTrace.cs (C#) and add the following namespace directives

    C#

    using HelloWorkflow.Activities.Designers;
  2. Add the Designer attribute to the class as shown

    C#

    [Designer(typeof(DiagnosticTraceDesigner))]
    public sealed class DiagnosticTrace : CodeActivity
    FakePre-651ca8e54ca044a9891826bf34b3d105-783bae08955f4ec3a9f11aa493f70d24

    Visual Basic

    <Designer(GetType(DiagnosticTraceDesigner))>
    Public NotInheritable Class DiagnosticTrace
    FakePre-041ceb5cd1554f6abd1b9b26846c0eb7-e787627bfdd44b4fb066d36169d5f9aa

  3. Open PrePostSequence.cs (C#) and add the following namespace directives

    C#

    using HelloWorkflow.Activities.Designers;

  4. Add the Designer attribute to the PrePostSequence class.

    C#

    [ContentProperty("Activities")]
    [Designer(typeof(PrePostSequenceDesigner))]FakePre-6904f7839e464c8eb050ef1d352332e8-42a613ab88284e0a987c1762c2cab768FakePre-d6b9e8a34d134db4bc6f5d39a1470b27-0c045a2fecfd41f89b79191612f6409b

    Visual Basic

    <ContentProperty("Activities")> <Designer(GetType(PrePostSequenceDesigner))>
    Public NotInheritable Class PrePostSequence

Next Step

Exercise 9: Verification