Null Object pattern for a WPF ComboBox’s deselected value
Last week, I encountered a problem doing some fiddly UI behaviour in a Prism/MVVM app. I wanted something like this:
I wanted a ComboBox where you can choose one of the available options, or none of them. Null, deselected, whatever.
Unfortunately for me, WPF doesn’t support binding null values in ComboBoxes, ListBoxes or ListViews. Now this is a pretty common problem, and a quick google reveals a few solutions available to this problem:
- Add an “IsNull” property to your models
- Wrap the ComboBox in an adapter that provides null support
- Create an attached property that clears the selected item when the user hits DEL
However, if like us, your domain model is all nicely decoupled through interfaces, the easiest way to get null values into a ComboBox is with the null object pattern. (And thankfully for me doesn’t require any knowledge of WPF control internals to implement).
Here’s what my null object looked like:
public class NullCurve : IFxCurve, IEquatable<NullCurve>{ public string Name { get { return ""; } } public void DoStuff() {} // Has no implementation // Note the equality overload. Null objects are value types -- they // have no identity, and are all equal. public bool Equals(NullCurve other) { return other != null; } public override bool Equals(object obj) { return Equals(obj as NullCurve); } public override int GetHashCode() { return -1; }}
To use it, you simply pop a null object at the start of your list. And check whether it was selected on submit:
public class FxCurveSelectionViewModel : ViewModelBase{ public ObserableCollection<IFxCurve> AvailableCurves { get; private set; } public IFxCurve SelectedCurve { get; set; } public FxCurveSelectionViewModel(ICurveRepository curves) { AvailableCurves = new ObservableCollection<IFxCurve>(); AvailableCurves.Add(new NullCurve()); AvailableCurves.AddRange(curves.GetAllCurves()); } public void Submit() { if (SelectedCurve.Equals(new NullCurve())) // nothing was selected }}
XAML binding is standard; no need to change anything because we handle it all in the view model. Which arguably is not the most DRY solution long-term, but sufficient until I learn more about WPF internals to do it elegantly with XAML.