This documentation is archived and is not being maintained.

Creating Custom Composite Activities

When you are creating custom composite activities, you have several options when you design and create default logic for your custom composite activity.

Adding Design-Time Support to a Custom Composite Activity

If your activity derives from CompositeActivity and you want design-time support for your activity, you must set the DesignerAttribute to a derivative of CompositeActivityDesigner, such as ParallelActivityDesigner or SequentialActivityDesigner. Of course, you may also create your own composite activity designer by deriving directly from CompositeActivityDesigner.

For example, if you want to create a custom composite activity that mirrors the design-time experience of the ParallelActivity activity, you must add the following code to your custom activity definition:

[Designer(typeof(ParallelActivityDesigner), typeof(IDesigner))]
public class CustomCompositeActivity : CompositeActivity
{
    // Define custom activity here.
}

You can also use the SequentialActivityDesigner to create a custom activity that mirrors the design-time experience of the SequenceActivity activity, although an easier method is to derive your custom activity from SequenceActivity itself. Also, by having this activity as a base class for your custom activity, the default logic for scheduling the execution of activities in a sequential manner is provided for you.

Conversely, if you want to hide the internal structure of your custom composite activity, such as one derived from SequenceActivity, you should set the DesignerAttribute to ActivityDesigner because this type does not have design-time support for composite activities.

Adding Child Activities to a Custom Composite Activity

You can choose how to add child activities to your custom activity. There are two methods that you can use to add child activities to your custom composite activity:

  • Deriving the custom activity from SequenceActivity.

  • Deriving the custom activity from CompositeActivity.

The CompositeActivity activity does not contain default logic for handling child activities. If you want to create a custom composite activity, you must still override Execute to handle the execution of your child activities and Cancel to cancel your child activities and close your custom activity.

The following example shows how to add CodeActivity activities to a custom composite activity that behaves like a ParallelActivity activity. You must also inherit from IActivityEventListener to be notified when the Closed and Executing events on a child activity are raised.

[Designer(typeof(ParallelActivityDesigner), typeof(IDesigner))]
public class CustomCompActivity : CompositeActivity, IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
{
    // Declare variables.
    private CodeActivity ca1;
    private CodeActivity ca2;
    private CodeActivity ca3;
    private CodeActivity ca4;
    private int index;

    public CustomCompActivity()
    {
        InitializeComponent();
    }

    // Initialize your child activities and add them to your custom 
    // composite activity.
    private void InitializeComponent()
    {
        this.CanModifyActivities = true;
        this.ca1 = new CodeActivity("CodeActivity1");
        this.ca2 = new CodeActivity("CodeActivity2");
        this.ca3 = new CodeActivity("CodeActivity3");
        this.ca4 = new CodeActivity("CodeActivity4");
        ca1.ExecuteCode += new EventHandler(ca_ExecuteCode);
        ca2.ExecuteCode += new EventHandler(ca_ExecuteCode);
        ca3.ExecuteCode += new EventHandler(ca_ExecuteCode); 
        ca4.ExecuteCode += new EventHandler(ca_ExecuteCode); 
        this.Activities.Add(ca1);
        this.Activities.Add(ca2);
        this.Activities.Add(ca3);
        this.Activities.Add(ca4);

        // Cache index value of last child activity in the collection.
        index = this.Activities.Count - 1;
        this.CanModifyActivities = false;
    }

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
    {
        if (executionContext == null)
            throw new ArgumentNullException("executionContext");

        // If there are no child activities initialized, then close 
        // the parent activity.
        if (this.EnabledActivities.Count == 0)
        {
            return ActivityExecutionStatus.Closed;
        }

        // Cycle through all of the enabled child activities in your 
        // activity, register for their Closed event, and then run the
        // child activity.
        for (int i = 0; i < this.EnabledActivities.Count; ++i)
        {
            Activity childActivity = this.EnabledActivities[i];
            childActivity.RegisterForStatusChange(Activity.ClosedEvent, this);
            childActivity.RegisterForStatusChange(Activity.ExecutingEvent, this);
            executionContext.ExecuteActivity(childActivity);
        }

            return ActivityExecutionStatus.Executing;
    }

    // This event handler is used for the ExecuteCode event that 
    // each CodeActivity activity will raise during its execution.
    void ca_ExecuteCode(object sender, EventArgs e)
    {
        Console.WriteLine ("In event handler for {0}.ExecuteCode event...", ((Activity)sender).Name);
    }

    // Implement the IActivityEventListener.OnEvent event handler to 
    // display the current activity state and close your parent custom
    // activity when all of the child activities are either in the 
    // Initialized or Closed state. Otherwise, keep the parent activity 
    // running.
    public void OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
    {
        if (sender == null)
            throw new ArgumentNullException("sender");
        if (e == null)
            throw new ArgumentNullException("e");

        ActivityExecutionContext context = sender as ActivityExecutionContext;

        if (context == null)
            throw new ArgumentException("The sender must be an ActivityExecutionContext object", "sender");

        CustomCompActivity custActivity = context.Activity as CustomCompActivity;

        // Display the current state of the child activity.
        Console.WriteLine ("{0} is in the {1} state.", e.Activity.Name, e.ExecutionStatus);

        // As a good coding practice, unregister for the Closed event 
        // if the child activity is in the closed state.  
        if (e.Activity.ExecutionStatus == ActivityExecutionStatus.Closed)
        {
            e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
        }

        // Check whether the activity passed into the payload of the
        // ActivityExecutionStatusChangedEventArgs object is the last 
        // child activity and if it is in either the Initialized state
        // or Closed state before closing the parent activity.
        if (e.Activity.Equals(custActivity.EnabledActivities[index]) && (e.ExecutionStatus == ActivityExecutionStatus.Closed || e.ExecutionStatus == ActivityExecutionStatus.Initialized)) 
        {
            // The child activities are now in a closed state, so it is 
            // safe to put your custom activity in a closed state as 
            // well.
            context.CloseActivity();
        }
    }
}

By using the previous example, your custom activity will be locked when it is used inside a workflow designer. If you want to provide a custom activity that can have additional child activities added to it within the workflow designer, you must create a custom class that derives from ActivityToolboxItem. Note that the data you have added is still locked. Deriving from ActivityToolboxItem merely gives users the ability to add additional data; they cannot modify what is already within the custom activity.

Custom Toolbox Items

The following code example shows a custom toolbox item for a custom composite activity. The toolbox item adds a CodeActivity activity to the custom composite activity.

[Serializable]
public class SampleToolboxItem: ActivityToolboxItem
{
    protected override IComponent[] CreateComponentsCore(IDesignerHost designerHost)
    {
        CustomCompositeActivity CompActivity = new CustomCompositeActivity();
        CompActivity.Activities.Add(new CodeActivity());
        CompActivity.Activities.Add(new CodeActivity());
        return new IComponent[] {CompActivity};
    }
}

When you are using SampleToolboxItem with your custom composite activity, you must declare it through the ToolboxItemAttribute as shown in the following example.

[ToolboxItem(typeof(SampleToolboxItem))]

As noted in a previous section, if you need the designer for your custom composite activity to show child activities, such as the CodeActivity activities in this example, your custom activity must be associated with a composite activity designer like ParallelActivityDesigner or with a custom designer deriving from any of the base composite activity designer classes.

See Also

Show: