While Activity that can contain multiple activities

Locked While Activity that can contain multiple activities

  • Wednesday, May 14, 2008 5:21 AM
     
     

    Has anyone got a method of creating a while activity that can have more than one activity contained.  It seems unnecessary that the workflow designing user should have to put a sequence activity in all the time.

    I'm happy to create my own while activity, but am stuck at

     

    ActivityExecutionContext newContext = manager.CreateExecutionContext(this.EnabledActivities[0]);

     

    Does anyone know how to add an activity collection (keeping in mind that I want the activities to run in sequence) to the createExecutionContext?

    Maybe there is a way I can copy everything in EnabledActivities to a new instance of SequenceActivity, and then create the context with it, however, copying the activities gives me an error saying the activities already exist in another activity. 

    I suppose I could make my while activity automatically add its own SequenceActivity, but that would still look funny.

    Does anyone have a nice solution to this problem??

     

    Cheers

     

All Replies

  • Wednesday, May 14, 2008 12:23 PM
    Moderator
     
     Answered

    I don't think you can add an activity collection to CreateExecutionContext.

     

    I think the right approach is:

    1. make your own WhileActivity derived from CompositeActivity and IActivityEventListener<ActivityExecutionStatusChangedEventArgs>

    2. In WhileActivity.Execute, subscribe status change for the first child activity: this.EnabledActivities[0].RegisterForStatusChange(Activity.ClosedEvent, this); And then schedule the first child activity.

    3. In WhileActivity.IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent, subscribe status change for the next child activity, and then schedule it.

     

    Hope this can help.

     

    BTW, the easiest way is to use a sequence inside a build-in while activity. This is not that funny as you think.

     

    Thanks

     

    Yuan Hu

  • Wednesday, May 14, 2008 11:09 PM
     
     

    Cheers for that.  I was pretty much there, just not closing the contexts etc correctly.  You helped heaps.

     

    Cheers

     

    Jason

     

  • Thursday, May 15, 2008 4:48 AM
     
     

    With an activity like a custom While, that will execute its children multiple times, make sure you create a new execution context, like the following:

     

    ActivityExecutionContext spawnedContext = context.ExecutionContextManager.CreateExecutionContext(base.EnabledActivities[0]);

    spawnedContext.Activity.RegisterForStatusChange(Activity.ClosedEvent, this);

    spawnedContext.ExecuteActivity(spawnedContext.Activity);

  • Thursday, May 15, 2008 8:51 PM
     
     

    Since this activity might help others I thought I would post up the bulk of the activity code (save a couple of properties).  The condition property is just a bool for now, as I wan't it to be bindable, and there is also a max loop count.  To use, set contition to true and max loop to x.

    Anyway, Let me know if I have stuffed up the context closing (or anything else) - I would hate to be doing this wrong.

    Hope this helps someone.

     

    /// <summary>

    /// Overridden execute. Requests that the execute children happens if the condition is true or null.

    /// </summary>

    /// <param name="executionContext"></param>

    /// <returns></returns>

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)

    {

    if (Condition == true)

    {

    //Condition is valid to repeat.

    ExecuteBody(executionContext);

    return ActivityExecutionStatus.Executing;

    }

    //Condition not met, so close the while activity and continue.

    return ActivityExecutionStatus.Closed;

    }

    /// <summary>

    /// Kicks of the first activity in the while loop.

    /// </summary>

    /// <param name="context"></param>

    void ExecuteBody(ActivityExecutionContext context)

    {

    ActivityExecutionContextManager manager = context.ExecutionContextManager;

    //register the first activity to go to IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent once it closes

    this.EnabledActivities[0].RegisterForStatusChange(Activity.ClosedEvent, this);

    //Create a child context for the activity to run in and start executing the activity

    ActivityExecutionContext newContext = manager.CreateExecutionContext(this.EnabledActivities[0]);

    Activity newActivity = newContext.Activity;

    newContext.ExecuteActivity(newActivity);

    }

    /// <summary>

    /// Counter for the number of repetitions.

    /// </summary>

    int count = 0;

    #region IActivityEventListener<ActivityExecutionStatusChangedEventArgs> Members

    /// <summary>

    /// Handle when a child activity is closed

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    void IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)

    {

    ActivityExecutionContext executionContext = (ActivityExecutionContext)sender;

    //Get the index of the next activity within the while

    int index = 0;

    index = FindActivityIndex(e);

    if (index==0)

    {

    throw new ArgumentOutOfRangeException("The index of the activity cannot be found");

    }

    if (index == 1)

    {

    //Increase the count if this is the first item in the while.

    count++;

    }

    //Remove the close event binding for the close activity.

    this.EnabledActivities[index - 1].UnregisterForStatusChange(Activity.ClosedEvent, this);

    //test if there are any more activities in the while.

    if (index == EnabledActivities.Count)

    {

    //No more activities, so reavaluate the condition.

    ActivityExecutionContextManager manager = executionContext.ExecutionContextManager;

    //Close the execution context for the final child activity

    ActivityExecutionContext innerContext = manager.GetExecutionContext(e.Activity);

    manager.CompleteExecutionContext(innerContext);

    //retest the condition

    if (this.ExecutionStatus == ActivityExecutionStatus.Executing && Condition == true && (count < MaximumLoops || MaximumLoops == 0))

    {

    //Continue looping again

    ExecuteBody(executionContext);

    }

    else

    {

    //Complete the while - condition no longer met, or count exceeded.

    executionContext.CloseActivity();

    }

    }

    else

    {

    //Execute the next activity in the hole

    ActivityExecutionContextManager manager = executionContext.ExecutionContextManager;

    //Close the execution context for the last child activity

    ActivityExecutionContext innerContext = manager.GetExecutionContext(e.Activity);

    manager.CompleteExecutionContext(innerContext);

    //Get the next child activity and execute it in a new context..

    Activity child = EnabledActivities[index];

    child.RegisterForStatusChange(Activity.ClosedEvent, this);

    ActivityExecutionContext newContext = manager.CreateExecutionContext(child);

    newContext.ExecuteActivity(newContext.Activity);

    }

    }

    /// <summary>

    /// Finds the activity index for the current context. Has to be a name comparison as the IndexOf method doesn't work across contexts.

    /// </summary>

    /// <param name="e"></param>

    /// <param name="index"></param>

    /// <returns></returns>

    private int FindActivityIndex(ActivityExecutionStatusChangedEventArgs e)

    {

    foreach (Activity activity in EnabledActivities)

    {

    if (activity.Name == e.Activity.Name)

    {

    return this.EnabledActivities.IndexOf(activity) + 1;

    }

    }

    return 0;

    }

     

  • Tuesday, July 01, 2008 6:30 PM
     
     
    Cant you drop a Sequence activity inside while activity and add the required activities in to the Sequence activity?
  • Tuesday, July 01, 2008 8:54 PM
     
     

    You can, but then you end up with more clutter on the screen, as well as an extra step that needs to be explained to your workflow designer users.  Plus its a pain when you want to add another item into a while that has only one current activity - you have to take it out, then put in the sequence, then add it back in.

    The custom while activity just makes things simpler.

     

  • Friday, February 06, 2009 9:25 PM
     
     
    Thanks a lot! This saved me at least a couple days!

    I was trying to do the same for a ForEach activity:

    http://social.msdn.microsoft.com/Forums/en-US/windowsworkflowfoundation/thread/2a706023-6316-43b8-bded-389716922863

    Sten
  • Monday, April 12, 2010 9:41 PM
     
     

    Hi Jason,

     

    I am not sure if you state that this code works...

    I see something I am not sure if works in the following line

    this.EnabledActivities[0].RegisterForStatusChange(Activity.ClosedEvent, this);

    As far as I know, the ClosedEvent will bever be raised since none of the EnabledActivities from the While Activity will leave the Initialized state. The idea of copying the activities before executing is the core of having the same activity called multiple times in the workflow. The only activity that will reach Closed state is the copy of the EnabledActivties.

     

    I hope this help

    Best regargs

    Pablo

  • Friday, February 10, 2012 5:31 PM
     
     
    Just stick an IFELSE Activity in the WHILE and take out the ELSE and ensure IF is always true. ie IF (true).