(WF4) Showing an InArgument<bool> as a CheckBox in the Workflow Designer Property Grid

Here’s a workflow designer FAQ finally getting the attention it deserves, and helping show off how to do custom property editing in designer.

“How do I show an InArgument<bool>/<enum> in the property grid as a checkbox/combobox?”

[Aside: Before we start the guide, note that actually doing the thing you are asking about has certain implications, and that there’s one obvious alternative that works in some cases: using a plain property, instead of an InArgument<T> typed-property.

Once you go the InArgument<T> route and restrict the UI to just a checkbox, the person using your custom activity can’t set an InArgument<bool> to custom expressions through the property grid. Which might be useful. But they can still set it to a custom expression by other ways, like editing XAML, if they can access the XAML. And they might choose to set no value at all, i.e. their InArgument<bool> == null. Take all of this into account when designing your custom activity logic.]

OK, here goes.

We have a custom activity. Via the property grid it must be possible to set the EnableLions argument to one of exactly two expressions: ‘true’, or ‘false’, using a checkbox.

The first thing we should do in order to accomplish the task is associate the custom property editor to the property. We can do this in one of two ways.

Way #2) registering metadata (someone will need to explicitly call this code):

void RegisterMetadata()

{

AttributeTableBuilder tb = newAttributeTableBuilder();

tb.AddCustomAttributes(typeof(ZooActivity), "EnableLions",

newEditorAttribute(

typeof(InArgumentBoolPropertyEditor),

typeof(PropertyValueEditor)));

MetadataStore.AddAttributeTable(tb.CreateTable());

}

Once this attribute applied, when you select the activity the workflow designer will try to instantiate an InArgumentBoolPropertyEditor object to act as the property editor. The property editor type is a new type defined thusly:

we must set InlineEditorTemplate, which should be a DataTemplate, but in practice can come from anywhere

for a rehosted app, an easy way to store your inline editor templates is in Application.xaml as application resources, which is convenient the demo here, but in practice you’ll probably need to store it elsewhere.

The resource loaded above is just a regular WPF DataTemplate, holding the checkbox.

In order to bind Checkbox.IsChecked to the property value Value, which is of type InArgument<bool>, we had to define a custom type converter class. It’s used as a StaticResource.

the DataContext which the DataTemplate is being bound to is an instance of ModelPropertyEntry (inherits PropertyEntry)

you can pass a ModelPropertyEntry to converters such as ModelPropertyEntryToModelItemConverter (System.Activities.Presentation.Converters), this is helpful for implementing some more advanced custom property editors

Last piece of the puzzle is the converter implementation.

publicclassInArgumentBoolConverter : IValueConverter

{

publicobject Convert(

object value,

Type targetType,

object parameter,

System.Globalization.CultureInfo culture)

{

if (value isInArgument<bool>)

{

Activity<bool> expression = ((InArgument<bool>)value).Expression;

if (expression isLiteral<bool>)

{

return ((Literal<bool>)expression).Value;

}

}

returnnull;

}

publicobject ConvertBack(

object value,

Type targetType,

object parameter,

System.Globalization.CultureInfo culture)

{

if (value isbool)

{

returnnewInArgument<bool>(newLiteral<bool>((bool)value));

}

else

{

returnnull;

}

}

}

Notes:

We only understand how to convert the InArgument<bool> if it is set to a Literal<bool> expression. Otherwise the checkbox will show a gray mark (indeterminate)

If the InArgument<bool> value is null, we also return null and the checkbox state is indeterminate

Remember we’re binding to PropertyEntry.Value value, which is expected by our activity OM to be an object of type InArgument<bool>.If we return something else, nothing happens

Literal<T> can be used for booleans and enums, but doesn’t work for most complex types

We could also have gone with VisualBasicValue<bool>(“false”) and VisualBasicValue<true>(“true”) instead of Literals, but note that in this case we are responsible for parsing the VisualBasic string – there is no API to call which can figure out the value as bool for us.

For the ComboBox/ListBox scenario, things should be similar, except that we need to remain aware of the expected item type of the control, and write our converter accordingly.