Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

AVEVA™ Work Tasks

Custom Property

  • Last UpdatedJun 10, 2024
  • 8 minute read

Using the AVEVA Work Tasks defined interfaces, custom property can be created that can be implemented by any custom activity. This custom property will be available in the Activity Properties of the custom activity implementing this.

Procedure

Dll Reference

System.Web

Workflow.NET.NET2

Workflow.NET.Web.Designer.NET2

Namespace Used

System

System.IO

System.Xml

System.Xml.XPath

System.Collections.Generic

System.Text

Workflow.NET

Workflow.NET.Engine

Workflow.NET.Interfaces

Workflow.NET.Web.Designer.Interfaces

Note: When you package a workflow with custom activity, if you need to correct the custom activity in the target repository, follow the steps mentioned in "Resolving Custom Activities" and "Correcting Custom Activities". For more information, see Resolve Custom Activities and Correct Custom Activities.

To create Custom Property

  1. Go to Start->Visual Studio 2005->New->Projects->Class Library->SampleProperty. This step will create a Class Library project in the path specified by the name SampleProperty.

  2. Create two class files under the project SampleProperty.cs and SamplePropertyWebUI.cs

  3. There are two interfaces of AVEVA Work Tasks which will be used while developing a custom property.
    One is IPropertyTypeWebUI interface and the other is IPropertyType2 interface.

  4. The IPropertyTypeWebUI interface is used to render the property in the Process Designer.

    Note: Add a reference to Skelta.Bpm.Quickflow.Bridge.dll.

  5. The IPropertyType2 interface is used for setting and getting the details of the property. IPropertytype2 interface internally inherits the IpropertyType interface.

    SampleProperty.cs

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Xml;

    using System.Xml.XPath;

    using System.IO;

    using Workflow.NET;

    using Workflow.NET.Engine;

    using Workflow.NET.Interfaces;



    namespace Workflow.NET.SampleProperty

    {

    /// <summary>

     /// Class for Custom Property which implements the IPropertyType2 interface and also Inherits the ExpressionHandler Class

    /// </summary>

    public class SampleProperty : ExpressionHandler, IPropertyType2

    {


    string _value;

    string _defaultvalue;

    bool _mandatory;

    string _name;

    string _choicestring;

    InitializationContext _contextInfo;

    /// <summary>

    /// Initializes a new instance of SampleProperty class.

    /// </summary>

    public SampleProperty()

    {


    }

    #region IPropertyType2 Members




     /// <summary>

    /// The class  InitializationContext provides the current running instance details of workflow at /// runtime. Used to resolve variable or contents used during runtime.  (e.g :  /// ContextInfo.CurrentContext.ExecutionID) and also design time details.

    /// This class is used in property for accessing the design time details

    /// like applicationname,workflowname,database details,Workflow.Net.config object.

     /// </summary>  

     InitializationContext IPropertyType2.ContextInfo

    {

    get

    {

    return _contextInfo;

    }


    set

    {

    _contextInfo = value;

    }

    }


    #endregion


    #region IPropertyType Members


    /// <summary>

    /// Clearing the value of the custom property.

    /// </summary>

    void IPropertyType.Clear()

    {

    _value = "";

    if (_defaultvalue != null)

    if (_defaultvalue != "")

    _value = _defaultvalue;

    }


    /// <summary>

    /// Clones the property instance.

    /// Improper implementation of clone method could result in an unexpected value display.

    /// We have to make sure the required properties  are set from the clone method.

    /// </summary>

    /// <returns></returns>

    IPropertyType IPropertyType.Clone()

    {

    SampleProperty oSampleProperty = new SampleProperty();

    oSampleProperty._mandatory = _mandatory;

    oSampleProperty._defaultvalue = _defaultvalue;

    oSampleProperty._name = _name;

    oSampleProperty._value = _value;

    oSampleProperty._choicestring = _choicestring;

    return oSampleProperty;

    }


    /// <summary>

    /// Name of the Custom Property

    /// </summary>

    string IPropertyType.Name

    {

    get

    {

    return _name;

    }


    set

    {

    _name = value;

    }

    }


    /// <summary>

    /// This is specific to this custom property which is used

    /// to fill the predefined values for the property.

    /// </summary>

    public string ChoiceString

    {

    get

    {

    return _choicestring;

    }

    }



    /// <summary>


    /// If there is some definition which needs to be passed and read for initializing the property.

    /// The definition is defined along with the property in the actions.xml.


    /// (for ex: In the property below (sample) configuration, the nodes choice is the definition for the property and which is used for filling the property default values.

    ///</summary>

    /*

    <property name="Sample Property" type="sample" defaultvalue="N">

     <choice>Yes;Yes</choice>

     <choice>No;No</choice>

     </property>

    */

    void IPropertyType.ReadDefinitionXml(System.Xml.XmlTextReader xtr)

    {

    _defaultvalue = xtr.GetAttribute("defaultvalue");

    string mandatory = xtr.GetAttribute("mandatory");

    if (mandatory != null)

    {

    if (mandatory.ToUpperInvariant() == "TRUE")

    _mandatory = true;

    else

    _mandatory = false;

    }


    _choicestring = "";


    while (!xtr.EOF)

    {

    xtr.MoveToElement();

    if (xtr.NodeType == XmlNodeType.EndElement && xtr.Name == "property") break;

    if (xtr.NodeType == XmlNodeType.Element)

    {

    if (xtr.Name == "choice")

    {

    string _choicestringdisp = xtr.GetAttribute("displayas");

    string _choicestringValue = xtr.ReadElementString();


    if (_choicestringdisp == null || _choicestringdisp == "")

    _choicestringdisp = _choicestringValue;


    _choicestring += (_choicestring == "" ? "" : ",") + _choicestringdisp + ";" + _choicestringValue;

    }

    }

    xtr.Read();

    }

     

    }



    /// <summary>

     /// The ReadXml method is called whenever the action which is having the property is loaded from process designer.

    /// The ReadXml method is used to read the Xml data persisted in the process definition.

     /// ReadExpressionXml method reads the defined expression for the property from the

    /// Xml format and stored in the property Codeclassname and Expressionstring while loading the action.

    /// </summary>


    void IPropertyType.ReadXml(System.Xml.XmlTextReader xtr)

    {

    if (!ReadExpressionXml(xtr))

    _value = xtr.ReadElementString();

    }

    /// <summary>

    /// Gets a string that represents the property. Used Internally while rendering report.

    /// </summary>

    /// <returns></returns>

    public override string ToString()

    {

    return Value;

    }


    /// <summary>

    /// Gets a string that represents the property. Used internally while rendering report.

    /// </summary>

    /// <param name="XPath">The xpath expression for getting a specific string.</param>

    /// <returns>A string that represents the property.</returns>


    public virtual string ToString(string XPath)

    {

    string strValue = "";

    string strXml = ToString(true);

    XPathDocument xpathDoc = new XPathDocument(new StringReader(strXml));

    XPathNavigator xpathNavigator = xpathDoc.CreateNavigator();

    XPathNodeIterator iterator = xpathNavigator.Select(XPath);

    while (iterator.MoveNext())

    {

    strValue += iterator.Current.Value;

    }

    return strValue;

    }

    /// <summary>

    /// Gets a string that represents the property. Used internally while rendering report.

    /// </summary>

    /// <param name="ReturnXml">Determines whether the string should be returned in XML format.</param>

     /// <returns>A string that represents the property.</returns>

    public virtual string ToString(bool ReturnXml)

    {

    string strValue = "";

    if (ReturnXml)

    {

    MemoryStream stream = new MemoryStream();

    StreamWriter writer = new StreamWriter(stream);

    XmlTextWriter xmlWriter = new XmlTextWriter(writer);

    xmlWriter.WriteStartElement("property");

    xmlWriter.WriteAttributeString("name", _name);

    xmlWriter.WriteAttributeString("value", Value);

    xmlWriter.WriteAttributeString("type", "SampleProperty");

    xmlWriter.WriteEndElement();

    xmlWriter.Flush();

    stream.Position = 0;

    StreamReader reader = new StreamReader(stream);

    strValue = reader.ReadToEnd();

    stream.Close();

    }

    else

    {

    strValue = ToString();

    }

    return strValue;

    }



    /// <summary>

    ///  This property is used to set  or get the Value for the custom property.

    ///  Using this property the value of the property can be accessed at runtime.

     ///  This method is used to evaluate the expression value defined in the property.

     ///  We would have defined variable or content or xml variables or hard coded value in the expression builder

     /// for a property in an action.

     /// GetProcessedValue method is used to evaluate these variable or content or xmlvariable or hard coded value

    /// and assigned as the value to the property.  

    /// </summary>

    public string Value

    {

     get

    {

    if (_value == null || _value == "")

    {

    if (_defaultvalue != null && _defaultvalue != "")

    _value = _defaultvalue;

    }


    if (this.IsExpression)

    {

    if (_contextInfo != null)

    {

    if (_contextInfo.CurrentContext != null)

    return this.GetProcessedValue(_contextInfo.CurrentContext).ToString();

    else

    return _value;

    }

    else

    return _value;

    }

    else

    return _value;

    }


    set

    {

    string ErrorString = "";

    if (((IPropertyType)this).isValidValue(value, ref ErrorString))

    _value = value;

    else

    throw new System.Exception(ErrorString);


     }  

    }


    /// <summary>

    /// This method is used to write the property value data in the Process Definition in the XML format.

    /// This method will be called when the activity in which this property is used is rendered.

    /// This method is used to persist data which is saved after rendering the property in process designer

    /// The value persisted here is read back from the ReadXml method of the IPropertytype interface.

    /// WriteExpressionXml method writes the defined expression for the property in  Xml format.

    /// This XML contains the details of the expression defined and associated class name

     /// of the compiled code.

    /// </summary>


    void IPropertyType.WriteXml(System.Xml.XmlTextWriter xtw)

    {

    if (!WriteExpressionXml(xtw))

      xtw.WriteCData(_value);

    }


     /// <summary>

    /// Used to check the whether the property value is valid or not.

     /// Gets called while process definition is validated on deploy of the workflow.

     /// IsExpression property is used to check Whether any expression

    /// is defined for a property for an action.

    /// </summary>

    /// <param name="Value"></param>

    /// <param name="ErrorString"></param>

    /// <returns></returns>

    bool IPropertyType.isValidValue(string Value, ref string ErrorString)

    {

    bool valid = false;

    if (this.IsExpression)

    {

    return true;

     }

    if (_mandatory)

    {

    if (Value != null)

    {

    if (Value != "" && Value != "0")

    valid = true;

    }

    if (!valid)

    {

    if (ErrorString != null)

    {

    SkeltaResourceSetManager resourceManager = new SkeltaResourceSetManager();

    ErrorString = resourceManager.GlobalResourceSet.GetString("Value cannot be left empty") + " [" + resourceManager.GlobalResourceSet.GetString(_name) + "]";

    }

    _value = "";

    }

    }

    else

    valid = true;

    return valid;

    }


    #endregion

    }

    }

    SamplePropertyWebUI.cs

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using Workflow.NET;

    using Workflow.NET.Web.Designer.Interfaces;


    namespace Workflow.NET.SampleProperty

    {

    /// <summary>

    /// Class which implements IPropertyTypeWebUI interface for rendering the custom property

    /// in the process designer.

    /// </summary>

    public class SamplePropertyWebUI :IPropertyTypeWebUI

    {


    /// <summary>

    /// Initializes a new instance of SamplePropertyWebUI class.

    /// </summary>

    public SamplePropertyWebUI()

    {


    }

    #region IPropertyTypeWebUI Members



    /// <summary>

    ///  This method is used to render the UI for the Property (The right pane which gets displayed /// for an activity to display it's collection  of properties) in the process designer.

    /// </summary>


    /// <param name="oAction">Action class details to which this custom property belongs.</param>

    /// <param name="oProperty">Property object which is used for getting value,name so that the value will be retained

    ///  if you are loading the action after setting the property value.</param>

    /// <param name="oWebDesigner">ProcessDesigner control to which the control used in the custom property is added.

    /// here in this custom property dropdownlist control is added.</param>

    public void Draw(Action oAction, Property oProperty, Workflow.NET.Web.Designer.ProcessDesigner oWebDesigner)

    {

    string output;

    Workflow.NET.SampleProperty.SampleProperty oSampleProperty =  (Workflow.NET.SampleProperty.SampleProperty)oProperty.PropertyHandler;



    output = "<TABLE class='propertiesboxinputstable' cellpadding=0 cellspacing=0 width=100%>";

    output += "<TR><TD>";

    oWebDesigner.Controls.Add(new LiteralControl(output));


    string[] Choices = oSampleProperty.ChoiceString.Split(new char[] { ',' });

    DropDownList oChoice = new DropDownList();

    oChoice.CssClass = "propertiesboxinputs";

    oChoice.ID = "ActionProperty" + oAction.Name + oProperty.Name + "SampleProperty";

    oChoice.Items.Add(new ListItem(oWebDesigner.ActionsDefinition.GlobalResourceSet.GetString("Select Option"), "0"));

    foreach (string sItem in Choices)

    {

    string[] valueArr = sItem.Split(';');

    oChoice.Items.Add(new ListItem(oWebDesigner.ActionsDefinition.GlobalResourceSet.GetString(valueArr[0]), valueArr[1]));

    }

    bool selected = false;

    if (oProperty.Value != null)

    {


    ListItem oSelectedItem = oChoice.Items.FindByValue((string)oProperty.Value);

    if (oSelectedItem != null)

    {

    oSelectedItem.Selected = true;

    selected = true;

    }

    }

    oWebDesigner.Controls.Add(oChoice);

    output = "</TD></TR></TABLE>";

    oWebDesigner.Controls.Add(new LiteralControl(output));

     

    }



    /// <summary>

    /// This is to handle the Custom Property details after clicking on Save button from the Activity

    /// which is using this custom property in the Process designer.

    ///

    /// </summary>

    /// <param name="oAction">Action class details to which this custom property belongs.</param>

    /// <param name="oProperty">Property object which is used for getting value,name so that the value will be retained</param>

    /// <param name="oWebDesigner">Getting the Control used for the custom property from the ProcessDesigner control.In this custom

    /// property we have used dropdown list control.</param>

    /// <param name="ErrorString">Checking for error in the input for the custom property.</param>

    /// <returns>true if the input is valid; else false</returns>


    public bool HandlePostBack(Action oAction, Property oProperty, Workflow.NET.Web.Designer.ProcessDesigner oWebDesigner, ref string ErrorString)

    {

    bool valid = false;

    ErrorString = "";

    DropDownList oChoice = (DropDownList)oWebDesigner.FindControl("ActionProperty" + oAction.Name + oProperty.Name + "SampleProperty");

    if (oChoice != null)

    {

    if (oProperty.PropertyHandler.isValidValue(oChoice.SelectedItem.Value, ref ErrorString))

    {

    oProperty.Value = oChoice.SelectedItem.Value;

    valid = true;

    }

    }


    return valid;

    }


    /// <summary>

    /// This method is used where in we need to render as html.

    /// </summary>

    /// <returns></returns>

    public Workflow.NET.Web.Designer.PropertyPage CreatePropertyPageInstance()

    {

    return null;

    }


    #endregion

    }

    }

  6. Build the Project.

  7. Place the dll in the AVEVA Work Tasks Installed path\BPM.NET bin directory.

  8. Configuration of Custom Property in the Actions.XML file (which will be located in WorkflowElements\Default\en-us\Actions\XML). We need to add the custom property under the properties tag with the classname,assemblyname,webuiclassname and webuiassemblyname for the custom property.

    <propertytype name="SampleProperty" classname="Workflow.NET.SampleProperty.SampleProperty"

    assemblyname="bin\SampleProperty.dll" webuiclassname="Workflow.NET.SampleProperty.SamplePropertyWebUI"

    webuiassemblyname="bin\SampleProperty.dll"/>

  9. Configuration of Custom Property for the Custom Engine Activity (Specified in the next topic) in the Actions.XML file.

    <action name="CustomActivity" displayname="Custom Activity">

     <description>Custom activity. </description>

     <image resourcename="Customac.png"></image>

     <handler classname="SampleCustomAction.CustomAction" assembly="bin\SampleCustomAction.dll"></handler>

     <properties>

     <property name="Sample Property with Expression" type="SampleProperty" mandatory="true">

     <choice>Yes;Yes</choice>

     <choice>No;No</choice>

     </property>

     </properties>

     <return>

     <value helpstring="Success">Success</value>

     <value helpstring="UnSuccess">UnSuccess</value>

     </return>

    </action>

  10. After the configuration of the custom property in the Actions.xml the Property will appear in the Process Designer for the specified custom activity as below. See that there is a mandatory icon that gets displayed when it is viewed from the Process Designer. This indicates that without providing the value to this property we cannot publish the workflow. The property is made mandatory using the attribute mandatory="true".

    The following figure shows the property value set through the Expression Editor.

Related Links
TitleResults for “How to create a CRG?”Also Available in