Thursday 15 September 2011

Workflow Wizards – Part 2

As promised, the follow-up to the previous post. The biggest challenge in communicating with a view model from the workflow is to indicate to the workflow what result is expected. The second challenge was to allow the view model to resume the workflow without having to hardcode bookmark names. Both of these problems were solved with the UserPrompt class.

UserPrompt and UserPrompt<T>

	/// <summary>
/// Contains the result of prompting a user for input
/// </summary>
/// <remarks>This is used to pass back information to the workflow</remarks>
public class UserPrompt
{
/// <summary>
/// Initializes a new <see cref="UserPrompt"/>
/// </summary>
public UserPrompt(ActivityContext context)
{
if (context == null) throw new ArgumentNullException("context");
Proceed = true;
BookmarkName = context.ActivityInstanceId;
}

/// <summary>
/// Gets/sets whether to proceed to the next node
/// </summary>
/// <remarks>If <see langword="true"/> the workflow will proceed to the next node.
/// If <see langword="false"/>, it will return to the previous node requiring user input.</remarks>
public bool Proceed { get; set; }

/// <summary>
/// Gets/sets the name of the bookmark to resume
/// </summary>
public string BookmarkName { get; private set; }
}

/// <summary>
/// Contains the result of prompting a user for input
/// </summary>
/// <remarks>This is used to pass back information to the workflow</remarks>
/// <typeparam name="T"></typeparam>
public class UserPrompt<T> : UserPrompt
{
/// <summary>
/// Initializes a new <see cref="UserPrompt"/>
/// </summary>
/// <param name="context"></param>
public UserPrompt(ActivityContext context)
: base(context)
{
}

/// <summary>
/// gets/sets the result of the user input.
/// </summary>
public T Result { get; set; }
}

There are two versions of the class, the first being the basic one used when the workflow doesn’t expect a result. It contains two properties – the bookmark name and a boolean called Proceed. The bookmark name is used to resume the bookmark (obviously!) and the Proceed property is set by the view model to indicate whether the user pressed “Next” or “Previous”.


The second version of UserPrompt – UserPrompt<T> is used when the current activity expects a result. The view model should set the Result property. Our credit check example shows clearly how this is used:

void PerformCreditCheck(UserPrompt<CreditCheckResult> userPrompt, Company company);

This prompts the view model to perform a credit check and indicates that the result is a CreditCheckResult object.


Resuming the bookmark


It is relatively easy to resume a workflow bookmark, but to make sure that the developers writing the view models know what to do, I wrote the following extension method for WorkflowApplication :

/// <summary>
/// Resumes the current bookmark with the result
/// </summary>
/// <param name="app"></param>
/// <param name="result">The result of the user input</param>
public static void ResumeBookmark(this WorkflowApplication app, UserPrompt result)
{
if (app == null) throw new ArgumentNullException("app");
if (result == null) throw new ArgumentNullException("result");
app.ResumeBookmark(result.BookmarkName, result);
}

 


Conclusion


The user prompt class is the glue that holds everything together, but there is still something missing: The activities. In the next post, I’ll dig into them.

No comments:

Post a Comment