Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Binding To Nullable Enums

0.00/5 (No votes)
26 Jan 2015 1  
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.

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.

<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:

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:

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:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here