Workflow Activities and Data Templates

A.K.A. “Why won’t the workflow designer use my data template?”

I was on site with a customer today writing some custom activities and found a problem that wasn’t immediately obvious how I could get around it (and indeed it’s taken me some hours of head scratching and debugging to finally find the answer).

I have a custom activity on which I’ve defined the following property…

 public Comparison Comparison { get; set; }

The comparison class is one I’ve made up myself, and I have derived classes such as BooleanComparison, StringComparison and so on. The actual value of the Comparison property is set by my designer. In the designer I then want to show the appropriate UI for each type of designer, so I created some data templates…

 <DataTemplate DataType="{x:Type helpers:Comparison}">
    <TextBlock Text="BOOL"/>
</DataTemplate>

<DataTemplate DataType="{x:Type helpers:StringComparison}">
    <TextBlock Text="STRING"/>
</DataTemplate>

<DataTemplate DataType="{x:Type helpers:DateTimeComparison}">
    <TextBlock Text="DATETIME"/>
</DataTemplate>

OK, so these are hardly going to set the world on fire, but you get the idea. I then databound the Content property to a ContentPresenter so…

 <ContentControl Content="{Binding ModelItem.Comparison}"/>

But on screen this never worked – I kept on getting the TypeName show up – a sure sign that the right data template wasn’t being selected. Puzzled, I fiddled around for some hours trying to solve this. I thought I must have the XAML wrong, so popped the lot into a new WPF project but it all worked as expected. Even more puzzling. I then decided to move the property into the designer, which changed the binding from ModelItem.Comparison to just Comparison, and lo & behold it now worked.

Knowing a bit about how ModelItem worked under the covers (it’s a custom type converter) I twigged – the object returned from ModelItem.Comparison wasn’t a real Comparison object (or rather, it was wrapped in something, which just so happened to be the ModelItem class).

So, armed with this I knew I was just about there, and a quick stint in Reflector told me I should call the GetCurrentValue() method of ModelItem in order to get the actual value for my binding. So I crufted up the following converter and now all is sweet in Workflow land for me – all I have to do is add on the converter to my binding and it’ll then find the appropriate data template.

<ContentControl Content="{Binding ModelItem.Comparison, Converter={StaticResource modelItemConverter}}" />

The converter class is shown below – it’s trivial to implement but this will save you typing a few lines!

 public class ModelItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        object retVal = value;

        if (value is ModelItem)
            retVal = ((ModelItem)value).GetCurrentValue();

        return retVal;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}

Hopefully this will save someone some time!