I currently have a requirement for a client where there a series of folders inside a document library that get automatically generated by another workflow. The folder itself is a content type and has a series of columns that get populated during the workflow that creates it, such as product name, country, a custom ID number, etc.

Once the folder has been created, the documents inside the folder should also pick up the same properties from the parent folder, for reporting purposes. Rather than relying on the user to populate those values manually every time a new document gets added to that folder, I thought I could write a custom workflow activity that I would add to a workflow which I would attach to that document library and set to run every time a new item is added to the library.

Before I go into my code, if you’ve never created a custom workflow activity for SharePoint, take a look at this tutorial on MSDN.

using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Activities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;
namespace BeckyBertram.ActivityLibrary.Activities
{
  public partial class UpdateDocument : SequenceActivity
  {
    public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(UpdateDocument));
    public static DependencyProperty __ListItemProperty = DependencyProperty.Register("__ListItem", typeof(Int32), typeof(UpdateDocument));
    public static DependencyProperty __ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(UpdateDocument));

    [ValidationOption(ValidationOption.Required)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public WorkflowContext __Context
    {
      get { return ((WorkflowContext)(base.GetValue(UpdateDocument.__ContextProperty)));}
      set { base.SetValue(UpdateDocument.__ContextProperty, value);}
    }

    [ValidationOption(ValidationOption.Required)]
    public int __ListItem
    {
      get { return (int)base.GetValue(__ListItemProperty); }
      set { base.SetValue(__ListItemProperty, value); }
    }
    
    [ValidationOption(ValidationOption.Required)]
    public string __ListId
    {
    get { return (string)base.GetValue(__ListIdProperty); }
    set { base.SetValue(__ListIdProperty, value); }
    }

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
    {
      try
      {
        UpdateDocument();
      }
      catch (Exception e)
      {
        throw e;
      }

      return ActivityExecutionStatus.Closed;
    }

    private void UpdateDocument()
    {
      try
      {
        using (SPSite srcSite = (SPSite)(__Context.Site))
        {
          using (SPWeb srcWeb = (SPWeb)(__Context.Web))
          {
            Guid guidList = new Guid(__ListId);
            SPList docLib = srcWeb.Lists[guidList];
            SPListItem documentListItem = docLib.GetItemById(__ListItem);
            SPFolder parentFolder = documentListItem.File.ParentFolder;

            UpdateListItemValue(parentFolder, documentListItem, "ProductName");
            UpdateListItemValue(parentFolder, documentListItem, "CountryName");
            UpdateListItemValue(parentFolder, documentListItem, "CustomID");

            documentListItem.SystemUpdate();
          }
        }
      }
      catch (System.Exception e)
      {
        throw e;
      }
    }

    public UpdateDocument()
    {
      InitializeComponent();
    }

    private void UpdateListItemValue(SPFolder folder, SPListItem item, string fieldName)
    {
      SPListItem folderItem = folder.Item;
      if (folderItem[fieldName] != null)
      {
        item[fieldName] = folderItem[fieldName];
      }
    }
  }
}

All three parameters getting passed in (Content, List Guid, List Item Id) are all default parameters passed in to a workflow activity in SharePoint Desiner. Because of that, my Action element was fairly straight-forward:

<Action Name="Update Document With Folder Values" ClassName="BeckyBertram.ActivityLibrary.Activities.UpdateDocument" Assembly="BeckyBertram.ActivityLibrary.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[Your Token goes here]" AppliesTo="all" Category="Becky's Custom Actions">
  <RuleDesigner Sentence="Update the properties of the list item to match its parent folder."></RuleDesigner>
  <Parameters>
    <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
    <Parameter Name="__ListId" Type="System.String, mscorlib, mscorlib" Direction="In" />
    <Parameter Name="__ListItem" Type="System.Int32, mscorlib, mscorlib" Direction="In" />
    </Parameters>
</Action>

Once my dll and my ACTIONS file have been deployed to the server, I’m able to add my new activity (“Update Document With Folder Values”) to a workflow I create in SharePoint Designer.