创建自定义复合活动

创建自定义复合活动时,如果您为自定义复合活动设计和创建默认逻辑,则有以下几种选择。

为自定义复合活动添加设计时支持

如果您的活动派生自 CompositeActivity 并希望为其提供设计时支持,则必须将 DesignerAttribute 设置为 CompositeActivityDesigner 的派生,如 ParallelActivityDesignerSequentialActivityDesigner。 当然,您也可以通过直接从 CompositeActivityDesigner 派生来创建自己的复合活动设计器。

例如,如果要创建镜像 ParallelActivity 活动的设计时体验的自定义复合活动,则必须将以下代码添加到您的自定义活动定义中:

<Designer(GetType(ParallelActivityDesigner), GetType(IDesigner))> _
Public Class CustomCompositeActivity
    Inherits CompositeActivity
    ' Define custom activity here.
End Class
[Designer(typeof(ParallelActivityDesigner), typeof(IDesigner))]
public class CustomCompositeActivity : CompositeActivity
{
    // Define custom activity here.
}

还可以使用 SequentialActivityDesigner 来创建镜像 SequenceActivity 活动的设计时体验的自定义活动,但更简单的方法是从 SequenceActivity 自身派生您的自定义活动。 同样,通过将此活动作为您的自定义活动的基类,可以为您提供以顺序方式计划活动的执行的默认逻辑。

相反,如果希望隐藏您的自定义复合活动的内部结构,如从 SequenceActivity 派生的内部结构,则应将 DesignerAttribute 设置为 ActivityDesigner,因为此类型不具备对复合活动的运行时支持。

为自定义复合活动添加子活动

可以选择为您的自定义活动添加子活动的方式。 可以使用两种方法为您的自定义复合活动添加子活动:

  • SequenceActivity 派生自定义活动。

  • CompositeActivity 派生自定义活动。

CompositeActivity 活动不包含用于处理子活动的默认逻辑。 如果希望创建自定义复合活动,还必须重写 Execute 来处理您的子活动和 Cancel 的执行,以取消您的子活动和关闭自定义活动。

下面的示例演示如何将 CodeActivity 活动添加到行为与 ParallelActivity 活动类似的自定义复合活动。 还必须从 IActivityEventListener 继承,以便在子活动上引发 ClosedExecuting 事件时得到通知。

<Designer(GetType(ParallelActivityDesigner), GetType(IDesigner))> _
Public Class CustomCompActivity
    Inherits CompositeActivity
    Implements IActivityEventListener(Of ActivityExecutionStatusChangedEventArgs)

    ' Declare variables.
    Private ca1 As CodeActivity
    Private ca2 As CodeActivity
    Private ca3 As CodeActivity
    Private ca4 As CodeActivity
    Private index As Integer

    Public Sub New()
        InitializeComponent()
    End Sub

    ' Initialize your child activities and add them to your custom 
    ' composite activity.
    Private Sub InitializeComponent()
        Me.CanModifyActivities = True
        Me.ca1 = New CodeActivity("CodeActivity1")
        Me.ca2 = New CodeActivity("CodeActivity2")
        Me.ca3 = New CodeActivity("CodeActivity3")
        Me.ca4 = New CodeActivity("CodeActivity4")
        AddHandler ca.ExecuteCode, AddressOf ca_ExecuteCode
        AddHandler ca2.ExecuteCode, AddressOf ca_ExecuteCode
        AddHandler ca4.ExecuteCode, AddressOf ca_ExecuteCode
        AddHandler ca4.ExecuteCode, AddressOf ca_ExecuteCode
        Me.Activities.Add(ca1)
        Me.Activities.Add(ca2)
        Me.Activities.Add(ca3)
        Me.Activities.Add(ca4)

        ' Cache index value of last child activity in the collection.
        index = Me.Activities.Count - 1
        Me.CanModifyActivities = False
    End Sub

    Protected Overrides Function Execute(ByVal ExecutionContext As ActivityExecutionContext) As ActivityExecutionStatus
        If (ExecutionContext Is Nothing) Then
            Throw New ArgumentNullException("ExecutionContext")
        End If

        ' If there are no child activities initialized, then close 
        ' the parent activity.
        If (Me.EnabledActivities.Count = 0) Then
            Return ActivityExecutionStatus.Closed
        End If


        ' Cycle through all of the enabled child activities in your 
        ' activity, register for their Closed and Executing events, 
        ' and then run the child activity.
        Dim i As Integer
        For i = 0 To Me.EnabledActivities.Count - 1 Step +1
            Dim ChildActivity As Activity = Me.EnabledActivities(i)
            ChildActivity.RegisterForStatusChange(Activity.ClosedEvent, Me)
            ChildActivity.RegisterForStatusChange(Activity.ExecutingEvent, Me)
            ExecutionContext.ExecuteActivity(ChildActivity)
        Next

        Return ActivityExecutionStatus.Executing
    End Function

    ' This event handler is used for the ExecuteCode event that 
    ' each CodeActivity activity will raise during its execution.
    Private Sub ca_ExecuteCode(ByVal sender As Object, ByVal e As EventArgs)
        Console.WriteLine("In event handler for {0}.ExecuteCode event...", CType(sender, Activity).Name)
    End Sub

    ' Implement the IActivityEventListener.OnEvent method to 
    ' display the current activity state and close your 
    ' parent custom activity when all of the child activities are
    ' are either in the Initialized or Closed state. Otherwise, keep
    ' the parent activity running.
    Sub OnEvent(ByVal Sender As Object, ByVal E As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener(Of System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs).OnEvent

        If (Sender Is Nothing) Then
            Throw New ArgumentNullException("Sender")
        End If

        If (E Is Nothing) Then
            Throw New ArgumentNullException("E")
        End If

        Dim Context As ActivityExecutionContext = CType(Sender, ActivityExecutionContext)

        If (Context Is Nothing) Then
            Throw New ArgumentException("The sender must be an ActivityExecutionContext object", "Sender")
        End If

        Dim CustActivity As CustomCompActivity = CType(Context.Activity, 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) Then
            E.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
        End If

        ' 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)) And (E.ExecutionStatus = ActivityExecutionStatus.Closed Or E.ExecutionStatus = ActivityExecutionStatus.Initialized)) Then

            ' 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()
        End If
    End Sub
End Class
[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();
        }
    }
}

通过使用上述示例,在工作流设计器内使用您的自定义活动时会将其锁定。 如果希望提供可以在设计时操作的具有子活动的自定义活动,则必须创建一个从 ActivityToolboxItem 派生的自定义类。

自定义工具箱项

下面的代码示例演示自定义复合活动的自定义工具箱项。 该工具箱项将一个 CodeActivity 活动添加到自定义复合活动中。

<Serializable()> _
Public Class SampleToolboxItem
    Inherits ActivityToolboxItem
    Protected Overrides Function CreateComponentsCore(ByVal designerHost As IDesignerHost) As IComponent()
        Dim CompActivity As CustomCompositeActivity = New CustomCompositeActivity()
        CompActivity.Activities.Add(New CodeActivity())
        CompActivity.Activities.Add(New CodeActivity())
        Return (New IComponent() {CompActivity})
    End Function
End Class
[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};
    }
}

对您的自定义复合活动使用 SampleToolboxItem 时,必须通过 ToolboxItemAttribute 对其进行声明,如下面的示例所示。

<ToolboxItem(GetType(SampleToolboxItem))> _
[ToolboxItem(typeof(SampleToolboxItem))]

如上节所述,如果需要您的自定义复合活动的设计器显示子活动,如本示例中的 CodeActivity 活动,则您的自定义活动必须与类似 ParallelActivityDesigner 的复合活动设计器相关联,或者与派生自任意基础复合活动设计器类的自定义设计器相关联。

请参见

参考

CompositeActivity
SequenceActivity
CompositeActivityDesigner
ParallelActivityDesigner
SequentialActivityDesigner
ActivityToolboxItem

概念

创建自定义活动
了解活动执行上下文
了解活动状态模型

其他资源

Windows Workflow Foundation 活动

Footer image

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。