Context-Aware SharePoint Designer Custom Workflow Activities

I had this nice little idea that I could teach the guys in my department how to do workflow with the Sharepoint designer so I wouldn’t have to do all that myself with the big Visual Studio Guns. All was going well until they needed to send out an email containing the URL of the site from which we were sending it. Not a link to an item in the site, the actual URL of the site itself.

“Simple!,” said I. I’ll just get it from the sharepoint object model with a custom activity. I wrote it all up, tried to use SPContext.Current.Web… and got it to puke nicely on a Null reference to Current. Of course! Why would a workflow activity have any reference at all to sharepoint? Technically a workflow activity doesn’t care where it’s hosted. But mine were to be hosted in Sharepoint, and that’s what I wanted to access. So, I looked around on the web and found nothing. I knew it had to be possible to get the context somehow, because other Sharepoint Designer Activities that came in-the-box were using it… so I looked for a clue.

I cracked open Microsoft.SharePoint.WorkflowActions.dll in Lutz Roeder’s Reflector, and lo-and behold there is a very cheap trick at work here!

Microsoft has implemented a custom dependency property by the name of __Context that is picked up as a Microsoft.SharePoint.WorkflowActions.WorkflowContext object IF it exists in your code. If you don’t implement such a property then it’s just not there, and nobody’s the wiser. This WorkflowContext object has a reference to .Site and .Web as properties, so it’s really handy to have at from within your custom activities that need to know where they are.

I’d like to post the code I found directly, but I can’t since it’s not mine. I will show you, however, what my code looks like.


 
public static System.Workflow.ComponentModel.DependencyProperty __ContextProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(FooActivity));
 
        [Description("Context")]
        [ValidationOption(ValidationOption.Required)]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Microsoft.SharePoint.WorkflowActions.WorkflowContext __Context
        {
            get
            {
                return ((Microsoft.SharePoint.WorkflowActions.WorkflowContext)base.GetValue(FooActivity.__ContextProperty));
            }
            set
            {
                base.SetValue(FooActivity.__ContextProperty, value);
            }
        }
 

That’s a lot of typing if you write a bunch of these activities, so I made a codesnippet just for all of you out there to have and keep forever so you don’t have to do this. (Warning, do not just make this into a base class and derive your activities from it. It will throw an exception when you try to use it. I’m not sure why, but it does. You have to add this to every single activity you create for SPD that needs to know the sharepoint context.)

Here’s the code snippet I made:


<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>spwfactivity</Title>
      <Shortcut>spwfactivity</Shortcut>
      <Description>Code snippet for creating a Sharepoint-Aware Workflow Activity</Description>
      <Author>Dolan</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>classname</ID>
          <ToolTip>Class Name</ToolTip>
          <Default>Activity1</Default>
        </Literal>
      </Declarations>
      <Code Language="csharp"><![CDATA[public partial class $classname$: System.Workflow.ComponentModel.Activity
  {
        public static System.Workflow.ComponentModel.DependencyProperty __ContextProperty =
            System.Workflow.ComponentModel.DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof($classname$));
 
        [Description("Context")]
        [ValidationOption(ValidationOption.Required)]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Microsoft.SharePoint.WorkflowActions.WorkflowContext __Context
        {
            get
            {
                return ((Microsoft.SharePoint.WorkflowActions.WorkflowContext)base.GetValue($classname$.__ContextProperty));
            }
            set
            {
                base.SetValue($classname$.__ContextProperty, value);
            }
        }
 
    public $classname$()
    {
      InitializeComponent();
    }
  }]]>
      




You'll also have to add a Parameter value to the .actions file with your activity that specifies the __Context property as an Input property.


  .
  .
  .
  <Parameters>
    <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
   .
   .
   .
  </Parameters>

This one took me a while to figure out, but finally it worked!

If you want some more info on adding your own actions to designer (my article has thus far assumed you're familiar with the process,) check out this link.

Update: I forgot to mention that just about the DAY after I figured this out on my own, I stumbled onto this codeproject article that dealt with this exact topic.

Just for fun I've also attached the snippet file so you don't have to copy and paste it. Just save it into "%ProgramFiles%\Microsoft Visual Studio 9.0\VC#\Snippets\1033\Visual C#."

Click here to download it.

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

2 Comments »

 
  • Joe says:

    This is not working for me. __Context comes up null every time. Could you show me the full .Actions code you are using, not just the Parameters part? I’m very new to this stuff, this is the first time I have used Visual Studio since doing C++ code in VS6.

    BTW, what is that directory you are saying to put the snippet code in? No such baby. Is there even a Visual Studio 9.0 yet? And Visual Studio 8.0 does not seem to have a VC# subdirectory…?

  • Dave says:

    Visual studio 9 is Visual Studio 2008. I wrote this using visual studio 2008, on a windows 2003 server Standard R2 w/sp2 base OS, also with MS Office 2007 including sharepoint designer 2007, and MOSS 2007 enterprise installed on it. I don’t think all of that’s required, and it should run just dandy on a barebones WSS 3.0 basic install, but just to give the context of what I did. Also, if you want to see exactly how that all works I suggest you read the official docs I have linked to in the article.
    If you want to know how to use code snippets in Visual Studio 2005, check out this article from dotnetjunkies.

    Just so you know, I’m happy to help, but this stuff is just an example, not a definitive howto guide, which is why I’ve provided the more official links. Generally speaking, I write for people who get stuck on the same things I do, not necessarily the whole swath of developers across the community. I do that kind of broad scoped writing when someone else is paying me to do it, like my good old articles for DevX. Here is just my thoughts, my work and experiences, and some hints for what I consider to be helpful for folks working on things like the stuff I am. This is not my resume, and I’m not interviewing for a job, so I’ve got nobody to impress. I hope I’ve been helpful to you, but if not, don’t worry, I’m not asking for a C# MVP nomination anytime soon ;)

 

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>