4
votes

I am binding a WPF ComboBox to a nullable property of type MyEnum? (where MyEnum is an enumerated type)

I am programmatically populating the ComboBox items like this:

// The enum type being bound to 
enum MyEnum { Yes, No }

// Helper class for representing combobox listitems
// (a combination of display string and value)
class ComboItem {
  public string Display {get;set}
  public MyEnum? Value {get;set}
}

private void LoadComboBoxItems()
{
  // Make a list of items to load into the combo
  var items = new List<ComboItem> {
    new ComboItem {Value = null, Display = "Maybe"},
    new ComboItem {Value = MyEnum.Yes, Display = "Yes"},
    new ComboItem {Value = MyEnum.No, Display = "No"},};

  // Bind the combo's items to this list.
  theCombo.ItemsSource = items;
  theCombo.DisplayMemberPath = "Display";
  theCombo.SelectedValuePath = "Value";
}

Also in the code-behind, I am setting the DataContext to an instance of a class with a property called TheNullableProperty (for this example anyway) of type MyEnum?.

The binding of theCombo's SelectedValue is done in my XAML file.

<ComboBox 
  Name="theCombo" 
  SelectedValue="{Binding Path=TheNullableProperty,
                          UpdateSourceTrigger=PropertyChanged}"/>

Problem:

When the value of the bound property is initially non-null, the combo box displays the value properly.

But when the value of the bound property is initially null, the combo box is blank.

It looks like every aspect of data binding is working apart from the representation of the null value when the combobox is first shown.

For example: you can select Maybe from the dropdown, and the bound property is correctly set to null. It's just that initial loading that's failing. Maybe I need to just manually set the SelectedValue initially...

What I Ended Up Doing

  • Add a hidden textblock databound to the underlying nullable enum value via a Converter that converts from the nullable enum to a string (enum.ToString, or "null").
  • Load up the combo box with 'ComboItems' each having a string Label (displayed in the combo) and a string Value equal to the enum values as strings (and "null" for the null value).
  • Data-bind the combo box to the textblock.

    /// <summary>
    /// Convert from EnumeratedType? to string (null->"null", enum values->ToString)
    /// </summary>
    public class EnumConverter<T> : IValueConverter where T:struct 
    {
      public static string To(T? c)
      {
        if (c == null)
          return "null";
        return c.ToString();
      }
    
      public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
      {
        return To((T?)value);
      }
    
      public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
      {
        var s = (string) value;
        if (s == "null")
          return null;
        return (T?)Enum.Parse(typeof(T), s);
      }
    }
    
    public class MyEnumConverter : EnumConverter<MyEnum>
    {
    }
    
    public class ComboItem
    {
      public string Value { get; set; }
      public string Label { get; set; }
    
      public ComboItem(MyEnum? e, string label)
      {
        Value = MyEnumConverter.To(e);
        Label = label;
      }
    }
    
    static IEnumerable<ComboItem> GetItems()
    {
      yield return new ComboItem(null, "maybe");
      yield return new ComboItem(MyEnum.Yes, "yup");
      yield return new ComboItem(MyEnum.No, "nope");
    }
    
    private void SetupComboBox()
    {
      thecombo.ItemsSource = GetItems().ToList();
    }
    
5

5 Answers

5
votes

You cannot bind to a null value by design in WPF (at least 3.5 SP1). This means that at the moment Source gets a Null as a value your Binding will be automatically broken and won't work even if you specify a valid value for the Source. You need to provide some "heart beating" mechanism for your binding via Value Converter. So that Converter transforms null value to some "Undefined" (non-null) for the Targets and than is able to convert your "Undefined" back to null...

2
votes

There seem to be many issues with having null be a valid value for a ComboBox or ListBox, as similar questions have been asked here, here and here.

There hasn't been a superb, ogre-slaying answer to any of those.

My own suggestion is to add Maybe as a member of MyEnum. I know it's just an example but if everything with a MyEnum has it as nullable, then maybe MyEnum should have an Undefined member (or something similar), this is pretty common with enums anyway.

1
votes

I found this solution online, which I think is pretty deck. I don't fully understand the implementation -- it's some pretty serious .NET kung fu -- but it's great from my perspective as a user. Give it a try:

http://philondotnet.wordpress.com/2009/09/18/how-to-select-null-none-in-a-combobox-listbox-listview/

1
votes

DependencyProperty.UnsetValue Field

using System;

using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace MyConverters
{
  [ValueConversion(typeof(DateTime), typeof(String))]
  public class StringToIntConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      int number;

      return int.TryParse(value.ToString(), out number) ? number : DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return value.ToString();
    }
  }
}
0
votes

Here's a veryy simple solution to this problem:

Instead of having an item with a value of null in your ItemsSource, use DbNull.Value as item or as the item's value property.

I described this approach in detail here: https://stackoverflow.com/a/44170898/6713814