Start by creating a new empty Class Library project.
Once the project has been created, add a reference to the microsoft.sharepoint.WorkflowActions assembly. The location of this assembly can be found in the ISAPI folder of the SharePoint hive. In case you’re not aware of where the hive is located, it can be found here:
[install drive]:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14
You should also add the following three references from the GAC.
- System.Workflow.Activities
- System.Workflow.ComponentModel
- System.Workflow.Runtime
Delete the auto generated Class1.cs file and create a folder to create the custom activity code in. For example, WorkflowActivities\CustomActivity
Next, add a new Class item to the folder, calling it MyCustomActivity.
Add the following using directives to the new class:
- using System.ComponentModel;
- using System.Workflow.Activities;
- using System.Workflow.ComponentModel;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Workflow;
- using Microsoft.SharePoint.WorkflowActions;
[ActivityToolboxDisplay("Custom Activities", true)] public class MyCustomActivity : SequenceActivity { } |
- Activity
- Delay Activity
- Parallel Activity
- Replicator Activity
- Wait For Document Unlock Activity
OK, back to the project.
We just added the ActivityToolboxDisplay class attribute, the purpose of this is to define the tab where the activity will appear in the toolbox for Visual Studio. Other attributes that you should look at adding include:
- ActivityValidator – Add code to validate the activity at both design and runtime
- ToolboxBitmap – Add a custom icon to your workflow activity
A dependency property basically provides the plumbing for things like property resolution, data binding and change notification. One important thing to note here is that they behave completely differently to normal .NET properties in that they are not stored in an instance variable but instead use the dependency property framework to register themselves and are subsequently retrieved from this framework based on the rules defined by the property registration. They’re not the most elegant feature of WF (or WPF) but is something that you have to live with if you want to work with workflows.
The main feature that we’ll be concentrating here for dependency properties is activity binding. Basically, activity binding gives a dependency property an object to evaluate for the value requested. This can point to properties, fields or methods of activities inside a workflow. For example, one activity could bind its input to the output of a previous activity, this makes data flow a huge part of the workflow model.
For a basic workflow activity that will be interacting with the workflow instance, we need to define three dependency properties.
public static readonly DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(MyCustomActivity)); public static readonly DependencyProperty __ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(MyCustomActivity)); public static readonly DependencyProperty __ItemIdProperty = DependencyProperty.Register("__ItemId", typeof(SPItemKey), typeof(MyCustomActivity)); |
When defining a property there are some simple rules that must be followed.
- The property must be declared as public so that all property system calls, including cross-assembly, can access it.
- It must also be static because this isn’t a definition that should ever change, i.e. the system expects to get consistent results
- It should also be marked as readonly, although it will work without this. It should be marked like this to stop code outside the registering class altering it.
- The name used when registering the property should have a corresponding public .NET property
__ListId as you might have guessed relates to the GUID, or name, of the list the activity belongs to.
Lastly, __ItemId relates to the identifier of the list item for the activity.
Following these rules, you can easily add your own custom dependency properties to the activity.
Now to add the related .NET properties for the three dependency properties.
[Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public WorkflowContext __Context { get { return (WorkflowContext)base.GetValue(__ContextProperty); } set { base.SetValue(__ContextProperty, value); } } [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public string __ListId { get { return (string)base.GetValue(__ListIdProperty); } set { base.SetValue(__ListIdProperty, value); } } [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public SPItemKey __ItemId { get { return (SPItemKey)base.GetValue(__ItemIdProperty); } set { base.SetValue(__ItemIdProperty, value); } } |
As mentioned, the object of this activity is to send an email to an address passed into the activity. To do this we shall define our own dependency property and associated class property:
public static readonly DependencyProperty EmailToProperty = DependencyProperty.Register("EmailTo", typeof(string), typeof(MyCustomActivity)); [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public string EmailTo { get { return (string)base.GetValue(EmailToProperty); } set { base.SetValue(EmailToProperty, value); } } |
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { return base.Execute(executionContext); } |
If you need to initialise member values before this event, the best place would be to override the Initialize method.
The execute method for this example should look like:
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { // Deliberately no error checking\handling to keep method as simple as possible - not recommended! // Get the actual GUID object for the list Guid listGuid = Helper.GetListGuid(__Context, __ListId); // Get SPList item instance SPList listInstance = __Context.Web.Lists[listGuid]; // Get the SPItem instance for the workflow activity SPListItem itemInstance = __Context.GetListItem(listInstance, __ItemId); // Get the title of the item, which will be used in email body\subject. string itemTitle = itemInstance["Title"].ToString(); string subject = string.Format("'{0}' workflow message", itemTitle); string body = string.Format("A test email message for list item '{0}'", itemTitle); System.Collections.Specialized.StringDictionary headers = new System.Collections.Specialized.StringDictionary { {"subject", subject}, {"from", "webmaster@SharePoint.com"}, {"to", EmailTo} }; // Using the static SendEmail method to send our activity email. Microsoft.SharePoint.Utilities.SPUtility.SendEmail(__Context.Web, headers, body); return ActivityExecutionStatus.Closed; } |
The method demonstrates how you can reference the list and item associated with the workflow activity.
One final thing that you should do is to sign the assembly.
This concludes the first part of this post. The following post continues with this example and involves creating and deploying a workflow that uses the custom activity.
No comments:
Post a Comment