Blue Flower

Switch Converter deserves special attention. Simple and easy, he has an amazing versatility. On the basis of easy to build a set of common types of converters without declaring new classes and not just.
image
In an analysis of medium and large-scale projects can reveal important regularity - a significant part of the class-converters, in fact, it contains the logic equivalent constructions if-else, and the switch-case-default. Immediately there is a desire to reduce everything to a common denominator, in order not to create a class-twins, and it is not so difficult.

ISwitchConverter

using System;
using System.Collections.Generic;
using System.Windows.Data;

namespace Aero.Converters.Patterns
{
    public class CaseSet : List
    {
        public static readonly object UndefinedObject = new object();
    }

    public interface ICase
    {
        object Key { get; set; }
        object Value { get; set; }
        Type KeyType { get; set; }
    }

    public interface ISwitchConverter : IValueConverter
    {
        CaseSet Cases { get; }
        object Default { get; set; }
        bool TypeMode { get; set; }
    }
}


ICompositeConverter

using System.Windows.Data;

namespace Aero.Converters.Patterns
{
    public interface ICompositeConverter : IValueConverter
    {
        IValueConverter PostConverter { get; set; }
        object PostConverterParameter { get; set; }
    }
}


SwitchConverter

using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using Aero.Converters.Patterns;

namespace Aero.Converters
{
    [ContentProperty("Cases")]
    public class SwitchConverter : DependencyObject, ISwitchConverter, ICompositeConverter
    {
        public static readonly DependencyProperty DefaultProperty = DependencyProperty.Register(
            "Default", typeof(object), typeof(SwitchConverter), new PropertyMetadata(CaseSet.UndefinedObject));

        public SwitchConverter()
        {
            Cases = new CaseSet();
        }

        public object Default
        {
            get { return GetValue(DefaultProperty); }
            set { SetValue(DefaultProperty, value); }
        }

        public CaseSet Cases { get; private set; }

        public bool TypeMode { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (TypeMode) value = value == null ? null : value.GetType();
            var pair = Cases.FirstOrDefault(p => Equals(p.Key, value) || SafeCompareAsStrings(p.Key, value));
            var result = pair == null ? Default : pair.Value;
            value = result == CaseSet.UndefinedObject ? value : result;
            return PostConverter == null
                ? value
                : PostConverter.Convert(value, targetType, PostConverterParameter, culture);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (TypeMode) value = value == null ? null : value.GetType();
            var pair = Cases.FirstOrDefault(p => Equals(p.Value, value) || SafeCompareAsStrings(p.Value, value));
            value = pair == null ? Default : pair.Key;
            return PostConverter == null
                ? value
                : PostConverter.ConvertBack(value, targetType, PostConverterParameter, culture);
        }

        private static bool SafeCompareAsStrings(object a, object b)
        {
            if (a == null && b == null) return true;
            if (a == null || b == null) return false;
            return string.Compare(a.ToString(), b.ToString(), StringComparison.OrdinalIgnoreCase) == 0;
        }

        public IValueConverter PostConverter { get; set; }
        public object PostConverterParameter { get; set; }
    }
}


Used converter is quite simple, but when not set the default value (default), using pass-through conversion - compare the two examples below.

<Grid.Resources>
    <SwitchConverter x:Key="NumberSwitchConverter">
        <Case Key="0" Value="Zero"/>
        <Case Key="1" Value="One"/>
    </SwitchConverter>
</Grid.Resources>
<TextBlock Text="{Binding Number, Converter={StaticResource NumberSwitchConverter}}"/>

Number==0 => out: Zero
Number==1 => out: One
Number==2 => out: 2

<Grid.Resources>
    <SwitchConverter x:Key="NumberSwitchConverter" Default="Hello">
        <Case Key="0" Value="Zero"/>
        <Case Key="1" Value="One"/>
    </SwitchConverter>
</Grid.Resources>
<TextBlock Text="{Binding Number, Converter={StaticResource NumberSwitchConverter}}"/>

Number==0 => out: Zero
Number==1 => out: One
Number==2 => out: Hello

It is easy to work with numbers, enumerations, strings, Boolean values, and it supports chained composition.

Moreover one has a more significant bonus. The WPF there is the concept of Template Selectors, which is not supported, for example, on the Windows Phone Silverlight. If you ever use it, then surely you have noted is not very convenient moment - need to create a C# is the class where not very nice way to access the XAML-Resource. Take a look at the sample from MSDN .

In fact, the problem of patterns of selectors in most actual cases is very easy to reduce to the use of conventional converters, and the only advantage of Template Selector in an additional provision of access to a data container, to which the template is applied.

Especially for such scenarios Switch Converter supports a special mode of Type Mode, which is the key to the value of the object type.

<SwitchConverter TypeMode="True" Default="{StaticResource DefaultDataTemplate}" x:Key="InfoConverter">
    <Case KeyType="local:Person" Value="{StaticResource PersonDataTemplate}"/>
    <Case KeyType="local:PersonGroup" Value="{StaticResource PersonGroupDataTemplate}"/>
</SwitchConverter>


<ListBox ItemsSource="{Binding Items}">
	<ListBox.ItemTemplate>
		<DataTemplate>
			<ContentControl Template="{Binding Converter={StaticResource TemplateSelectorConverter}}"/>
		</DataTemplate>
	</ListBox.ItemTemplate>
</ListBox>

It is noteworthy that there is no need to access from the C# code resources XAML, and do not need to create a separate class to implement the template selector, since all the logic is contained in the markup.

I hope you enjoyed this story.
Examples of the use of different converters are available with the library Aero Framework [ backup link ].

Thank you for your attention!

P.S. Composite Converters