State Machine Module - Introduction

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.
14 comment(s)
Robert Fuchs

@Tolis: this post seems to be truncated.

It mentions the "Hard Coded definition" and then stops.

11 May, 2011
Apostolis Bekiaris (DevExpress)

thanks Robert! I fixed it

11 May, 2011
Steven Rasmussen

Very interesting...  This looks promising.  Is there a way to control who can change from one state to another based on security roles?

For example, maybe everyone has the ability to move the task through all the states except for 'Completed'.  Only certain people with rights have the ability to change the status to 'Completed.'

11 May, 2011
Apostolis Bekiaris (DevExpress)

@Steven. The module does not provide such means out of the box. However its very easy to do so. You could create a permission apply it to a role and on OnTransitionExecuting check if your permission is granted

11 May, 2011
Armando de la Torre

Hey , great !!

The runtime editor is strikingly similar to one of the suggestions I made in the forum!!

community.devexpress.com/.../PostAttachment.aspx

11 May, 2011
Luc DEBRUN

The module should have a system to check permissions to change states out of the box.

This seems quite obvious to me that (almost?) any application with multiple roles will require that.

Actually, I will be more frank... I am shocked it was not included.

Luc

12 May, 2011
Luc DEBRUN

And some positive comments now ...

I do like that the module has a runtime and that it links to appearances.

Our organization has many offices all over the world. The processes are the same but the workflow might slightly differ from office to office to reflect a local practice/availability of staff.

It thus allows the design of an universal app easily customisable by office.

Luc

12 May, 2011
Dennis (DevExpress Support)

@Luc:

Thank you very much for your valuable feedback. We agree with your comments and we consider creating a demo, better demonstrating how to implement this scenario.

Please track a suggestion we created in this regard, if you are still interested in this functionality: www.devexpress.com/issue=S37383

12 May, 2011
Steven Rasmussen

Thanks Dennis!  I look forward to the example regarding permissions and roles.

I would also suggest that you could simply add a collection on the 'Transitions' titled 'Permissable Executers' or something (I'm sure you could come up with a better name). This would just be a collection of the security roles that are allowed to execute this transition.

Then the module could check to see if the current user has those roles.  If so... leave the particular transition button enabled.  If not... then disable that transition button.  

What do you think?  I believe that would probably cover most (if not all) the security scenarios and would be pretty easy to implement :)

Thanks for listening.

12 May, 2011
Jose Ojeda

Hi Apostolis

this module looks like something I could use for one of my projects. can we use it on version 2010.2? if so where can I download it.

12 May, 2011
Apostolis Bekiaris (DevExpress)

Interesting idea Jose, but the answer is no. It can compile only with v2011.1.

12 May, 2011
P_C

It's actually what i wanted when i posted request on TOA (table of authorities)

it's a perfect solution

+1 on transition actions access permissions

though i can implement it myself

22 May, 2011
stefano del furia

HI, is the StateMachine available only with the XAF or i can use it without XAF ???

8 August, 2011
Apostolis Bekiaris (DevExpress)

@stefano del furia

The functionality provided by XAF modules targets only XAF applications. However the decoupled MVC architecture makes it possible to use some of the classes in a non XAF application. If we exclude the ViewControllers of the module (for controlling the XAF UI) everything else is of course reusable. Interesting classes are StateMachineLogic, XpoState, XpoStateMachine etc.

8 August, 2011

Please login or register to post comments.