Quick WPF/Silverlight tip: Generic Converter MarkupExtension 4/3/10
Hey there!
It’s been quite a while since the last English post – XAMLCast has been taking much of my blogging time
Today’s tip is an expansion of a method originally developed by Dr. WPF in this post: http://www.drwpf.com/blog/Home/tabid/36/EntryID/48/Default.aspx .
Usually, when working with Converters in WPF/SL, we always follow the same steps:
- Create a class that derives from IValueConverter:
public MyConverter : IValueConverter {} - Implement Convert (and sometimes ConvertBack)
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // convert and return something } - Instantiate the converter as a resource and use it:
<ResourceDictionary ...> <local:MyConverter x:Key="TheConverter" /> </ResourceDictionary> ... {Binding Converter={StaticResource TheConverter} ...}
Well, it works but it’s not a compact syntax. Following Dr. WPF’s idea, we can use a MarkupExtension to replace the StaticResource by a static instance of the Converter:
public class MyConverter: MarkupExtension, IValueConverter
{
private static MyConverter _converter;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// convert and return something
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// convert and return something (if needed)
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new MyConverter();
return _converter;
}
}
Usage:
xmlns:conv="[Path to namespace that contains the converter]"
...
{Binding Converter={conv:MyConverter}}
Now that’s pretty!
The only problem is that with this method, you’d have to repeat the implementation of the ProvideValue for each converter you create, and we programmers hate repeating ourselves
One solution I found is to create a generic abstract class that will contain that implementation, and derive each converter from that class. It’s cleaner and works the same:
using System;
using System.Windows.Data;
using System.Windows.Markup;
namespace VirtualDreams.Converters
{
[MarkupExtensionReturnType(typeof(IValueConverter))]
public abstract class ConverterMarkupExtension<T> : MarkupExtension where T : class, IValueConverter, new()
{
private static T _converter;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
{
_converter = new T();
}
return _converter;
}
}
}
Let’s apply it to MyConverter:
public class MyConverter: ConverterMarkupExtension<MyConverter>, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// convert and return something
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// convert and return something (if needed)
}
}
Usage:
xmlns:conv="[Path to namespace that contains the converter]"
...
{Binding Converter={conv:MyConverter}}
Simpler, less repetitive – that’s the way I like it!
Happy converting!
Roberto
This blog post is also available on CodeProject
Categories: .net, Dicas, Silverlight, WPF
Comments»
no comments yet - be the first?