Click here to Skip to main content
Click here to Skip to main content

Tagged as

XAML : Binding To Nullable Enums With Friendly Names

, 3 Mar 2014
Rate this:
Please Sign up or sign in to vote.
I was at work the other day and I had a situation where I needed to use a nullable enum. I wanted this to be available within a ComboBox but It was not compulsary for the users to select something. I also wanted to allow the ComboBox to print a friendly name of the Enum […]

I was at work the other day and I had a situation where I needed to use a nullable enum. I wanted this to be available within a ComboBox but It was not compulsary for the users to select something. I also wanted to allow the ComboBox to print a friendly name of the Enum value.

The friendly name thing is something I have covered before in a previous article Binding-and-Using-Friendly-Enums-in-WPF but I have never really come across a time where I needed to allow a ComboBox with null selection support for the user.

I did not want to polute my enum values with a special “None” value, as that is really what the nullable aspect of it was doing. So I started to explore things a bit, and this is what I ended up with

My ViewModel and Enum

public enum Animals
{
    [EnumMember(Value = "Its a nice kitty")]
    Cat=1,

    [EnumMember(Value = "Its a bow wow")]
    Dog = 2
}

public class MainWindowViewModel : INPCBase
{
    private Animals? selectedAnimal;

    public MainWindowViewModel()
    {
        SelectedAnimal = null;
    }

    public Animals? SelectedAnimal
    {
        get { return this.selectedAnimal; }
        set
        {
            RaiseAndSetIfChanged(ref this.selectedAnimal, value, () => this.SelectedAnimal);
            MessageBox.Show(
                "SelectedAnimal: " + (!selectedAnimal.HasValue ?
                    NullHelper.NullComboStringValue : selectedAnimal.ToString()));
        }
    }

}

Next up was my View. The interesting points here are

  1. We use a ObjectDataProvider to supply the enum values
  2. We use a CompositeCollection (which doesn’t work with Binding ie ItemsSource being bound) where we get the Enum values and also a special “Null” value from a helper class
  3. We use a special NullableEnumToFriendlyNameConverter value converter to supply the friendly name lookup value for the enum (providing its not the special “Null” value
  4. We use a special NullableEnumConverter to convetr back to either null or a picked enum value for the ViewModel property setter
<Window x:Class="NullEnumCombo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:nullEnumCombo="clr-namespace:NullEnumCombo"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>

        <ObjectDataProvider x:Key="animalTypeFromEnum"
                            MethodName="GetValues"
                            ObjectType="{x:Type sys:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="nullEnumCombo:Animals" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

    <Grid>
        <ComboBox Width="150" Height="20" 
            HorizontalAlignment="Center" 
            VerticalAlignment="Center"
            SelectedItem="{Binding SelectedAnimal, 
                Converter={x:Static nullEnumCombo:NullableEnumConverter.Instance},
                    ConverterParameter={x:Static nullEnumCombo:Animals.Cat}}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock  Text="{Binding   Path=., Mode=OneWay,
                        Converter={x:Static 
                           nullEnumCombo:NullableEnumToFriendlyNameConverter.Instance}}"
                        Height="Auto"
                        Margin="0"
                        VerticalAlignment="Center"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>

            <ComboBox.ItemsSource>
                <CompositeCollection>
                    <x:Static Member="nullEnumCombo:NullHelper.NullComboStringValue"/>
                    <CollectionContainer
                        Collection="{Binding Source={StaticResource animalTypeFromEnum}}" />
                </CompositeCollection>
            </ComboBox.ItemsSource>

        </ComboBox>
    </Grid>
</Window>

Here are the 2 value converters and the small helper class

public class NullableEnumConverter : IValueConverter
{
    private NullableEnumConverter()
    {

    }

    static NullableEnumConverter()
    {
        Instance = new NullableEnumConverter();
    }

    public static NullableEnumConverter Instance { get; private set; }

    public object Convert(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return NullHelper.NullComboStringValue;
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        Type enumType = parameter.GetType();
        if (value.ToString().Equals(NullHelper.NullComboStringValue))
        {
            return null;
        }
        object rawEnum = Enum.Parse(enumType, value.ToString());
        return System.Convert.ChangeType(rawEnum, enumType);
    }
}

/// <summary>
/// This class simply takes an enum and uses some reflection to obtain
/// the friendly name for the enum. Where the friendlier name is
/// obtained using the EnumMemberAttribute, which hold the localized
/// value read from the resource file for the enum
/// </summary>
[ValueConversion(typeof(object), typeof(String))]
public class NullableEnumToFriendlyNameConverter : IValueConverter
{

    private NullableEnumToFriendlyNameConverter()
    {

    }

    static NullableEnumToFriendlyNameConverter()
    {
        Instance = new NullableEnumToFriendlyNameConverter();
    }

    public static NullableEnumToFriendlyNameConverter Instance { get; private set; }

    #region IValueConverter implementation

    /// <summary>
    /// Convert value for binding from source object
    /// </summary>
    public object Convert(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        // To get around the stupid wpf designer bug
        if (value != null && !string.IsNullOrEmpty(value.ToString()) && 
             !value.ToString().Equals(NullHelper.NullComboStringValue))
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            // To get around the stupid wpf designer bug
            if (fi != null)
            {
                var attributes =
                    (EnumMemberAttribute[])fi.GetCustomAttributes(typeof(EnumMemberAttribute), false);

                return ((attributes.Length > 0) &&
                        (!String.IsNullOrEmpty(attributes[0].Value)))
                            ?
                                attributes[0].Value
                            : value.ToString();
            }
        }

        return NullHelper.NullComboStringValue;
    }

    /// <summary>
    /// ConvertBack value from binding back to source object
    /// </summary>
    public object ConvertBack(object value, Type targetType, 
           object parameter, CultureInfo culture)
    {
        throw new Exception("Cant convert back");
    }
    #endregion
}

public class NullHelper
{
    public static string NullComboStringValue
    {
        get
        {
            return "(None)";
        }
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140827.1 | Last Updated 3 Mar 2014
Article Copyright 2014 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid