Using untyped arguments in an ExpressionTextBox

The ExpressionTextBox sample code in the SDK works just fine in beta 2. The sample demonstrates the use of an ExpressionTextBox in a custom activity designer. In the sample, I implemented a simple MultiAssign activity, which assigns two values to two variables. In the example, I used untyped InArguments and OutArguments. That particular example was bad for a couple of reasons:

  1. If you don’t specify an ExpressionType on an ExpressionTextBox, you get a weird red box around invalid expressions inside the box. Also the hosted compiler and hostable editor do not work as well as you’d expect (less predictive behavior in IntelliSense, poor compiler error messages, can’t use to bind to delegates, etc). It is best practice to specify the expression type.
  2. In post beta 2 bits, when you tried to use the activity in a workflow, it would throw at runtime saying that it could find an argument of type <blah>.

Regarding the second issue - in beta 2 (and earlier), the runtime would try to automatically add untyped arguments. This behavior was buggy, so the functionality was removed. Now, if you want to use untyped arguments, you have to explicitly add the argument in CacheMetadata. The code below shows the corrected MultiAssign activity. Thanks Nate Talbert for providing this code for the corrected MultiAssign activity and the explanation.

     public sealed class MultiAssign : CodeActivity
    {
        public OutArgument To1 { get; set; }
        public OutArgument To2 { get; set; }
        public InArgument Value1 { get; set; }
        public InArgument Value2 { get; set; }

        protected override void CacheMetadata(CodeActivityMetadata metadata)
        {
            AddArgument(metadata, this.To1, "To1", ArgumentDirection.Out);
            AddArgument(metadata, this.To2, "To2", ArgumentDirection.Out);
            AddArgument(metadata, this.Value1, "Value1", ArgumentDirection.In);
            AddArgument(metadata, this.Value2, "Value2", ArgumentDirection.In);
        }

        void AddArgument(CodeActivityMetadata metadata, Argument argument, string argumentName, ArgumentDirection direction)
        {
            Type argumentType = typeof(object);

            if (argument != null)
            {
                argumentType = argument.ArgumentType;
            }

            RuntimeArgument runtimeArgument = new RuntimeArgument(argumentName, argumentType, direction);
            metadata.Bind(argument, runtimeArgument);
            metadata.AddArgument(runtimeArgument);
        }

        protected override void Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the arguments
            this.To1.Set(context, this.Value1.Get(context));
            this.To2.Set(context, this.Value2.Get(context));
        }
    }

That said, the code above still does not fix problem #1 above, which is the problem of not passing the ExpressionType to the ExpressionTextBox. So, instead of changing the sample to use the above code, I changed the MultiAssign activity from one that assigns to any argument type to one that assigns only to strings. This reflects the mainline scenario for arguments, which is that arguments are typed and that ExpressionType is passed to the ExpressionTextBox. As a best practice, type your arguments. Post beta 2 versions of the SDK will have the string MultiAssign sample.

If you absolutely can’t live without untyped arguments, you should strongly consider doing a bit of trickery to provide the ExpressionType based on the expression text that was entered. If you pass a null target type to the CreatePrecompiled* methods on VisualBasicDesignerHelper, the type of the expression will be returned to you and you can use this information in a style setter.