There are quite a few configuration steps to be completed on the Workflow Activity after it is dragged onto the workflow design surface. The complexity and the number of data entry points for configuring these Activities could lead to errors and poor user experience.
A very common ask from the developers and ITPro users in the ISV community is to simplify the configuration effort for deploying ‘repeatable’ and ‘often used’ Workflow Activities. As an example, the Receive Activity requires quite a few properties (OperationName, ServiceContractName, CanCreateInstance and Content) to be configured before the solution is used. In this example, the Receive Activity property OperationName is the only ‘unique’ property. All of the other properties, (ServiceContractName. etc.) are derived from the OperationName. A typical ISV Application can have 100’s of WCF/Workflow Services and each of these Services usually has several instances of use of the Receive Activity within the Service; offering immense opportunity to simplify the configuration experience.
While there are many approaches to streamline the process, one approach that has excited the ISV community is creating a Morphing Custom Activity that at design-time is pre-configured with properties. An Activity Designer is applied to an Activity that either changes the properties of the Activity or replaces the Activity completely at design time, to create the Morphed Activity. The Morphed Activity can be designed appropriately to reduce the configuration steps and make the Workflow Designer experience simpler and error free.
This Blog post provides the WorkflowDesignerExtensibility solution that contains updates to both the ActivityLibrary (provides a new custom activity that shows how to use activity delegates) and ActivityLibrary.Design (provides a new activity designer that shows how recreate activity trees at design time) projects. It also includes a new project called DemoService that defines Service1.xamlx which can be used to test the LaunchOperation Activity.
In this Activity Project, a new custom activity called LaunchOperation has been added that makes use of the InvokeFunc activity to “poke a hole” in the Activity design in which a Workflow developer can drop custom activities; e.g., Receive Activity. For this Activity, the OperationName is specified by the user, while all other configuration properties are specified by the Activity Designer. There are two classes of Delegate Activity that can be plugged in: those that only take zero or more input parameters with no return value; and those that take zero or more input parameters as well as have a return value. These map to ActivityAction and ActivityFunc respectively.
In this example, we want to create a “hole” with an enforced contract for an activity that will take no input parameters and return a string result. To do this we will create an ActivityFunc<string> at design time and drop it into the “hole” that is exposed by the body argument (of type ActivityFunc<string> as shown above).
The InvokeFunction<string> Activity, when it executes, will run what was plugged in to its Func property and write the output into OutArgument called Result (as shown above). In this case, our Func property is empty because the Activity Designer will fill it in when the user sets the OperationName on the LaunchOperation activity, but the Result is pre-configured to write its value into the Value output argument defined on LaunchOperation (see the Arguments screenshot), and thus will be accessible to Workflows hosting the LaunchOperation activity.
In our scenario, we have established that the Receive Activity, which will be contained within the ActivityFunc<string>, always receives a string input parameter, and we want to surface that string as an OutArgument on the LaunchOperation activity so that Workflows that use the LaunchOperation activity can get the string value passed as input to the operation; this is the Value OutArgument shown above.
The LaunchOperation Activity Designer (associated with the LaunchOperation activity via the Register Method in the Metadata.cs file for ActivityLibrary.Design) works as follows: whenever a user changes the value of the OperationName text box and tabs out (such that the text box loses focus), a new Receive Activity is created under the covers with an OperationName as specified in the textbox. This receive is added to ActivityFunc<string> defining the LaunchOperation activity’s body. This effect can best be visualized by looking at the XAML for a Workflow containing a LaunchOperation Activity
The details of the Activity Designer are indexed as TODO’s (use Visual Studio’s task list to view) and described with inline comments.
The logic which accomplishes this is primarily defined in LaunchOperation.Designer.xaml.cs:
Using the Sample
Compile the solution and open up Service1.xamlx. Drop a new LaunchOperation on the sequence and change the OperationName. Run the DemoService project and either examine the WSDL or Call the Service using the WCF Test Client to verify that the operation name changes are as expected.
The source code around this sample is available for download from the Skydrive/Live site.