locked
Custom Activities RRS feed

  • Question

  • I am a WF beginner and have some question about creating custom activities.

    From some examples I am noticing that there are two parts to an activity: the UI and the code.  The Activity Designer Library is used to develiop the UI and the Activity Library project type is used to develop the code for the activity.  Am I correct ?

    When creating a custom basic activity which class should I derive from: CodeActivity, NativeActivity?  What are their differences?

     

    Thanks

    Chris

    Tuesday, June 22, 2010 1:10 PM

Answers

  • Hello Chris -

    To your first question: Yes, the different project types are for the activity itself and the related designer (the UI). Activities are not strictly required to have a designer (for example, if the workflows are only being authored/edited via code or XAML).

    To your second question: You would derive from CodeActivity/AsyncCodeActivity if you are going to only implement the execute method for the activity - and the work that it performs can be done in a single method, or composed of other activities.

    If you need to implement your own control flow activity that performs its own scheduling you would use NativeActivity. It allows you to schedule other activities, propagate errors, handle cancelation, etc. The learning curve for NativeActivity is higher.

    You may find the following blog post from my colleagues to be useful on this subject: http://blogs.msdn.com/b/flow/archive/2010/01/24/lights-camera-activities-windows-workflow-foundation-s-activity-model.aspx

    Tuesday, June 22, 2010 2:29 PM

All replies

  • Hello Chris -

    To your first question: Yes, the different project types are for the activity itself and the related designer (the UI). Activities are not strictly required to have a designer (for example, if the workflows are only being authored/edited via code or XAML).

    To your second question: You would derive from CodeActivity/AsyncCodeActivity if you are going to only implement the execute method for the activity - and the work that it performs can be done in a single method, or composed of other activities.

    If you need to implement your own control flow activity that performs its own scheduling you would use NativeActivity. It allows you to schedule other activities, propagate errors, handle cancelation, etc. The learning curve for NativeActivity is higher.

    You may find the following blog post from my colleagues to be useful on this subject: http://blogs.msdn.com/b/flow/archive/2010/01/24/lights-camera-activities-windows-workflow-foundation-s-activity-model.aspx

    Tuesday, June 22, 2010 2:29 PM
  • The blog explains everything clearly. Thanks.

    I am trying to stay away from the WF3 examples since that would just confuse me.

     

    Tuesday, June 22, 2010 2:45 PM
  • Hi Karl.

    When I load a designer, I pass it a Sequence with a variable to load.  This variable is the 1st activitiy's out arg and 2nd activity's in arg. How can I have the 2nd activity look for this variable w/o passing it in as an in arg ?  Is there a better way of doing this ?

     public sealed class StartSolidWorks: CodeActivity<SldWorks>
      {
        [DisplayName("Start Visible?"),Description("Should SolidWorks be started as a visible window?")]
        [DefaultValue(true)]
        public InArgument<bool> StartVisible { get; set; }
    
        protected override SldWorks Execute(CodeActivityContext context)
        {
          Type swAppType = Type.GetTypeFromProgID("SldWorks.Application");
          SldWorks swAppObject = (SldWorks)Activator.CreateInstance(swAppType);
          if (this.StartVisible.Get(context) == true)
            swAppObject.Visible = true;
    
          return swAppObject;
        }
      }
    
      [DisplayName("OpenDocumentActivity")]
      public sealed class OpenDocument : CodeActivity
      {
        [RequiredArgument]
        public InArgument<SldWorks> SwInstance { get; set; }
    
        [DisplayName("File to Open"),Description("Absolute path of file to open.")]
        public InArgument<string> Path { get; set; }
    
        protected override void Execute(CodeActivityContext context)
        {
          DocumentSpecification docspec = (DocumentSpecification)this.SwInstance.Get(context).GetOpenDocSpec(this.Path.Get(context).ToString());
          ModelDoc2 doc2 = this.SwInstance.Get(context).OpenDoc7(docspec);
          Marshal.ReleaseComObject(doc2); 
        }
      }

    chris

    Wednesday, June 23, 2010 4:27 PM
  • If the activities are siblings in a Sequence then the Variable/InArgument like you are doing is the only way for them to pass data to each other.

    If they don't need to be sliblings, there are some non-sibling ways you can define activities pass data to each other such as with ActivityAction<T>.

    Or a two-siblings of some required mutual custom ancestor scenario: another option is using a custom Scope activities to replace the variable/arguments way of passing. A scope can set up data which is 'implicitly shared' between activities in its scope.

    Tim

    Wednesday, June 23, 2010 4:39 PM
  • Thanks for the post. 

    Currently my activities are siblings inside of a sequence.  From you post I gather that I need to create a custom Sequence activity so that I can pass it an argument once and all of its children can use it.

     

    What is the point of context.ScheduleActivity(Body); inside the FileScope activity?

    Thursday, June 24, 2010 1:28 PM
  • btw, where is the designer for System.Activities.Statements.Sequence?
    Thursday, June 24, 2010 2:52 PM
  • The designer classes are internal, they aren't meant to be reused directly.
    If you want a scope you don't actually have to reimplement sequence. You can just place a Sequence inside of your scope.

    That is the point of this line:

    'context.ScheduleActivity(Body);'

    if you add a Sequence as the child (Body) of your Scope, the scope must schedule the Sequence to execute, otherwise the sequence will never actually run.

    Tim

    Thursday, June 24, 2010 4:39 PM
  • Dropping a sequence into my NativeActivity works great, with the exception that its designer doesn't resize to fit the sequence in nicely.

    Is there a way where the below activity is either invisible or fills the WF designer when I rehost WorkFlowDesigner?  Currently I call WorkflowDesigner.Load(SolidWorksSequence) but SolidWorksSequence will really be a template for every workflow I create...

     

    Thanks

    [Designer(typeof(SolidWorksSequenceDesigner))]
      public sealed class SolidWorksSequence : NativeActivity
      {
        [DisplayName("Start Visible?"), Description("Should SolidWorks be started as a visible window?")]
        [DefaultValue(true)]
        public InArgument<bool> StartVisible { get; set; }
    
        public Activity Body { get; set; }
    
        protected override void Execute(NativeActivityContext context)
        {
          Type swAppType = Type.GetTypeFromProgID("SldWorks.Application");
          SldWorks swAppObject = (SldWorks)Activator.CreateInstance(swAppType);
          if (this.StartVisible.Get(context) == true)
            swAppObject.Visible = true;
    
          context.Properties.Add("SolidWorksInstance", swAppObject);
          context.ScheduleActivity(Body);
        }
      }

    Friday, June 25, 2010 1:47 PM
  • There's no preset way to do that,

    It ends up being all about how you code the custom activity designer.

    If you want maximum flexibility you can derive from WorkflowViewElement instead of ActivityDesigner, and/or override the Templates of WorkflowItemPresenter.

    Tim

    Friday, June 25, 2010 3:44 PM