Why is this feature useful for you?
Like any feature or technology, it requires understanding what it is, why it is useful, and when it makes sense to use it. Let us start with a brief description of “workflow” from Wikipedia:
“A workflow consists of a sequence of connected steps. It is a depiction of a sequence of operations, declared as work of a person, a group of persons, an organization of staff, or one or more simple or complex mechanisms. Workflow may be seen as any abstraction of real work. For control purposes, workflow may be a view on real work under a chosen aspect, thus serving as a virtual representation of actual work. The flow being described may refer to a document or product that is being transferred from one step to another.
A workflow is a model to represent real work for further assessment, e.g., for describing a reliably repeatable sequence of operations. More abstractly, a workflow is a pattern of activity enabled by a systematic organization of resources, defined roles and mass, energy and information flows, into a work process that can be documented and learned. Workflows are designed to achieve processing intents of some sort, such as physical transformation, service provision, or information processing.”
As you see, almost everything you can do in your business applications “breathes” with workflows. For better understanding, I would prefer to get away from this abstract and scientific description, and stay with something more concrete. Let’s demonstrate how to implement a simple incoming issues process scenario. So, here we go.
Suppose we have two classes: Issue and Task. A new task should be created when an active issue appears. It is necessary to inform an end user that there is work to do. The Issue and Task classes are very simple:
1: [DefaultClassOptions]
2: [DefaultProperty("Subject")]
3: public class Issue : BaseObject {
4: private string subject;
5: private bool active;
6: public Issue(Session session) : base(session) { }
7: public string Subject {
8: get { return subject; }
9: set { SetPropertyValue("Subject", ref subject, value); }
10: }
11: public bool Active {
12: get { return active; }
13: set { SetPropertyValue("Active", ref active, value); }
14: }
15: }
16: [DefaultClassOptions]
17: public class Task : BaseObject {
18: private string subject;
19: private Issue issue;
20: public Task(Session session) : base(session) { }
21: public string Subject {
22: get { return subject; }
23: set { SetPropertyValue("Subject", ref subject, value); }
24: }
25: public Issue Issue {
26: get { return issue; }
27: set { SetPropertyValue("Issue", ref issue, value); }
28: }
29: }
Previously, we would implement some hard-coded business logic, but now, with the help of the Windows Workflow Foundation functionality, we can easily get the same or better result using the Workflow Designer and the built-in DevExpress.Workflow.Activities library.
First, we will add a Class Library for our custom workflow activities (it is WorkflowDemo.Activities in the screenshot below). Since Issue and Task are pure persistent objects, and we want to perform CRUD operations with them, we need to add a reference to DevExpress.Workflow.Activities, which contains appropriate activities.
We will add a new Activity into our WorkflowDemo.Activities project and name it CreateUserTask and then construct it using the designer:

As you can see in screenshot above, there is an ObjectSpaceTransactionScope activity, containing several activities. This activity encapsulates a data access layer (including ConnectionString) and blocks for workflow persisting. The reason for the persistence block is that persistent objects most often are not serializable.
The first activity inside the ObjectSpaceTransactionScope is a TransactionalCreateObject<Task> activity that creates a new Task object.
Then, the TransactionalGetObjectByKey<Issue> activity loads an issue with a passed Id from a database.
Next, two Assign activities set the Issue and Subject properties of the created Task.
The TransactionalCommitChanges activity commits all the changes we made in the database.
And the last Assign activity sets the CreatedTaskId result parameter.
Also, we created several auxiliary variables and arguments that were used to pass data between activities.
Now, we have designed a workflow activity that implements simplified incoming issues processing, and can run it as a typical workflow. For example, we can publish it as a WCF service, add a controller with the "Start 'Process Issue' workflow" action into a WorkflowDemo.Module project and simply make a call to the published service. Another approach is to start the workflow automatically when a new Issue appears: it could be implemented via a separate application that periodically searches issues by the "Active = true" criteria and start the workflow for each issue found.
Now, if we create new active Issue in the client application, CreateUserTask activity will create a new Task for that issue:
Since the main workflow scenario will be creation of activities at runtime, we also provided a runtime workflow designer that can be invoked in every XAF Windows Forms application:


We also supported scenarios such as automatic database scanning for workflow target objects (see the 'Target Object Type' and 'Criteria' properties on the Workflow definition Detail View) and made it possible to implement a workflow server that can receive requests to start workflows, load workflow definitions from the database, etc.
Related Links
Online documentation
How-to & Blog posts