Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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!
Comments
Anonymous
June 24, 2011
Hi, I just stumbled across your blog, and I'm enjoying it. I do have a tip for this one, you could use the framework class System.Activities.Presentation.Converters.ModelToObjectValueConverter, which does the same as ModelItemConverter. TimAnonymous
June 24, 2011
Great - thanks for this Tim, I should have looked for this before constructing my own. I'll use the inbuilt one in the future - as an old colleague of mine used to say, "less code more smiles". Cheers, Morgan