I want to make a ComboBox in WPF that has one null
item on the top, when this gets selected, the SelectedItem should be set to null (reset to default state). I\
While I agree there are plenty solutions to WPF ComboBox's null item issue, Andrei Zubov's reference to Null Object Pattern inspired me to try a less overkilling alternative, which consists on wrapping every source item allow with a null value (also wrapped) before injecting the whole wrapped collection into ComboBox.ItemsSource property. Selected item will be available into SelectedWrappedItem property.
So, first you define your generic Wrapper...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComboBoxWrapperSample
{
///
/// Wrapper that adds supports to null values upon ComboBox.ItemsSource
///
/// Source combobox items collection datatype
public class ComboBoxNullableItemWrapper
{
string _nullValueText;
private T _value;
public T Value
{
get { return _value; }
set { _value = value; }
}
///
///
///
/// Source object
/// Text to be presented whenever Value argument object is NULL
public ComboBoxNullableItemWrapper(T Value, string NullValueText = "(none)")
{
this._value = Value;
this._nullValueText = NullValueText;
}
///
/// Text that will be shown on combobox items
///
///
public override string ToString()
{
string result;
if (this._value == null)
result = _nullValueText;
else
result = _value.ToString();
return result;
}
}
}
Define your item model...
using System.ComponentModel;
namespace ComboBoxWrapperSample
{
public class Person : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
// Name property
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
// Age property
private int _age;
public int Age
{
get { return _age; }
set
{
_age = value;
OnPropertyChanged("Age");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
// Don't forget this override, since it's what defines ao each combo item is shown
public override string ToString()
{
return string.Format("{0} (age {1})", Name, Age);
}
}
}
Define your ViewModel...
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
namespace ComboBoxWrapperSample
{
public partial class SampleViewModel : INotifyPropertyChanged
{
// SelectedWrappedItem- This property stores selected wrapped item
public ComboBoxNullableItemWrapper _SelectedWrappedItem { get; set; }
public ComboBoxNullableItemWrapper SelectedWrappedItem
{
get { return _SelectedWrappedItem; }
set
{
_SelectedWrappedItem = value;
OnPropertyChanged("SelectedWrappedItem");
}
}
// ListOfPersons - Collection to be injected into ComboBox.ItemsSource property
public ObservableCollection> ListOfPersons { get; set; }
public SampleViewModel()
{
// Setup a regular items collection
var person1 = new Person() { Name = "Foo", Age = 31 };
var person2 = new Person() { Name = "Bar", Age = 42 };
List RegularList = new List();
RegularList.Add(person1);
RegularList.Add(person2);
// Convert regular collection into a wrapped collection
ListOfPersons = new ObservableCollection>();
ListOfPersons.Add(new ComboBoxNullableItemWrapper(null));
RegularList.ForEach(x => ListOfPersons.Add(new ComboBoxNullableItemWrapper(x)));
// Set UserSelectedItem so it targes null item
this.SelectedWrappedItem = ListOfPersons.Single(x => x.Value ==null);
}
// INotifyPropertyChanged related stuff
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
And, finnaly your View (ok, it's a Window)
Favorite teacher
Selected wrapped value:
Reaching this point, did I mention that you could retrieve unwrapped selected item thru SelectedWrappedItem.Value property ?
Here you can get a working sample
Hope it helps someone else