Click here to Skip to main content
15,881,027 members
Articles / Desktop Programming / WPF

Binding To Nullable Enums

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
26 Jan 2015CPOL 19.9K   7  
A short post about how you can easily bind to nullable enums value in WPF

This is a short post about how you can easily bind to nullable enums value in WPF. This is quite useful when you have optional values.

So let's start with a simple ViewModel shall we, it can be seen that this ViewModel uses a Nullable<ProductType> and that the ProductType itself uses the DescriptionAttribute.

C#
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
  
namespace WpfApplication1
{
    public enum ProductType
    {
        [Description("Wet Food")]
        WetFood=1,
  
        [Description("Dry Food")]
        DryFood=2
    }
    public class MainWindowViewModel : INotifyPropertyChanged
    {
 
        private ProductType? selectedProductType ;
 
        public ProductType? SelectedProductType
        {
          get
          {
              return selectedProductType;
          }
          set
          {
              selectedProductType = value;
              OnPropertyChanged();
          }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected virtual void OnPropertyChanged(
            [CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, 
                new PropertyChangedEventArgs(propertyName)); 
        }
    }
}

All good so far, so now let's look at the XAML.

XML
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:sys="clr-namespace:System;assembly=mscorlib"
       xmlns:local="clr-namespace:WpfApplication1"
       Title="MainWindow" Height="350" Width="525">
 
     <Window.Resources>
 
        <ObjectDataProvider x:Key="ProductTypeEnumProvider"
                MethodName="GetValues"
                ObjectType="{x:Type sys:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:ProductType" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources> 
 
    <Grid>
  
        <ComboBox HorizontalAlignment="Center" VerticalAlignment="Center"
                 
                SelectedItem="{Binding SelectedProductType, 
                    Converter={x:Static local:NullableEnumConverter.Instance}, 
                        ConverterParameter={x:Static local:ProductType.DryFood}}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock  Text="{Binding   Path=., Mode=OneWay, 
                        Converter={x:Static local:NullableEnumToFriendlyNameConverter.Instance}}"
                        Height="Auto"
                        Margin="0"
                        VerticalAlignment="Center"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
            <ComboBox.ItemsSource>
                <CompositeCollection>
                    <x:Static Member="local:NullHelper.NullComboStringValue"/>
                    <CollectionContainer Collection="{Binding 
                        Source={StaticResource ProductTypeEnumProvider}}" />
                </CompositeCollection>
            </ComboBox.ItemsSource>
        </ComboBox> 
 
    </Grid>
</Window>

Most of that is standard stuff. What is nice with my approach here is the use of the CompositeCollection which allows you to treat disparate sources as one overall source, in this case an empty string, and the actual enum values form the final ItemSource for the ComboBox.

The empty string is within this small helper class:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace WpfApplication1
{
    public class NullHelper
    {
        public static string NullComboStringValue
        {
            get
            {
                return "None";
            }
        }
    }
}

And there are also a couple of value converters that deal with the nullable enum value:

C#
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Data;
 
namespace WpfApplication1
{
    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);
        }
    }
}

And also one that is responsible for showing the friendly name of the DescriptionAttribute that the enum values make use of:

C#
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Windows.Data;
 
 
namespace WpfApplication1
{
    /// <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 DescriptionAttribute, 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 =
                        (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), 
			false);
  
                    return ((attributes.Length > 0) &&
                            (!String.IsNullOrEmpty(attributes[0].Description)))
                               ?
                                   attributes[0].Description
                               : 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    
    }
}

Hope that helps you in some small way. I know this post was a small one, but hopefully it's useful.

License

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


Written By
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 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • 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 --