Customising flow editor

From FlexRule Wiki
Jump to: navigation, search

In FlexRule it is very easy to customize the behaviour of different editors in Rule Designer.

For this tutorial we are going to introduce a custom activity node to flow editor.

Steps

There are couple of steps needs to be taken in order to create and register a new custom node in designer.

  1. Creating item builder
  2. Creating toolbox
  3. Defining object model to be displayed in property window
  4. Creating custom activity
  5. Registering custom activity

Required Assembly References

When you create a custom activity project you need to reference the following assemblies in your project:

  1. FlexRule.Designer.Common
  2. FlexRule.Designer.Controls
  3. FlexRule.Designer.Controls.Toolbox
  4. FlexRule.Designer.Core
  5. FlexRule.Designer.FlowEngine

For Shapes you need to reference the following assembly as well:

  1. MindFusion.Diagramming

Creating item builder

An item builder is a class that is responsible to

  1. Build an object model associated to your node in an editor
  2. Loads properties from model to a defined object model
  3. Attaches the item when it is built to editor
  4. Validates an item
  5. Save an item

In order to create an builder, you simply need to override a class named ElementItemBuilderBase. And then override the following methods.

  1. /// This is the main class to create the rule node command 
  2. /// For example, this will be created by the builder strategy (NotifElementItemBuilder)
  3. public abstract class ElementItemBuilderBase
  4. {
  5.     /// <summary>
  6.     /// Create the properties for the item
  7.     /// </summary>
  8.     /// <param name="ctx">Context information related to the builder</param>
  9.     public abstract void InitializePropertyCollection(BuildContext ctx);
  10.  
  11.     /// <summary>
  12.     /// When an rule node is being loaded this method will be used to assign the property's value
  13.     /// </summary>
  14.     /// <param name="node"></param>
  15.     public abstract void AssignAttributes(XmlNode node);
  16.  
  17.     /// <summary>
  18.     /// Indicates if there is any custom child needs to be created. Return false as default.
  19.     /// </summary>
  20.     /// <param name="node"></param>
  21.     public abstract  bool ShouldCreateChild(string childName);
  22.  
  23.  
  24.     /// <summary>
  25.     /// When a node is being created and attached to the document
  26.     /// </summary>
  27.     /// <param name="creator">node creator</param>
  28.     /// <param name="parent">parent of the node to be created</param>
  29.     /// <param name="reference">reference node to the one is being created</param>
  30.     /// <param name="position">position of the node to be created</param>
  31.     /// <returns></returns>
  32.     public abstract IElementItem Attach(ILogicalDocumentCreator creator, IElementItem parent, IElementItem reference, Position position);
  33.  
  34.     /// <summary>
  35.     /// Override the behaviour for validating nodes
  36.     /// </summary>
  37.     /// <param name="context"></param>
  38.     /// <param name="item"></param>
  39.     public abstract  void ValidateItem(ElementItemValidationContext context, IElementItem item);
  40.  
  41.     /// <summary>
  42.     /// Writes the content of the rule node the the xml writer
  43.     /// </summary>
  44.     /// <param name="document">The document logic has all the element builders of the rule </param>
  45.     /// <param name="name">name of the file</param>
  46.     /// <param name="extension">file extension, .help.xml is for the designer help and .xml is for the rule</param>
  47.     /// <param name="writer"></param>
  48.     public abstract void WriteContent(ILogicalDocument document, string name, string extension, XmlWriter writer);
  49.  
  50.     /// <summary>
  51.     /// Properties of the object model that this builder is responsible to build. 
  52.     /// </summary>
  53.     public DynamicPropertyCollection Properties { get; protected set; }
  54. }

Creating toolbox

The next step is to define the Group and Command (custom node) in the toolbox.

Toolbox.png

Defining a command on the toolbox is as easy as implementing interface named IToolBoxInitializer.

  1. /// <summary>
  2. /// Implement initializer for toolbox that allows toolbox items be created
  3. /// </summary>
  4. public interface IToolBoxInitializer
  5. {
  6.     /// <summary>
  7.     ///  Initialize commands on the toolbox
  8.     /// </summary>
  9.     /// <param name="toolBoxBuilder"></param>
  10.     void Initialize(IToolBoxWindowBuilder toolBoxBuilder);
  11.  
  12.     /// <summary>
  13.     ///  Stores and reads next available toolbox (if required)
  14.     /// <remarks>Implement this as an auto property.</remarks>
  15.     /// </summary>
  16.     IToolBoxInitializer Next { get; set; }
  17. }

With the argument that Initialize method provides you can create groups and commands on the toolbox. To add a group for example you can use

toolBoxBuilder.ToolBox.Groups.Add("Custom", new ToolboxGroup("Custom"));

And to add a command for example

ToolboxGroup windowsFormsGroup = toolBoxBuilder.ToolBox.Groups["Custom"];
windowsFormsGroup.Items.Add(new ToolboxItem(Commands.Notif, ImageKeyNotif, new string[] { "Activity", Commands.Notif }, true));

Defining object model

Each builder (e.g. NotifElementItemBuilder) would have a method implemented called:

public override void InitializePropertyCollection(BuildContext ctx)

Which is where your application gets a chance to register model object properties to be added into Properties Window. BuildContext gives you information about

  • Where does this command belong to?
  • What is the command name?
  • What is the parent item or builder?

When an item is added to a document, or an item is selected from a document editor, the Properties Window show that item with list of all available properties. Property window then allows user to modify values of the selected item. There are multiple different types of view that Property windows allow you to create:

  1. Simple
  2. Collection
  3. Nested

PropertiesWindow.png

Simple Properties

Simple properties are just a name and a value of specific type. e.g. string, int, enum... To add simple property/value you just need to AddProperty to Properties. If Properties is null then you need to instantiate it first. To add name/value to it you can simply use a code like this:

  1. // Creates a string property named 'id' with null default value that the display title is '(id)'  
  2. Properties.AddProperty("id", null, typeof(string)).DisplayName = "(id)";
  3.  
  4. // And similarly for description
  5. Properties.AddProperty("Description", string.Empty, typeof(string), "Any description related to this command").DisplayName = "(Description)";
  6. Properties["Description"].AllowEnterValue = true;

Collection of Properties

Now when you want a collection of some other properties you add the property similarly, however instead of setting a simple type for a name/property you use

typeof(List<DynamicPropertyCollection>)

as the type. This makes Properties window automatically launches a collection editor. For example let's say we want a collection of string that is called Messages

  1. var messagesProperty = Properties.AddProperty("Messages", null, typeof(List<DynamicPropertyCollection>));

That sets up the collection editor, now in the collection editor there will be a similar structure to edit messages. In order to setup that structure you need use ListManager.Schema from Messages property (in this example).

  1. messagesProperty.ListManager.Schema.AddProperty("Message", null, typeof(string)).DisplayName = "Title";

Now what happens now is, when collection editor is launched, allows you to modify list of object that has one property called Message displayed as Title.

Nested Properties

For nested properties you need to create an instance of DynamicPropertyCollection and add all the properties to this collection. And then pass that instance to a DynamicObject

  1. // Creates a collection to hold all the nested properties
  2. var collection = new DynamicPropertyCollection();
  3. collection.AddProperty("uri", null, typeof(string));
  4.  
  5. // Create a dynamic object using the initialised properties
  6. var nestedProperty = new DynamicObject("", collection);
  7.  
  8. // And add the nestedProperty to the 'Properties' in the builder
  9. Properties.AddProperty("ProcSource", nestedProperty, typeof(DynamicObject)).DisplayName = "ProcSource";

Creating custom activity

A custom activity in a flow editor implements ICustomActivity interface. Which is very similar to ElementItemBuilderBase with one difference. It allows you to create an Printable object and return it to be displayed as a not in flow.

public CustomNode CreateNode(IDiagramNodeCreationInfo info, IElementItem element);

Please note for easier implementation you can drive a custom activity class from CustomActivityBase

Shapes

There are different ways to create a CustomNode's shape. In FlexRule Designer we use Shape to build different shapes on diagram. MindFusion.Diagramming.Shapes in assembly MindFusion.Diagramming will give you some predefined shapes you can use. Check http://www.mindfusion.eu/onlinehelp/flowchartnet/Table_of_Predefined_Shapes.htm for list of predefined shapes.

  1. public override CustomNode CreateNode(IDiagramNodeCreationInfo info, IElementItem element)
  2. {
  3.     return new CustomNode(Shapes.RoundRect)
  4.     {
  5.         FillColorFrom = Color.FromArgb(255, 224, 192),
  6.         FillColorTo = Color.FromArgb(255, 128, 0),
  7.         Size = new Size(10, 10),
  8.     };
  9. }

MindFusion.Diagramming.Shapes provides some pre-built shapes that can be used. Otherwise you can implement your own using by following the bellow link:

http://www.mindfusion.eu/onlinehelp/flowchartnet/T_MindFusion_Diagramming_Shape.htm

Registering custom activity

You need to register your custom activity in the setting file: FlexRule.Designer.Settings.config

In the Activities section of the setting add your custom node:

  1.   <Activities>
  2.     <Validator assembly="FlexRule.Designer.ValidationEngine.dll" type="FlexRule.Designer.ValidationEngine.CustomActivity" />
  3.     <Notification assembly="FlexRule.Designer.Core.dll" type="FlexRule.Designer.Notifications.CustomActivity" />
  4.     <Notif assembly="FlexRule.Designer.Sample.FlowCustomActivity.dll" type="FlexRule.Designer.Sample.FlowCustomActivity.NotifCustomActivity" />
  5.   </Activities>

And after registering the new activity, you can start dragging, dropping new activity and connecting other nodes to it.

NewCustomActivity.png

Download the sample code

Download the following project: http://www.flexrule.com/wp-content/uploads/2015/03/FlexRule.Designer.Sample.FlowCustomActivity2.zip

Build the class library and put the assembly into designer folder. When you create a new project, in property properties window select this assembly as one of your builders.


Migration from Old Designer

Assemblies and Types

  • Assembly FlexRule.Designer.Controls.Diagram has been replaced by MindFusion.Diagramming
  • Type ICustomActivity method signature has been changed from
Printable CreateNode(IFlowNodeCreationInfo info, IElementItem element);

to

 CustomNode CreateNode(IDiagramNodeCreationInfo info, IElementItem element);
  • Type Printble and LinkableCustomFigureContainer are replaced by CustomNode that contains a Shape

Re-defined Shapes and Custom Shapes

and to use a predefined shape by its identifier you can simply use

Shape.FromId(string identifier) 

to return the instance of a predefined shape by its identifier. For example Shape.FromId("Display") returns a shape for a Display. Diagram Shape Display.png