Silverlight Transform Markup Extension

<ComboBox ItemsSource="{Binding ItemsSource, ElementName=MyDataSource}, Converter={StaticResource MyConverter}}" Width="100" Height="30"/>

<ComboBox ItemsSource="{Binding ItemsSource, ElementName=MyDataSource}, Converter={StaticResource MyConverter}}" Width="100" Height="30"/>

Did you ever have one of those situations where you need to apply a data source to a control, but also convert the each of the data source elements?

Normally we set the data source to the DataContext or ItemsSource property and add an item template where we specify the converter, but sometimes this can’t be done this way, for one reason or the other. So, I worked out a simple solution: the TransformedItemSource! What it does is, it allows us to specify a converter for each of the source values in the data source collection, not the data source as a whole.

Here it is:

public class TransformedItemSource : DependencyObject, IEnumerable

{

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TransformedItemSource), new PropertyMetadata(null));

    public static readonly DependencyProperty ConverterProperty = DependencyProperty.Register("Converter", typeof(IValueConverter), typeof(TransformedItemSource), new PropertyMetadata(null));

    public static readonly DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(Object), typeof(TransformedItemSource), new PropertyMetadata(null));

    public static readonly DependencyProperty CultureProperty = DependencyProperty.Register("Culture", typeof(CultureInfo), typeof(TransformedItemSource), new PropertyMetadata(CultureInfo.CurrentCulture));

    public static readonly DependencyProperty TargetTypeProperty = DependencyProperty.Register("TargetType", typeof(Type), typeof(TransformedItemSource), new PropertyMetadata(null));

 

    public Type TargetType

    {

        get

        {

            return this.GetValue(TargetTypeProperty) as Type;

        }

        set

        {

            this.SetValue(TargetTypeProperty, value);

        }

    }

 

    public Object Parameter

    {

        get

        {

            return this.GetValue(ParameterProperty);

        }

        set

        {

            this.SetValue(ParameterProperty, value);

        }

    }

 

    public CultureInfo Culture

    {

        get

        {

            return this.GetValue(CultureProperty) as CultureInfo;

        }

        set

        {

            this.SetValue(CultureProperty, value);

        }

    }

 

    public IEnumerable ItemsSource

    {

        get

        {

            return this.GetValue(ItemsSourceProperty) as IEnumerable;

        }

        set

        {

            this.SetValue(ItemsSourceProperty, value);

        }

    }

 

    public IValueConverter Converter

    {

        get

        {

            return this.GetValue(ConverterProperty) as IValueConverter;

        }

        set

        {

            this.SetValue(ConverterProperty, value);

        }

    }

 

    IEnumerator IEnumerable.GetEnumerator()

    {

        foreach (var current in this.ItemsSource ?? new Object[0])

        {

            var targetType = this.TargetType ?? ((current != null) ? current.GetType() : null);

 

            yield return this.Converter.Convert(current, targetType, this.Parameter, this.Culture);

        }

    }

}

It inherits from DependencyObject, this is so that I can apply bindings to its properties. It will try to iterate through all the items in the ItemsSource property and convert one at a time using the supplied converter, culture and parameter. An example might be:

<UserControl.Resources>

    <my:TransformedItemSource x:Key="MyTransformer" ItemsSource="{Binding ItemsSource, ElementName=MyDatasource}" Converter="{StaticResource MyConverter}"/>

</UserControl.Resources>

<StackPanel x:Name="LayoutRoot">

    <ComboBox ItemsSource="{StaticResource MyTransformer}" Width="100" Height="30"/>

<StackPanel>

A nice way to use it is through a markup extension:

public class TransformExtension : DependencyObject, IMarkupExtension<IEnumerable>

{

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TransformExtension), new PropertyMetadata(null));

    public static readonly DependencyProperty ConverterProperty = DependencyProperty.Register("Converter", typeof(IValueConverter), typeof(TransformExtension), new PropertyMetadata(null));

    public static readonly DependencyProperty ParameterProperty = DependencyProperty.Register("Parameter", typeof(Object), typeof(TransformExtension), new PropertyMetadata(null));

    public static readonly DependencyProperty CultureProperty = DependencyProperty.Register("Culture", typeof(CultureInfo), typeof(TransformExtension), new PropertyMetadata(CultureInfo.CurrentCulture));

    public static readonly DependencyProperty TargetTypeProperty = DependencyProperty.Register("TargetType", typeof(Type), typeof(TransformExtension), new PropertyMetadata(null));

 

    public Type TargetType

    {

        get

        {

            return this.GetValue(TargetTypeProperty) as Type;

        }

        set

        {

            this.SetValue(TargetTypeProperty, value);

        }

    }

 

    public Object Parameter

    {

        get

        {

            return this.GetValue(ParameterProperty);

        }

        set

        {

            this.SetValue(ParameterProperty, value);

        }

    }

 

    public CultureInfo Culture

    {

        get

        {

            return this.GetValue(CultureProperty) as CultureInfo;

        }

        set

        {

            this.SetValue(CultureProperty, value);

        }

    }

 

    public IEnumerable ItemsSource

    {

        get

        {

            return this.GetValue(ItemsSourceProperty) as IEnumerable;

        }

        set

        {

            this.SetValue(ItemsSourceProperty, value);

        }

    }

 

    public IValueConverter Converter

    {

        get

        {

            return this.GetValue(ConverterProperty) as IValueConverter;

        }

        set

        {

            this.SetValue(ConverterProperty, value);

        }

    }

 

    public IEnumerable ProvideValue(IServiceProvider serviceProvider)

    {

        return new TransformedItemSource { ItemsSource = this.ItemsSource, Parameter = this.Parameter, Culture = this.Culture, Converter = this.Converter };

    }

}

The TransformExtension class just delegates all of the work to the TransformedItemSource, but because it is a markup extension, it can be used as:

<ComboBox ItemsSource="{Binding ItemsSource, ElementName=MyDataSource, Converter={StaticResource MyConverter}}" Width="100" Height="30"/>

Enjoy!

Published by

Ricardo Peres

Team Leader at Dixons Carphone. Microsoft MVP.

Leave a Reply

Your email address will not be published. Required fields are marked *