08 September 2011

WPF - ComboBox or DataGridComboBoxColumn with "none" option

Something trivial that's currently not possible with the ComboBox or DataGridComboBoxColumn (Toolkit) controls of WPF is adding a "none"-option on top op their dropdown without having to add it to the ItemsSource, which would be really bad design. So this is how I implemented this common requirement:

The converter

A converter is needed to make sure the none-option actualy sets the bound property to null. Without it the property would simply not change.
I created one called NullObjectConverter. It's Convert-method simply returns the value-parameter.
The ConvertBack-method does this:
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
  if (value == null || value.GetType() != targetType)
    return null;
  else
    return value;
}
I've put the resource for it in the resources of the App-class, so I don't have to do that in every XAML-file that needs it:
<this:NullObjectConverter x:Key="convNull" />

The XAML

This is my XAML for a DataGridComboBoxColumn. Notice how it uses the converter above. Important is that it's ItemsSource is a CompositeCollection composed from the none-option and the actual list of options:
<toolkit:DataGridComboBoxColumn Header="Store"
         SelectedValueBinding="{Binding Store,
                                Converter={StaticResource convNull}}">
  <toolkit:DataGridComboBoxColumn.ItemsSource>
    <CompositeCollection>
      <ListBoxItem>(none)</ListBoxItem>
      <CollectionContainer x:Name="cboStoreCollection" />
    </CompositeCollection>
  </toolkit:DataGridComboBoxColumn.ItemsSource>
</toolkit:DataGridComboBoxColumn>
The none-option will arrive in the converter as a ListBoxItem while the target type will be the type of the bound object, and therefore the converter will send NULL to the bindings' source when it's selected.

Finally, notice that the CollectionContainer that contains the actual list of options gets it's items in the code-behind, and therefore has a name:
cboStoreCollection.Collection = mStores;

No comments:

Post a Comment