I guess everyone is very excited by the new WF module that is coming up this year, v2011.1. However such flexibility does not come without difficulties, for example a learning curve exists. This is true even though our module provides the familiar XAF interface to deal with them. There is also a .NET4 prerequisite because as with WF4. When you do not want to face such challenges and your requirements are simple enough we recommend using our State Machine module.
Definition:
The StateMachine is defined by our team as module designed and positioned as a lightweight tool to easily manage the state of your object.
Let’s do an example: Assuming we have a Task object, which can be in one of five states: Draft, NotStarted, InProgress, Paused, Completed, Droppped:
public enum TaskStatus { Draft, NotStarted, InProgress, Paused, Completed, Dropped }
[DefaultClassOptions]
[ImageName("BO_Task")]
public class Task : BaseObject, IStateMachineProvider {
public Task(Session session)
: base(session) { }
public TaskStatus Status {
get { return GetPropertyValue<TaskStatus>("Status"); }
set { SetPropertyValue<TaskStatus>("Status", value); }
}
And we want to implement the following behavior:
Task states and transitions graph.
In previous versions we had to implement a set of controllers and may have had to inject logic into the Task class. State Machine module provides two flexible, XAF ways of completing this task.
Hard Coded definition
Derive from the StateMachine<T> class, passing as a parameter the Task class. Define and link with the status the start state, and the rest of the states. Add the transitions to each state. Implement IStateMachineUISettings interface that is responsible for modifying the appearance of Task items according to a Task’s state. Under the hood the Appearance module takes over, so everything we know about it can be applied here. The same interface defines how the transition actions will be displayed using the ShowActionsInPanel property. If we choose to show them in a panel the Name property defines the label of the ActionContainer. Finally the StatePropertyName provides the property value that the StateMachine will check in order to decide the position within the transition graph.
public class TaskStatusStateMachine : StateMachine<Task>, IStateMachineUISettings {
private IState startState;
public TaskStatusStateMachine() {
startState = new State(this, TaskStatus.Draft);
//Stage - Enum Linkage
IState notStartedState = new State(this, "Not Started", TaskStatus.NotStarted);
IState inProgressState = new State(this, "In Progress", TaskStatus.InProgress);
IState pausedState = new State(this, TaskStatus.Paused);
IState completedState = new State(this, TaskStatus.Completed);
IState droppedState = new State(this, TaskStatus.Dropped);
//Transition definition
startState.Transitions.Add(new Transition(notStartedState));
notStartedState.Transitions.Add(new Transition(startState));
notStartedState.Transitions.Add(new Transition(inProgressState));
inProgressState.Transitions.Add(new Transition(pausedState));
inProgressState.Transitions.Add(new Transition(completedState));
inProgressState.Transitions.Add(new Transition(droppedState));
pausedState.Transitions.Add(new Transition(inProgressState));
//Appeareance rule definition
StateAppearance inProgressAppearance = new StateAppearance(inProgressState);
inProgressAppearance.TargetItems = "Subject";
inProgressAppearance.Enabled = false;
StateAppearance completedAppearance = new StateAppearance(completedState);
completedAppearance.TargetItems = "Subject";
completedAppearance.Enabled = false;
StateAppearance pausedAppearance = new StateAppearance(pausedState);
pausedAppearance.TargetItems = "*";
pausedAppearance.BackColor = Color.Yellow;
States.Add(startState);
States.Add(notStartedState);
States.Add(inProgressState);
States.Add(pausedState);
States.Add(completedState);
}
//ActionContainer label
public override string Name {
get { return "Change status to"; }
}
public override IState StartState { get { return startState; } }
public override string StatePropertyName {
get { return "Status"; }
}
public bool ShowActionsInPanel {
get { return true; }
}
}
In order to inform the StateMachine module about state machines associated with the object Task class must implement IStateMachineProvider interface and return a set of available state machines.
public class Task : BaseObject, IStateMachineProvider {
public Task(Session session)
: base(session) { }
//IStateMachine members
public IList<IStateMachine> GetStateMachines() {
List<IStateMachine> result = new List<IStateMachine>();
result.Add(new TaskStatusStateMachine());
return result;
}
Runtime definition
The StateMachine module contains a set of persistent objects for storing the state machine definition in the DB. Hence it is possible for the end user to modify the persistent state machine in runtime.
The state machine definition detail view looks like this:

State definition detail view:

Controlling your transition states with code
Thanks to the XAF architecture it is very easy to subscribe to StateMachineController events and inject your logic before or after transition execution.
public class MyController:ViewController {
protected override void OnActivated() {
base.OnActivated();
Frame.GetController<StateMachineController>().TransitionExecuting+=OnTransitionExecuting;
Frame.GetController<StateMachineController>().TransitionExecuted+=OnTransitionExecuted;
}
void OnTransitionExecuted(object sender, ExecuteTransitionEventArgs executeTransitionEventArgs) {
}
void OnTransitionExecuting(object sender, ExecuteTransitionEventArgs executeTransitionEventArgs) {
}
}
We would appreciate your feedback on this post. Has it been useful to you? Feel free to contact us with any further questions.
Free DevExpress Products – Get Your Copy Today
The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the
DevExpress Support Center at your convenience. We’ll be happy to follow-up.