none
How to pass values for properties of child activities that dont accept arguments to a XAMLX activity?

    Question

  • Hi,

    I wanted to reuse parts of a workflow in several other workflows and therefore implemented a XAMLX activity that holds this parts. I know about passing arguments to XAMLX activities. My problem is that I have to pass some arguments to properties of sub activities that do not accept arguments or variables. For example I want to pass the value for the ServiceContractName property of a Receive activity. Unfortunately ServiceContractName seems to be no InArgument that accepts String variables or arguments. Does anyone know how I could pass values to such properties through arguments?

     

    Thursday, September 08, 2011 2:53 PM

Answers

  • Hi Jurgen,
    Let me explain further. A property reference is a bit like an argument in that it can be passed in from outside your workflow, in terms of outside the XAML file.

    However, it is unlike an argument in that the property's final value must be known at XAML load time, not runtime. You can see one usage of propertyreference here, as passing in an ActivityAction from outside the XAML file (I wanted to post the link last time but blogs.msdn.com was down).

    http://blogs.msdn.com/b/tilovell/archive/2010/02/26/an-activity-designer-for-invokeaction-t.aspx

    Tim

    • Marked as answer by Jürgen Bayer Saturday, September 24, 2011 12:46 PM
    Thursday, September 22, 2011 9:21 PM
    Moderator

All replies

  • There is something called 'Property Reference' in Xaml that would let you pass the property value in as long as it is provided from a parent XAML file - but it still can't be a dynamically computed value at runtime, like an InArgument.

    Would that possibly meet your needs?
    Tim

    Monday, September 19, 2011 5:23 PM
    Moderator
  • Hi Tim,

    thanks for the reply.

    I don't actually know what you meen with 'Property Reference'. But if you mean XAML data binding it does not work because it only works with dependency properties and the (simple, Non-InArgument) properties of activities seem not to be such. Or am I missing something? Do you have a link or something that could explain what you mean?


    Just to clarify what I mean: In following part of a declarative activity I want to pass the value of the ServiceContractName property from outside through an argument:

    <Activity ...

      <x:Members>
        <!-- THE ARGUMENT THAT SHOULD PASS THE VALUE -->
        <x:Property Name="ServiceContractName" Type="InArgument(x:String)" />
      </x:Members>

       ...

       <p1:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="ReceiveRequest"
              sap:VirtualizedContainerService.HintSize="255,90" OperationName="Start"
              ServiceContractName="HERE SHOULD THE ARGUMENT VALUE BE PASSED">
           ...
       </p1:Receive>
    </Activity>


    • Edited by Jürgen Bayer Thursday, September 22, 2011 1:36 PM
    • Marked as answer by Jürgen Bayer Saturday, September 24, 2011 12:45 PM
    • Unmarked as answer by Jürgen Bayer Saturday, September 24, 2011 12:45 PM
    Thursday, September 22, 2011 1:30 PM
  • Hi Jurgen,
    Let me explain further. A property reference is a bit like an argument in that it can be passed in from outside your workflow, in terms of outside the XAML file.

    However, it is unlike an argument in that the property's final value must be known at XAML load time, not runtime. You can see one usage of propertyreference here, as passing in an ActivityAction from outside the XAML file (I wanted to post the link last time but blogs.msdn.com was down).

    http://blogs.msdn.com/b/tilovell/archive/2010/02/26/an-activity-designer-for-invokeaction-t.aspx

    Tim

    • Marked as answer by Jürgen Bayer Saturday, September 24, 2011 12:46 PM
    Thursday, September 22, 2011 9:21 PM
    Moderator
  • Hi Tim

    Thanks. Now i got it :-). I tried to search for "Property Reference" on Google, but did not find any useful information. The link you provided gave the necessary hints.

    Using property references I managed to solve my problem basically. For all who are interested: Here is a sample how to pass the OperationName of a Receive activity that is part of a XAML activity:

    <?xml version="1.0" encoding="utf-8"?>
    <Activity mc:Ignorable="sap" x:Class="Demo_Workflow.StartActivity"  Namespaces omitted for clarity >
       <x:Members>
         <strong> <x:Property Name="StartOperationName" Type="x:String" /></strong>
       </x:Members>
       <sap:VirtualizedContainerService.HintSize>317,384</sap:VirtualizedContainerService.HintSize>
       <mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
       <Sequence DisplayName="Sequential Service" sad:XamlDebuggerXmlReader.FileName=".." sap:VirtualizedContainerService.HintSize="277,344">
          <Sequence.Variables>
             <Variable x:TypeArguments="p1:CorrelationHandle" Name="handle" />
             <Variable x:TypeArguments="x:Int32" Name="data" />
          </Sequence.Variables>
          <sap:WorkflowViewStateService.ViewState>
             <scg3:Dictionary x:TypeArguments="x:String, x:Object">
                <x:Boolean x:Key="IsExpanded">True</x:Boolean>
             </scg3:Dictionary>
          </sap:WorkflowViewStateService.ViewState>
          <p1:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="ReceiveRequest"  sap:VirtualizedContainerService.HintSize="255,90">
             <p1:Receive.CorrelationInitializers>
                <p1:RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
             </p1:Receive.CorrelationInitializers>
             <strong><p1:Receive.OperationName>
                <PropertyReference x:TypeArguments="x:String" PropertyName="StartOperationName" />
             </p1:Receive.OperationName></strong>
          </p1:Receive>
          <p1:SendReply Request="{x:Reference __ReferenceID0}" DisplayName="SendResponse" sap:VirtualizedContainerService.HintSize="255,90">
             <p1:SendParametersContent />
          </p1:SendReply>
       </Sequence>
    </Activity>
    
    

    Unfortunately I could not pass the ServiceContractName this way because the workflow designer reports that it could not serialize XName when using it as a property. But I'm sure I'll find a way around that. Also the workflow designer seems to have problems when using more than one PropertyReference in the declaration of an activity and reports that '"PropertyReference" properts has already been set on <ActivityName>'.

     

     

     

    Saturday, September 24, 2011 12:45 PM
  • Here is what i wrote for my kind of final solution to anyone concerned:

    The XAML activity:

    <Activity mc:Ignorable="sap" x:Class="Demo_Workflow.StartActivity"  ... >
      <x:Members>
        <x:Property Name="Number" Type="OutArgument(x:Int32)" />
        <x:Property Name="StartOperationName" Type="x:String" />
      </x:Members>
    <sap:VirtualizedContainerService.HintSize>317,482</sap:VirtualizedContainerService.HintSize>
      <mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
      <Sequence DisplayName="Sequential Service" sad:XamlDebuggerXmlReader.FileName="F:\Projekte\Evaluierungs-Projekte\Reusing XAMLX in Workflows Test\Demo Workflow\StartActivity.xaml" sap:VirtualizedContainerService.HintSize="277,442">
        <Sequence.Variables>
          <Variable x:TypeArguments="p:CorrelationHandle" Name="handle" />
          <Variable x:TypeArguments="x:Int32" Name="data" />
          <Variable x:TypeArguments="x:Int32" Name="result" />
        </Sequence.Variables>
        <sap:WorkflowViewStateService.ViewState>
          <scg3:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg3:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
        <p:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="ReceiveRequest" sap:VirtualizedContainerService.HintSize="255,90">
          <p:Receive.CorrelationInitializers>
            <p:RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
          </p:Receive.CorrelationInitializers>
          <p:Receive.OperationName>
            <PropertyReference x:TypeArguments="x:String" PropertyName="StartOperationName" />
          </p:Receive.OperationName>
          <p:Receive.ServiceContractName>
              <PropertyReference x:TypeArguments="x:String" PropertyName="StartServiceContractName" />
          </p:Receive.ServiceContractName>
          <p:ReceiveParametersContent>
            <OutArgument x:TypeArguments="x:Int32" x:Key="number">[Number]</OutArgument>
          </p:ReceiveParametersContent>
        </p:Receive>
        <Assign sap:VirtualizedContainerService.HintSize="255,58">
          <Assign.To>
            <OutArgument x:TypeArguments="x:Int32">[result]</OutArgument>
          </Assign.To>
          <Assign.Value>
            <InArgument x:TypeArguments="x:Int32">[Number * 2]</InArgument>
          </Assign.Value>
        </Assign>
        <p:SendReply Request="{x:Reference __ReferenceID0}" DisplayName="SendResponse" sap:VirtualizedContainerService.HintSize="255,90">
          <p:SendMessageContent DeclaredMessageType="x:Int32">
            <InArgument x:TypeArguments="x:Int32">[result]</InArgument>
          </p:SendMessageContent>
        </p:SendReply>
      </Sequence>
    </Activity>
    


    A partial class:

    partial class StartActivity
    {
       public string StartServiceContractNamespace { get; set; }
       public string StartServiceContractLocalName { get; set; }
    
       public XName StartServiceContractName
       {
          get { return XName.Get(this.StartServiceContractLocalName, this.StartServiceContractNamespace); }
       }
    }
    
    

    My Remarks:

    To be able to pass the OperationName for the Receive StartActivity.xaml contains the property 'StartOperationName'.
    This property is passed to the Reveive activity using a PropertyReference. This unfortunately is necessary since
    the OperationName property of a Receive is not an InArgument (what would allow to pass a property or variable directly)
    but a simple property. To answer one possible question: Since OperationName is not a dependency property we could not
    use XAML databinding for that.
     
    Nearly the same is done for the ServiceContractName property of the Receive. Problem here was that I could not use a
    XAML property declared in the XAML file itself. Reason for that is that the type of ServiceContractName is XName and
    the workflow engine could not serialize XName because it lacks a default constructor.

    To be able to use a property of type XName I tried to attach a ServiceXNameTypeConverter type converter in code:
    [TypeConverter(typeof(ServiceXNameTypeConverter)), DefaultValue(null)]
    public XName StartServiceContractName { get; set; }
    This had to be done in a partial class since I could not find out how to attach a type converter in XAML directly.

    But this did not provide a solution since then I got the (weird) error "... ServiceXNameTypeConverter cannont convert
    from System.String" although exactly and only this conversion is supported. Further investigation unsing the classes
    ServiceXNameTypeConverter and XNameTypeConverterHelper in code (fetched from ILSpy) lead to the conclusion that
    XNameTypeConverterHelper.ConvertFromHelper had the problem that it could not get a service of type
    IXamlNamespaceResolver from the passed context. Since I did not know why that was the case (The Receive activity
    class uses the ServiceXNameTypeConverter exactly the same way and works), I no longer followed this route.

    Instead I just implemented two simple string properties for the namespace and the local name that are read in the
    property StartServiceContractName. StartServiceContractName then is bound to ServiceContractName of the Receive
    using a PropertyReference.

    Two problems still exist with this solution:
    * I get the weird error "'PropertyReference' property has already been set on Receive" when trying to open
      the designer for the XAML Activity. This is caused by setting more than one property using a
      PropertyReference, but makes no sense at all.
    * The property 'StartServiceContractName' must be public to enable the designer to read it. This is not really
      a problem since 'StartServiceContractName'is not displayed as a property in the designer since it is read only.

    Sunday, September 25, 2011 12:59 PM