Creating a workflow in SharePoint Designer consists of selecting Conditions and Activities to build the logic and determine what actions the workflow performs. When you select an activity or condition a sentence appears in the WorkFlow Designer screen with underlined phrases that allow you click on them to set the workflow’s parameters. These parameters can be selecting a column on a list item and a value to give it, or they can be a user’s account name, a url to a site or something more complex like an email definition that includes a To address, CC address, a subject and a body.
When creating custom workflow activities and conditions for SharePoint in Visual Studio, you create a code class to build its functionality as well as a .actions file, both of which define the parameters. SharePoint Designer uses the .actions to be able to add the activity or condition to a workflow. You must specify the type for each parameter within both the class and the .actions file as well as the DesignerType in the .actions file. When a user clicks on a parameter, different windows open to allow the user to input the values according to the DesignerType given to the parameter. The DesignerType is simply an enumeration that SharePoint Designer uses to determine what type of form to display to gather information on each parameter. For example, a DesignerType of DropDown will present a drop down menu at the location of the parameter phrase in the sentence, whereas a DesignerType of Email will open a pop up window with fields for the To address, CC address, Subject and Body where each of these fields is defined as a parameter of different types in code and returned from the Designer form as an object of that type.
For an activity to send an email, the simple class definition may look something like this:
public partial class SendAnEmail : SequenceActivity
{
#region Dependency Properties
public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(SendAnEmail));
public static DependencyProperty RecipientCCProperty = DependencyProperty.Register("RecipientCC", typeof(ArrayList), typeof(SendAnEmail));
public static DependencyProperty RecipientFromProperty = DependencyProperty.Register("RecipientFrom", typeof(string), typeof(SendAnEmail));
public static DependencyProperty RecipientTOProperty = DependencyProperty.Register("RecipientTO", typeof(ArrayList), typeof(SendAnEmail));
public static DependencyProperty SubjectProperty = DependencyProperty.Register("Subject", typeof (string), typeof (SendAnEmail));
public static DependencyProperty BodyProperty = DependencyProperty.Register("Body", typeof(string), typeof(SendAnEmail));
#endregion
public SendAnEmail()
{
InitializeComponent();
}
#region Activity Parameters
[Description("Recipient address")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ArrayList RecipientTO
{
get { return ((ArrayList) (base.GetValue(RecipientTOProperty))); }
set { base.SetValue(RecipientTOProperty, value); }
}
[Description("Carbon copy recipient")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ArrayList RecipientCC
{
get { return ((ArrayList) (base.GetValue(RecipientCCProperty))); }
set { base.SetValue(RecipientCCProperty, value); }
}
[Description("Subject")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Subject
{
get { return ((string) (base.GetValue(SubjectProperty))); }
set { base.SetValue(SubjectProperty, value); }
}
[Description("HTML Body")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string Body
{
get { return ((string) (base.GetValue(BodyProperty))); }
set { base.SetValue(BodyProperty, value); }
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get { return ((WorkflowContext) (base.GetValue(__ContextProperty))); }
set { base.SetValue(__ContextProperty, value); }
}
[Description("Sender address. If this value is not specified, default sharepoint sender address will be used")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string RecipientFrom
{
get { return ((string) (base.GetValue(RecipientFromProperty))); }
set { base.SetValue(RecipientFromProperty, value); }
}
#endregion
protected override ActivityExecutionStatus OnExecute(ActivityExecutionContext executionContext, Microsoft.SharePoint.Workflow.ISharePointService service)
{
}
}
The .actions file would look like:
<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions Sequential="then" Parallel="and">
<Action Name="Send E-mail Extended" ClassName="SPSolutions.SharePoint.WorkflowEssentials.Activities.SendEmailExtended" Assembly="SPSolutions.SharePoint.WorkflowEssentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=08a33cc09f006379" AppliesTo="all" Category="SharePoint Solutions' WorkFlow Essentials">
<RuleDesigner Sentence="Send a %1 e-mail to %2 and display %3 as the sender">
<FieldBind Field="IsMessageUrgent" DesignerType="Dropdown" Text="choose" Id="1">
<Option Name="urgent" Value="true"/>
<Option Name="non urgent" Value="false"/>
</FieldBind>
<FieldBind Field="RecipientTO,RecipientCC,Subject,Body" Text="this address" DesignerType="Email" Id="2"/>
<FieldBind Field="RecipientFrom" Text="this address" Id="3" DesignerType="stringbuilder" />
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="IsMessageUrgent" Type="System.String, mscorlib" Direction="In" InitialValue="false" />
<Parameter Name="RecipientTO" Type="System.Collections.ArrayList, mscorlib" Direction="In" />
<Parameter Name="RecipientCC" Type="System.Collections.ArrayList, mscorlib" Direction="Optional" />
<Parameter Name="RecipientFrom" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="Subject" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="Body" Type="System.String, mscorlib" Direction="In" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>
In terms of SharePoint 2010, there are several new DesignerTypes and some of the existing ones have changed slightly in the types of parameters that they return. Below is an overview of most, if not all, of the DesignerTypes included in 2010.
Designer Type
|
New to 2010
|
Parameter(s) returned
(example parameter name) |
Parameter type
|
Description
|
||||||||||||
Assignment | Yes | AssignedTo | System.String | |||||||||||||
CC | System.Collections.ArrayList | |||||||||||||||
Comments | System.String | |||||||||||||||
Subject | System.String | |||||||||||||||
Duration | System.Double | |||||||||||||||
DurationUnit | Microsoft.Office.Workflow.Actions.DurationUnit | |||||||||||||||
DueDate | System.DateTime | |||||||||||||||
Boolean | No | Yes/No | System.Boolean | |||||||||||||
ChooseDocLibItem | No | DocumentLibrary ItemName |
System.String | Selects list item from a Document Library based on a field value. | ||||||||||||
ChooseListItem | Changed | ListId | System.String | Selects List Item from list based on a field value. | ||||||||||||
ListItem | Microsoft.SharePoint.Workflow.SPItemKey | |||||||||||||||
ContentType | Yes | ContentTypeID | System.String | |||||||||||||
CreateListItem | No | |||||||||||||||
DataSourceDropDown | Yes | DataSourceName | System.String | Drop Down list of DataSources for list associated with workflow. | ||||||||||||
Date | No | Date | System.DateTime | Date Time Selector | ||||||||||||
Dependent | Yes | S ystem.String | Based on TextArea DesignerType | |||||||||||||
DropDown | No | System.String | ||||||||||||||
No | To | System.Collection.ArrayList | ||||||||||||||
CC | System.Collection.ArrayList | |||||||||||||||
Subject | System.String | |||||||||||||||
Body | System.String | |||||||||||||||
Hide | No | Used to hide parameter from users in Designer | ||||||||||||||
FieldNames | No | FieldName | System.String | Select field column from list associated with workflow. | ||||||||||||
Float | No | System.Float | ||||||||||||||
HyperLink | No | Link | System.String | |||||||||||||
Integer | No | System.Int32 | ||||||||||||||
ListItems | Yes | System.String | ||||||||||||||
ListNames | No | ListTitle | System.String | Drop Down with list of list names from current site | ||||||||||||
Operator | No | Drop-down list box control that includes operators used to evaluate each side of the RuleDesigner sentence. Operators are static and must be added in Options elements. | ||||||||||||||
ParameterNames | No | System.String | Allows the creation of a new variable for the workflow. | |||||||||||||
Person | No | Users | System.Collections.ArrayList | Allows selection of multiple users | ||||||||||||
SinglePerson | Changed | System.Object | Allows selection of a single user | |||||||||||||
StringBuilder | No | System.String | ||||||||||||||
Survey | No | Creates a task in the workflow to gather data from users. | ||||||||||||||
TaskSummary | Yes | TaskProcessName | System.String | |||||||||||||
Text | No | System.Object | ||||||||||||||
TextArea | No | System.String | ||||||||||||||
UpdateListItem | No | ListItemToUpdate | ||||||||||||||
WritableFieldNames | No | System.String | Drop down including list of fields on list that are writable |
So far, the main changes appear in the return types of the ChooseListItem and SinglePerson DesignerTypes. In SharePoint 2010, the SinglePerson form returns a generic System.Object rather than a string, and the ChooseListItem’s ListItem parameter returns a new type of SPItemKey instead of a string.
To accommodate the return of an Object from the SinglePerson DesignerType form, simple define the parameter as System.Object in the .actions file and the DependencyProperty in code, then define the activities property as a String and explicitly convert it from object to string in the get statement.
The new .actions file section would now look like:
<Action Name="myNewActivity"
ClassName="SPSolutions.SharePoint.WorkflowEssentials.Activities. myNewActivity "
Assembly="SPSolutions.SharePoint.WorkflowEssentials, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=08a33cc09f006379"
AppliesTo="all" Category="SharePoint Solutions' WorkFlow Essentials">
<RuleDesigner Sentence="Get %1">
<FieldBind Field="SharePointUser" DesignerType="SinglePerson" Text="this person" Id="1" />
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="ListId" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="SharePointUser" Type="System.Object, mscorlib" Direction="In" />
</Parameters>
</Action>
The new declaration in the class for parameters returned from the ChooseListItem DesignerType would now look like:
public static DependencyProperty SharePointUserProperty =
DependencyProperty.Register("SharePointUser", typeof(object), typeof(myNewActivity));
[Description("SharePoint User")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SharePointUser
{
get { return ((string) (base.GetValue(SharePointUserProperty))); }
set { base.SetValue(SharePointUserProperty, value); }
}
The SPItemKey type appears to be a wrapper for returning an instance of the list item. It has two public properties: Id as Int32 and Key as String where the Id is the selected ListItem’s Id and Key is the name. You can programmatically get an instance of the SPListItem by passing the SPList object as a parameter to SPItemKey.GetItemByIdFromList(SPList).
SPItemKey() | Returns object with Key = string.Empty, Id = -1 | |||
SPItemKey(Int32 id) | Returns object with Key = id.ToString(), Id = id | |||
SPItemKey(string key) | Returns object with Key = key, Id =Int32.Parse(key) |
Equals(object rhs) | Boolean | Determines if current object equals object passed in | ||||||
Equals(object objA, object objB) | Boolean | Determines if two objects are equal | ||||||
Finalize() | Void | Empty override of base System.Object.Finalize() | ||||||
FromItem(SPListItem listItem) | SPItemKey | Returns SPItemKey object given SPListItem | ||||||
GetHashCode() | Int32 | Generated from Exclusive OR operation between Id and Key.GetHashCode() | ||||||
GetItemByIdFromList(SPList) | SPItemKey | Returns SPListItem from given list from Key or Id property | ||||||
GetType() | Type | Returns Type for current SPItemKey | ||||||
IsEmpty(SPItemKey) | Boolean | Static. Returns true if both Id and Key properties are null or if Id = -1 and Key is null or string.Empty | ||||||
MemberwiseClone() | Object | Creates a shallow copy of the current SPItemKey | ||||||
op_Equality(SPItemKey lhs, object rhs) | Boolean | Static. Determines if SPItemKey and Object are both null or equal | ||||||
op_Equality(SPItemKey lhs, SPItemKey rhs) | Boolean | Static. Determines if two SPItemKey objects are both null or equal | ||||||
op_Inequality(SPItemKey lhs, object rhs) | Boolean | Static. Determines if SPItemKey and Object are both not null or not equal | ||||||
op_Inequality(SPItemKey lhs, SPItemKey rhs) | Boolean | Static. Determines if two SPItemKey objects are both not null or not equal | ||||||
ReferenceEquals(object objA, object objB) | Boolean | Static. Determines if two objects are references to the same object | ||||||
ToString() | String | Comma separated string: Key, Id |
Empty | SPItemKey | Static. Returns empty object using empty constructor | ||||||
Id | Int32 | Read/Write. Id of SPItemKey | ||||||
Key | String | Read/Write. Key of SPItemKey |
To access the new type in your class, be sure to reference the new assemblies for SharePoint 14 including C:Program FilesCommon FilesMicrosoft Sharedweb server extensions14ISAPIMicrosoft.SharePoint.dll and C:Program FilesCommon FilesMicrosoft Sharedweb server extensions14ISAPIMicrosoft.SharePoint.WorkflowActions.dll.
The new .actions file section would now look like:
<Action Name="myNewActivity"
ClassName="SPSolutions.SharePoint.WorkflowEssentials.Activities. myNewActivity"
Assembly="SPSolutions.SharePoint.WorkflowEssentials, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=08a33cc09f006379"
AppliesTo="all" Category="SharePoint Solutions' WorkFlow Essentials">
<RuleDesigner Sentence="Get %1">
<FieldBind Field="ListId, ListItem" DesignerType="ChooseListItem" Text="List Item" Id="1" />
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="ListId" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="ListItem" Type="Microsoft.SharePoint.Workflow.SPItemKey, Microsoft.SharePoint" Direction="In" />
</Parameters>
</Action>
The new declaration in the class for parameters returned from the ChooseListItem DesignerType would now look like:
public static DependencyProperty ListIdProperty = DependencyProperty.Register("ListId", typeof(string), typeof(myNewActivity));
[Description("List Id")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ListId
{
get { return ((string) (base.GetValue(ListIdProperty))); }
set { base.SetValue(ListIdProperty, value); }
}
public static DependencyProperty ListItemProperty = DependencyProperty.Register("ListItem", typeof(SPItemKey), typeof(myNewActivity));
[Description("List Item")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public SPItemKey ListItem
{
get { return ((SPItemKey) (base.GetValue(ListItemProperty))); }
set { base.SetValue(ListItemProperty, value); }
}
Looking at the SharePoint 14 codebase, there are several new activities included in the namespace Microsoft.SharePoint.WorkflowActions.WithKey that use the SPItemKey object includng the same ListItem property as well as additional properties such as TaskId. It appears that the workflow activities in the new version of SharePoint uses this object extensively for List Items, probably as a smaller object to pass around instead of the full SPListItem object. It will be interesting to see how, if at all, this object changes in the beta release.