locked
How do I propagate implementation variables to activity custom-implemented children? RRS feed

  • Question

  • Hi,

    I need to create a set of custom composite activities that allow their children activities to access parent variables at design and runtime. I implemented a custom sequence activity, in which instead of scheduling each child manually in the Execute method, I use a Sequence activity as the Body. Body is updated each time an activity is added to or removed from my custom sequence.

    In the CacheMetadata I then add this Sequence (Body property) as the only child of my Custom Sequence. Because I want to access variables at runtime, I add them as Implementation Variables. Here is the code:

     [Designer(typeof(TestMySequenceDesigner))]
      public sealed class TestMySequence : NativeActivity
      {
        [Browsable(false)]
        public Sequence Body {get; set;}
    
        [Browsable(false)]
        public ObservableCollection<Activity> Activities { get; set; }
    
        [Browsable(false)]
        public Collection<Variable> Variables { get; set; }
    
    
        public TestMySequence()
        {
          this.Variables = new Collection<Variable>(); 
          this.Activities = new ObservableCollection<Activity>();
          this.Activities.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Activities_CollectionChanged);
          this.Body = new Sequence();
        }
    
        void Activities_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
          if(e.Action.ToString().Equals("Add")){
            Activity a = e.NewItems[0] as Activity;
            Body.Activities.Add(a); 
          }
          if (e.Action.ToString().Equals("Remove"))
          {
            Activity a = e.OldItems[0] as Activity;
            if (Body.Activities.Contains(a))
            {
              Body.Activities.Remove(a);
            }
          }
        }
    
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {
          metadata.AddImplementationChild(Body);
          // metadata.AddChild(Body); 
          foreach (Variable var in this.Variables)
          {
            metadata.AddImplementationVariable(var); 
           //  metadata.AddVariable(var);
          }
        }
    
        protected override void Execute(NativeActivityContext context)
        {
          foreach(Variable var in this.Variables){
            var value = context.GetValue(var);
            if (value != null) { Console.WriteLine(value.ToString()); }
          }
          context.ScheduleActivity(Body); 
        }
      }

     To test it I model a simple workflow:

    Add TestMySequence as the top activity, define variable x of type String in its scope;

    Add an Assign activity as the first child of TestMySequence, Assign sets variable x to "a";

    Add a Sequence activity as the second child of TestMySequence, add a WriteLine as a child to this Sequence, that prints the value of x

     - this test works fine. However, if I replace Sequence  with TestMySequence (second child of the top activity), I get an error:

    "'x' is not declared. It may be inaccessible due to its protection level"

    I also tried to use AddVariable  and AddChild in the CacheMetadata. This solves the problem during the design time, but I cannot access the varibles at runtime - it crashes at context.GetValue(var).

    What am I missing in my custom sequence? I know I can rewrite it completely and schedule all childs manually, but I would like to avoid it, as I do not need any extensions at this point (also I don't know if this would solve the problem).

    Can anyone help me further?

    Thanks,

    Anya

    Friday, March 4, 2011 10:15 AM

All replies

  • I'm able to reproduce your issue with your code. And I'm able to fix it by only changing this line

               metadata.AddImplementationChild(Body);

    to
               metadata.AddChild(Body);

    Can you try whether it works for you?

    You should use AddChild() for Body since it's a public child, not implementation child.


    This posting is provided "AS IS" and confers no rights or warranties.
    Friday, March 4, 2011 5:14 PM
  • Hi,

    thanks for your response. Unfortunately it does not work. If I change AddImplementationChild to AddChild, I cannot even access variables inside the top TestMySequence activity (the scope of the variable). I get the following error: "x is not declared. It might be inaccessible due to its protection level" when i use x on the left side of the Assign activity in my test workflow described above. The children activities also cannot access x.

    Could you use variables declared in TestMySequence activity in the children activities?

    Anya

    Saturday, March 5, 2011 3:26 PM
  • Hi, Anya

    ->"I need to create a set of custom composite activities that allow their children activities to access parent variables at design and runtime. "
    This sample could be helpful to you:
    http://xhinker.com/post/WF4My-Composite-Activity.aspx

    Regards
    MSDN Community Support
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    This posting is provided "AS IS" with no warranties, and confers no rights. My Blog: http://xhinker.com
    Microsoft Windows Workflow Foundation 4.0 Cookbook
    Tuesday, March 8, 2011 3:27 AM
  • Hi Andrew,

    thanks for the example, it doesn't help with my problem though.

    What I do not understand and do not know how to solve, is why when I declare variables in my custom sequence and cache them as implementation variables, i can use these variables in the standard sequence as a children activity, but not in my custom sequence as a children activity.

    Here is xaml for the first case, which works fine

    <Activity mc:Ignorable="sap" x:Class="Activity1" sap:VirtualizedContainerService.HintSize="306,377" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:rce="clr-namespace:RescueIT.ClientDesigner.ExtendedActivities;assembly=ClientDesigner" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <rce:TestMySequence sap:VirtualizedContainerService.HintSize="266,337" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">
      <rce:TestMySequence.Activities>
       <sco:ObservableCollection x:TypeArguments="Activity">
        <x:Reference>__ReferenceID0</x:Reference>
        <x:Reference>__ReferenceID1</x:Reference>
       </sco:ObservableCollection>
      </rce:TestMySequence.Activities>
      <rce:TestMySequence.Body>
       <Sequence>
        <Assign x:Name="__ReferenceID0" sap:VirtualizedContainerService.HintSize="242,58">
         <Assign.To>
          <OutArgument x:TypeArguments="x:String">[x]</OutArgument>
         </Assign.To>
         <Assign.Value>
          <InArgument x:TypeArguments="x:String">a</InArgument>
         </Assign.Value>
        </Assign>
        <Sequence x:Name="__ReferenceID1" sap:VirtualizedContainerService.HintSize="242,185">
         <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
           <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
         </sap:WorkflowViewStateService.ViewState>
         <WriteLine sap:VirtualizedContainerService.HintSize="211,61" Text="[x]" />
        </Sequence>
       </Sequence>
      </rce:TestMySequence.Body>
      <rce:TestMySequence.Variables>
       <Variable x:TypeArguments="x:String" Name="x" />
      </rce:TestMySequence.Variables>
     </rce:TestMySequence>
    </Activity>  
    

    and here is xaml for the second case which gives me this error: "x is not declared, it may be inaccessible due to its protection level"

    <Activity mc:Ignorable="sap" x:Class="Activity1" sap:VirtualizedContainerService.HintSize="306,331" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:rce="clr-namespace:RescueIT.ClientDesigner.ExtendedActivities;assembly=ClientDesigner" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=System" xmlns:sco1="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <rce:TestMySequence sap:VirtualizedContainerService.HintSize="266,291" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">
      <rce:TestMySequence.Activities>
       <sco:ObservableCollection x:TypeArguments="Activity">
        <x:Reference>__ReferenceID1</x:Reference>
        <x:Reference>__ReferenceID2</x:Reference>
       </sco:ObservableCollection>
      </rce:TestMySequence.Activities>
      <rce:TestMySequence.Body>
       <Sequence>
        <Assign x:Name="__ReferenceID1" sap:VirtualizedContainerService.HintSize="242,58">
         <Assign.To>
          <OutArgument x:TypeArguments="x:String">[x]</OutArgument>
         </Assign.To>
         <Assign.Value>
          <InArgument x:TypeArguments="x:String">a</InArgument>
         </Assign.Value>
        </Assign>
        <rce:TestMySequence x:Name="__ReferenceID2" sap:VirtualizedContainerService.HintSize="242,139">
         <rce:TestMySequence.Activities>
          <sco:ObservableCollection x:TypeArguments="Activity">
           <x:Reference>__ReferenceID0</x:Reference>
          </sco:ObservableCollection>
         </rce:TestMySequence.Activities>
         <rce:TestMySequence.Body>
          <Sequence>
           <WriteLine x:Name="__ReferenceID0" sap:VirtualizedContainerService.HintSize="211,61" Text="[x]" />
          </Sequence>
         </rce:TestMySequence.Body>
         <rce:TestMySequence.Variables>
          <sco1:Collection x:TypeArguments="Variable" />
         </rce:TestMySequence.Variables>
        </rce:TestMySequence>
       </Sequence>
      </rce:TestMySequence.Body>
      <rce:TestMySequence.Variables>
       <Variable x:TypeArguments="x:String" Name="x" />
      </rce:TestMySequence.Variables>
     </rce:TestMySequence>
    </Activity>
    

    The only difference between them is that i use Sequence as a child in the first sample and TestMySequence in the second.

    I also tried to rewrite my custom sequence as shown in your example. This hoewever results in the same problem (although xaml looks nicer :)).

    Do I need to somehow tell my custom activity to cache all parent variables? Shouldn't this happen automatically? I mean, it does with the normal variables, why not with the implementation ones?

    Thanks,

    Anya

    Tuesday, March 8, 2011 10:26 AM