locked
How to programatically create variables for child activities RRS feed

  • Question

  • Hi. I am just learning the new WF and would like to implement a custom activity like so:

    • It should act like a Sequence
    • It should have an InArgument
    • Based on that InArgument some internal variables will be set
    • Those internal variables should be available to child activities (able to use in expressions).

    There are already a few answers for some of my needs, but not all. Anyway, I came up with the following solution:

    [ContentProperty("Activities")]
     [Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
     public sealed class SequenceWithVars: NativeActivity {
    
      private Sequence innerSequence = new Sequence();
      private Variable<int> ageVar = new Variable<int>("Age");
    
      [Browsable(false)]
      public Collection<Activity> Activities {
       get {
        return innerSequence.Activities;
       }
      }
    
      [Browsable(false)]
      public Collection<Variable> Variables {
       get {
        return innerSequence.Variables;
       }
      }
    
      public InArgument<string> UserName { get; set; }
    
      protected override void CacheMetadata(NativeActivityMetadata metadata) {
       // Arguments
       var arg = new RuntimeArgument("UserName", typeof(string), ArgumentDirection.In);
       metadata.Bind(UserName, arg);
       metadata.AddArgument(arg);
       // Child activities
       metadata.AddImplementationChild(innerSequence);
       // Variables
       metadata.AddImplementationVariable(ageVar);
      }
    
      protected override void Execute(NativeActivityContext context) {
       string userName = (string)context.GetValue(UserName);
       if (!string.IsNullOrEmpty(userName))
        ageVar.Set(context, userName == "Test" ? 20 : 30);
       context.ScheduleActivity(innerSequence);
      }
    
     }
    

    Even though I cannot see the "UserName" argument or the "Age" variable in the expression editor intellisense I can still use them in expressions, so this solution works. But it somehow does not feel right. Is there any better way to get the desired functionality? Also, is there a way the argument and variables are visible in intellisense or is intellisense still limited to the Variables collection?

    Monday, August 16, 2010 8:32 AM

Answers

  • Hi Simon,
    Intellisense in the IDE is 'buggy' in the sense that it doesn't find all the variables that are actually available at runtime, especially in cases where implementation children are involved.

    One implementation-children free solution could be using ActivityAction<int> as a wrapper for your inner sequence. Which could be a public child (actually descendent). I'm not sure if this helps with the intellisense or not.
    Tim

    • Marked as answer by Andrew_Zhu Monday, August 23, 2010 7:40 AM
    Tuesday, August 17, 2010 5:11 PM

All replies

  • Hi Patkec,

    You can't use an activity argument in expresspression of an other activity. In expression you use Varaible.

    To use your variable "Age" you only have to add it in your sequence varaibles as this :

      [ContentProperty("Activities")]
      [Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
      public sealed class SequenceWithVars : NativeActivity
      {
        private Sequence innerSequence;
        private Variable<int> ageVar;
    
        public SequenceWithVars()
        {
          innerSequence = new Sequence();
          ageVar = new Variable<int>("Age");
          innerSequence.Variables.Add(ageVar);
        }
    
        [Browsable(false)]
        public Collection<Activity> Activities
        {
          get
          {
            return innerSequence.Activities;
          }
        }
    
        [Browsable(false)]
        public Collection<Variable> Variables
        {
          get
          {
            return innerSequence.Variables;
          }
        }
    
        public InArgument<string> UserName { get; set; }
    
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
          // Arguments
          var arg = new RuntimeArgument("UserName", typeof(string), ArgumentDirection.In);
          metadata.Bind(UserName, arg);
          metadata.AddArgument(arg);
          // Child activities
          metadata.AddImplementationChild(innerSequence);
          // Variables
          //metadata.AddImplementationVariable(ageVar);
        }
    
        protected override void Execute(NativeActivityContext context)
        {
          string userName = (string)context.GetValue(UserName);
          if (!string.IsNullOrEmpty(userName))
            ageVar.Set(context, userName == "Test" ? 20 : 30);
          context.ScheduleActivity(innerSequence);
        }
    
      }
    

     


    Jérémy Jeanson MCP, MCTS http://blogs.codes-sources.com/JeremyJeanson/ (French or English spoken)
    Monday, August 16, 2010 11:51 AM
  • Hi Jeremy,

    Thanks for the answer, but I'm afraid this won't work. The first problem is that when SequenceWithVars is used in designer those internal variables (Age in my example) will get automatically serialized in the XAML which I do not want. I guess I could use a custom serializer for this, but anyway.. Second problem is that I want to modify said variables in the SequenceWithVars (in the Execute method I am setting the ageVar), which cannot be done if variable is not declared as an implementation variable of the activity. So that approach won't work..

    Why shouldn't I use InArgument in an expression? True, there is no intellisense for it, but it works - if I drop a WriteLine activity inside my SequenceWithVars and set the Text to "UserName" it is correctly displayed. Or is there some other reason why it shouldn't be used?

    Monday, August 16, 2010 12:02 PM
  • Hi Simon,

    Arg, you'r job isn't so easy :(

    I don't really know why we haven't intellinsense...

    for this scenario i use more often Execution porperties in parent-child relation.

    For intellience i choosed to code static class than can be used in expression to know a static shared data (as servicecontext) ;)

     


    Jérémy Jeanson MCP, MCTS http://blogs.codes-sources.com/JeremyJeanson/ (French or English spoken)
    Monday, August 16, 2010 1:19 PM
  • Hi Simon,
    Intellisense in the IDE is 'buggy' in the sense that it doesn't find all the variables that are actually available at runtime, especially in cases where implementation children are involved.

    One implementation-children free solution could be using ActivityAction<int> as a wrapper for your inner sequence. Which could be a public child (actually descendent). I'm not sure if this helps with the intellisense or not.
    Tim

    • Marked as answer by Andrew_Zhu Monday, August 23, 2010 7:40 AM
    Tuesday, August 17, 2010 5:11 PM
  • Hi Tim,

    We will also be hosting the designer in the isolated VS shell, so the intellisense issue is a bit more complicated since it does not work there. So we will probably try to implement this ourselves. We also wish to limit the intellisense to show only what we wish it to, so built-in intellisense wouldn't fit either way.

    Thanks,

    Simon

    Wednesday, August 18, 2010 6:35 AM
  • Jeremy yes, I'm beginning to realize things are a bit more complicated than it seemed at first. But that also makes things interesting, so ... :)
    Wednesday, August 18, 2010 6:37 AM
  • Hi Trivolli,

    You confirme normelly all variables sould be visibles in designer (varaible bottom list)?

    Do you know if we can found a list of others little buggy features?

    I build training content for frenchy wrokflow users and it would be really apreciate to


    Jérémy Jeanson MCP, MCTS http://blogs.codes-sources.com/JeremyJeanson/ (French or English spoken)
    Wednesday, August 18, 2010 7:27 AM