Partager via


ExpressionTextBox 101

The ExpressionTextBox is the basic building block for editing expressions in custom activity designers. If you’re writing a custom activity designer that uses expressions, you’ll use this control. This post is meant to provide an overview for custom activity designer developers. I’m just going to annotate a small amount of the SDK sample and walk through it step by gory step. For those with short attention spans, skip this post and just take a look at the ExpressionTextBox SDK sample instead.   

Start with a new activity designer, and make sure you have the following namespaces available:

 <sap:ActivityDesigner x:Class="Microsoft.Samples.ExpressionTextBoxSample.MultiAssignDesigner"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
    xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation" 
    xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
>

Expressions are bound to arguments using the ArgumentToExpressionConverter*, which is in the System.Activities.Presentation.Converters namespace.  Add this converter to your resource dictionary, like so:

     <sap:ActivityDesigner.Resources>
        <ResourceDictionary>
            <sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
        </ResourceDictionary>
    </sap:ActivityDesigner.Resources>

Now you can go on to adding your ExpressionTextBox. First, you need to figure out which type of expression you are editing. You can edit two types of expressions inside of an ExpressionTextBox, location expressions (or L-value expressions) and value expressions. Value expressions do what they say on the box. They are expressions that evaluate to a value, and you can place them on the right hand side of an assignment statement. InArguments correspond to value expressions, and are serialized as VisualBasicValue expressions (shortened to [] when serialized from the designer). Now, let’s pretend we have an activity with an InArgument named FooValue of type String. Here is a simplified ExpressionTextBox that binds to that argument:

 <sapv:ExpressionTextBox 
    Expression="{Binding Path=ModelItem.FooValue, 
                         Mode=TwoWay, 
                         Converter={StaticResource ArgumentToExpressionConverter}, 
                         ConverterParameter=In }"
    ExpressionType="s:String"
    OwnerActivity="{Binding Path=ModelItem}"
   />

Obviously, you’d set other properties in the real world, but the two that you absolutely must set are Expression and OwnerActivity. It is important to set ExpressionType as well, otherwise you will see some wonky behavior. Take a look at that expression binding – Path is to the argument to which you are binding, mode is almost always TwoWay, and then we pass the In parameter to the ArgumentToExpressionConverter to tell it that we are binding to an InArgument.

Conversely, location expressions correspond to OutArguments and are serialized as VisualBasicReference expressions (shortened to [] when serialized from the designer). You can put a location expression on the left hand side of an assignment statement. Now, let’s pretend we have an activity with an OutArgument named FooReference of type String. Here is a simplified ExpressionTextBox to bind to that argument.

 <sapv:ExpressionTextBox 
      Expression="{Binding Path=ModelItem.FooReference, 
                           Mode=TwoWay, 
                           Converter={StaticResource ArgumentToExpressionConverter}, 
                           ConverterParameter=Out }"
      OwnerActivity="{Binding Path=ModelItem}"
      ExpressionType="s:String" 
      UseLocationExpression="True" 
/>

There are two differences from the previous example. Since we are binding to a L-value expression, the UseLocationExpression property is True. Because we are binding to an OutArgument, the ConverterParameter is Out.

Not shown today: binding ExpressionTextBoxes to CLR properties and binding to untyped arguments. Those will be subjects of another post.

*Aside – this is the only convenient converter for binding basic UI elements to arguments (WorkflowItem[s]Presenter excepted). In this release, you have to write your own converter to bind say a check box to a boolean argument.

Comments

  • Anonymous
    November 25, 2009
    How would you bind an expression textbox to a regular POCO property of an Activity?
  • Anonymous
    November 26, 2009
    Hi,Can I use ExpressionTextBox as an editor of System.Linq.Expressions.Expression type? If not, think about it, I believe you are really close to make it work this way.Marcin
  • Anonymous
    December 02, 2009
    Hi Marcin, yes you can use System.Linq.Expressions.Expression as the type. Just add the appropriate namespace xmlns:sle="clr-namespace:System.Linq.Expressions;assembly=System.Core" and then change the type above to sle:Expression. I even checked that this works at runtime if you have a variable of that type and you assign a value of say a NewExpression to it.Hi Will, I will work up a blog post with that sample shortly.Thanks
  • Anonymous
    December 06, 2009
    Hi,I think I was quite unprecise in my question :) I have just started a new project in WPF (http://mnajder.blogspot.com/2009/11/rxsandbox.html) and I want to use an ExpressionTextBox control as an editor for VB code. It is a very simple WPF application that has nothing to do with WF. I want to define some "referenced" assemblies (with namespaces),  set expression type, probably set some "This" object instance and that I'd like to be able to write a VB expression for example LINQ query. So the key point here is to not be depend on any WF stuff in ExpressionTextBox API such as activities, activities designers model and so on.
  • Anonymous
    January 07, 2010
    Sorry that won't work. You might consider Actpro Software Syntax Editor as an alternative. http://actiprosoftware.com/Products/DotNet/WPF/SyntaxEditor/Default.aspx.
  • Anonymous
    July 15, 2010
    The comment has been removed
  • Anonymous
    April 07, 2011
    Hi Noam,I just wanted to let you know that in the designer metadata you can specify an editor of your own for properties. If the item you want is on the designer then you'll have to write your own designer, otherwise you can do as follows.builder.AddCustomAttributes(typeof(FlowSwitch<>), "Expression", new EditorAttribute(typeof(Editors.ActivityDialogExpressionEditor), typeof(DialogPropertyValueEditor)));public class ActivityDialogExpressionEditor : DialogPropertyValueEditor   {       public ActivityDialogExpressionEditor()       {           this.InlineEditorTemplate = new DataTemplate();           FrameworkElementFactory stack = new FrameworkElementFactory(typeof(StackPanel));           FrameworkElementFactory textBox = new FrameworkElementFactory(typeof(ExpressionTextBox));           textBox.SetValue(ExpressionTextBox.HintTextProperty, "Paolo's Expression");           //textBox.SetValue(ExpressionTextBox.UseLocationExpressionProperty, true);           //textBox.SetValue(ExpressionTextBox.IsReadOnlyProperty, false);           //Expression Type Binding           Binding expressionTypeBinding = new Binding("ParentProperty.PropertyType");           expressionTypeBinding.Converter = new TypeToArgumentTypeConverter();           textBox.SetValue(ExpressionTextBox.ExpressionTypeProperty, expressionTypeBinding);           //Owner Activity Binding           Binding ownerActivityBinding = new Binding("ParentProperty");           ownerActivityBinding.Converter = new ModelPropertyEntryToOwnerActivityConverter();           ownerActivityBinding.Mode = BindingMode.OneWay;           textBox.SetValue(ExpressionTextBox.OwnerActivityProperty, ownerActivityBinding);           MultiBinding expressionBinding = new MultiBinding();           Binding valueBinding = new Binding("Value");           valueBinding.Mode = BindingMode.TwoWay;           Binding propertyBinding = new Binding("ParentProperty");           propertyBinding.Mode = BindingMode.OneWay;           expressionBinding.Bindings.Add(valueBinding);           expressionBinding.Bindings.Add(propertyBinding);           expressionBinding.Converter = new RulesEngineExpressionConverter(new ObjectToModelValueConverter());           textBox.SetValue(ExpressionTextBox.ExpressionProperty, expressionBinding);           stack.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);           stack.AppendChild(textBox);           FrameworkElementFactory editModeSwitch = new FrameworkElementFactory(typeof(EditModeSwitchButton));           editModeSwitch.SetValue(EditModeSwitchButton.TargetEditModeProperty, PropertyContainerEditMode.Dialog);           stack.AppendChild(editModeSwitch);           this.InlineEditorTemplate.VisualTree = stack;       }       public override void ShowDialog(PropertyValue propertyValue, IInputElement commandSource)       {           Window window = new Window()           {               Title = "Expression Editor",               Content = new ActivityExpressionEditorControl(propertyValue)           };           window.ShowDialog();       }The editor above shows the code to generate an inline property editor and opens a usercontrol as the dialog editor. We are using this successfully to create our own property editors in place of the standard ones.RegardsPaolo
  • Anonymous
    September 24, 2012
    Cathy is it possible to declaratively implement an Expressiontextbox control in Xaml without binding to an ModelItem and reference the control value in the code behind?