ExpressionTextBox – for arguments only (well mostly)

[12/19 edit: ExpressionTextBox is not strictly for arguments only, see the exception here]

In a previous post I alluded to the fact that you should be able to bind an ExpressionTextBox to a CLR property. My hazy memory was that this was done at one point in time by omitting the ArgumentToExpressionConverter from the Expression binding and mucking about with the OwnerActivity and/or PathToArgument properties. So I went ahead and modified my existing sample activity to use a property and changed my binding to remove the ArgumentToExpressionConverter. My activity designer looks fine. I drop it into a workflow, great. Start typing, I have Intellisense, fantastic. Lose focus, and I get the following error:

Visual Studio has encountered an exception. This may be caused by an extension. See the Activity Log for more details.

Hmm. The expression editing service is not happy. I wander over to get some wisdom from the developer and tester. As it turns out, properties just can’t deal with expressions at all. Take a simple example. Create an activity as follows:

 namespace MyNS
{
    [Designer(typeof(SimpleDesigner))]
    public sealed class Simple : CodeActivity
    {        
        public int Number { get; set; }
        protected override void Execute(CodeActivityContext context)
        {            
            throw new NotImplementedException();
        }
    }
}

Now take this activity and drop it into a workflow. Hand edit the workflow XAML file to change value of the Number property to [1+2] (the square brackets denote an expression). Reload the workflow XAML in the designer. You get the following error:

System.Xaml.XamlObjectWriterException: 'Failed to create a 'Number' from the text '[1+2]'.' Line number '2' and line position '213'. ---> System.Exception: [1+2] is not a valid value for Int32. ---> System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at System.ComponentModel.Int32Converter.FromString(String value, NumberFormatInfo formatInfo)
   at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   --- End of inner exception stack trace ---

The explanation is as follows. When you lose focus from the ExpressionTextBox, the text in the ExpressionTextBox is handed to compiler to create a VisualBasicValue(T) or VisualBasicReference(T). There is no converter available to convert these objects to the CLR property. The stack trace above illustrates the issue – you can see that there is no built-in converter from VisualBasicValue(Int32), which is the type of the expression in the square brackets, to Int32. And even you tried to write a converter, the expressions would not be executed at runtime, because they are not bound at runtime. Take a look at CacheMetadata – there is no way for you to do anything interesting with properties in there.

The moral of this story: you can’t bind expressions to properties. If you want an expression, you have to bind it to an argument. Nobody is really sure what PathToArgument does any more, since it’s not used to bind to CLR properties. Don’t use it. If you get an add-in exception for your custom activity, you probably forgot the ArgumentToExpressionConverter. We’ll file a bug to make the expression editing service fail more gracefully, but I don’t think the error will change in this release.