Introduction to WF Designer Rehosting - Part Two
In first part of this serie we saw how to bind our designer properties to activity properties and create a custom designer for our activity. Let’s see how to bind to activity properties through the designer and allow user to enter values in the designer.
If you have some experience with WF Activities you know that input properties are of type InArgument so how do we convert and bind our regular types to a InArgument<T>?
Let’s go with simple types like String, supposing our SendTo activity has an Enabled property of type InArgument<bool> and we have a CheckBox on our designer. First approach would be to bind directly through ModelItem property of the designer, inherited from the base class by using a ValueConverter:
1 | <CheckBox IsChecked="{Binding Path=ModelItem.IsEnabled, Mode=TwoWay, Converter={StaticResource ValueToInArgumentLiteralConverter}}" /> |
1 | public class ValueToInArgumentLiteralConverter : IValueConverter |
This approach works fine but only for simple scenarios. If you want more control on selected properties, changing values, having dependent properties or if your designer UI is complex, I advise using a ViewModel instead. You can access current ModelItem from your view:
1 | public interface ISendToDesigner |
Did you see SelectedModelItem is of dynamic type? Exposing underlying ModelItem as dynamic allows us to directly assign properties without going through indexers. This trick results in much cleaner and better maintainable code, supposing you have unit tests for your view models! Also I’m using an extension method named ToInArgument which converts the primitive values to InArgument. Here’s the extension method:
1 | public static InArgument<T> ToInArgument<T>(this T value) |
This works for primitive values but what if you have Enums, Guids, Lists or Dictionary properties on your Activity? Unfortunately though this seem to work, it won’t and designer shows an error. The alternative would be to use VisualBasicValue instead. If you are a C# developer, welcome yourself to the world of VB.NET!
Seems VisualBasicValue<T> can be assigned as InArgument<T> so you just need to convert your values to a VisualBasicValue. This approach works file with Enums as well, too bad you can not create an extension method with Enum constraint. Here’s an extension method that converts your enum to VisualBasicValue:
1 | public static InArgument<T> ToExpression<T>(this T value) |
Now List and Dictionaries are a little bit different. Let’s see how I convert List<Guid> to a VisualBasicValue and you can use the same approach to make it work for all generic types:
1 | public static InArgument<List<Guid>> ToExpression(this List<Guid> list) |
On the next part of this post, we’ll see how to allow user to select existing variables (of a specific types) and import needed namespaces into our workflow xaml.