State Machine Module - Introduction

XAF Team Blog
11 May 2011

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:

image
       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:

 

image

 

State definition detail view:

 

image

 

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.
No Comments

Please login or register to post comments.