Office Workflows are designed to be decoupled as much as possible. The Observer Design Pattern is used to decouple an Office Add-in host application from workflow business logic. A Windows Workflow 4.0 Tracking Participant is used as an Activity state subscriber. An Office Add-in acts as a subscriber by instantiating an instance of an activity state Tracking class providing a TrackingProfile. A TrackingProfile filters activity state change as well as argument and variable values. An add-in creates a delegate method to filter ActivityStateRecord properties available in the ActivityStateTrackedEventArgs event argument.

Example ActivityStateTracked

    void sampleParticipant_ActivityStateTracked(object sender, ActivityStateTrackedEventArgs e)
    {
      //Invoke a Treeview method to add all ActivityState names and arguments to Nodes
      if (Globals.ThisAddIn.CustomTaskPanes.Count > 0)
      {
        WorkflowActivitiesControl workflowActivities = Globals.ThisAddIn.CustomTaskPanes[0].Control as WorkflowActivitiesControl;
        workflowActivities.Invoke(new WorkflowActivitiesControl.AddNodeDelegate
          (workflowActivities.AddNode), e.ActivityStateRecord.Activity.Name, e.ActivityStateRecord.Arguments);
      }

      //Filter for Name == "FormattingActivity"
      if (e.ActivityStateRecord.Activity.Name == "FormattingActivity")
      {
        string argColor = (string)e.ActivityStateRecord.Arguments["ArgColor"];
        if (Globals.ThisAddIn.Application.ActiveCell != null)
          Globals.ThisAddIn.Application.ActiveCell.Style = argColor;
      }
    }    

To decouple an Office Add-in host

Step 1 – Implement IClientActivity.Run() and set arguments

A client host implements IClientActivity.Run() and passes a Dictionary<string, object> containing workflow arguments to a WorkflowApplication instance. A client runs a workflow by calling the WorkflowApplication.Run() method. WorkflowApplication.Run() is an asynchronous call; therefore, you need to create delegate callback methods for workflow events. For example, a Word add-in could pass ContentControl.Range.Text values.

Note Passing the Office add-in Window Handle allows the workflow to supply .NET forms setting the form parent to the application handle. See LoginFormActivity for an example Workflow client form.
  class SampleActivity : IClientActivity
  {
    public void Run(string workflowPath)
    {
      …
      var inputs = new Dictionary<String, Object>
        {
          {"ArgTitle",  title.Range.Text},
          {"ArgBody",  body.Range.Text},
          {"Handle", System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle}
        };

      Activity wf = (Activity)ActivityXamlServices.Load(workflowPath);

      WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);

Step 2 - Instantiate an instance of a Tracking class

The next step instantiates an instance of a Tracking class providing a TrackingProfile. A TrackingProfile subscribers to certain activity states extracting certain variables and arguments. For this example, the TrackingProfile is interested in all activity states extracting all variable and argument values.

      Tracking sampleParticipant = new Tracking
      {
        TrackingProfile = new TrackingProfile()
        {
          Name = "CustomTrackingProfile",
          Queries = 
            {
            new ActivityStateQuery()
            {
              // Subscribe for track records from all activities for all states
              ActivityName = "*",
              States = { ActivityStates.Closed },

              // Extract workflow variables and arguments as a part of the activity tracking record
              // Variables = "*" allows for extraction of all variables in the scope of the activity
              // Arguments = "*" allows for extraction of all arguments in the scope of the activity
              Variables = 
              { { "*" } },
              Arguments = 
              { { "*" } }
              }   
            }
          }
        };

      sampleParticipant.ActivityStateTracked += new Tracking.ActivityStateTrackedHandler(sampleParticipant_ActivityStateTracked);
      wfApp.Extensions.Add(sampleParticipant);

Step 3 - Create delegate callback methods

A WorkflowApplication.Run() is an Asynchronous call; therefore, you need to create delegate callback methods for workflow events. For this example, the host IClientActivity.Run() method contains a Completed delegate callback method to get the workflow outputs.

      wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs wfe)
      {
        IDictionary<String, Object> output = wfe.Outputs;

        syncEvent.Set();
      }; 

Step 4 – Run Workflow

The final step is to run the Workflow.

      wfApp.Run();

Example TrackingParticipant_ActivityStateTracked Filter

      if (e.ActivityStateRecord.Activity.Name == "DocumentParts")
      {
        if (e.ActivityStateRecord.Arguments.Keys.Contains("ContentParts"))
        {
          Dictionary<string, string> contentParts = (Dictionary<string, string>)e.ActivityStateRecord.Arguments["ContentParts"];
          foreach(string item in contentParts.Values)
          {
            Globals.ThisAddIn.Application.Selection.TypeText(item + "\r");
          }
        }
      }

Last edited Oct 14, 2011 at 2:03 AM by dvana, version 14

Comments

No comments yet.