41

Suppose we have a DataSource bind to a collection from Database. There is no null item of course. How to add a void item into a ComboBox, so that at first load user would see an empty string. I don't want to add a dummy/void object into the Collection. Optimally in XAML. Any proposals?

tshepang
  • 12,111
  • 21
  • 91
  • 136
theSpyCry
  • 12,073
  • 28
  • 96
  • 152
  • 3
    Beware, that the solution provided DOESN'T work with binding. – Cartesius00 May 23 '11 at 19:00
  • 2
    I found a way around the binding problem see this post: http://stackoverflow.com/questions/6446699/how-do-you-bind-a-collectioncontainer-to-a-collection-in-a-view-model – Frinavale Jun 23 '11 at 15:59

4 Answers4

46
<ComboBox Name="myComboBox" Width="200" Background="White">    
    <ComboBox.ItemsSource>    
        <CompositeCollection>
           <ComboBoxItem IsEnabled="False" Foreground="Black">Select Item</ComboBoxItem>
           <CollectionContainer Collection="{Binding Source={StaticResource DataKey}}" />    
        </CompositeCollection>
    </ComboBox.ItemsSource>
</ComboBox>

EDIT

As @surfen mentioned in comment, BindingProxy is workaround for the binding issue

Community
  • 1
  • 1
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
  • OK wonderful. But how to make the first item nonselectable? Only datasource items can be selected. – theSpyCry Jul 27 '09 at 14:31
  • 2
    See edited post, you just need to add IsEnabled="False" Foreground="Black" to items properties – Arsen Mkrtchyan Jul 27 '09 at 14:43
  • OK sorry I didn't notice that properties. Thank you ! – theSpyCry Jul 27 '09 at 14:47
  • 2
    I can't seem to get this to work in a ViewModel binding scenario... Any ideas? – Anderson Imes Jul 27 '09 at 15:41
  • 14
    Ah... I figured it out. CompositeCollection is not Freezable, so it won't work with binding. Unfortunate. – Anderson Imes Jul 27 '09 at 15:52
  • 4
    I found a way around the CompositeCollection problem: http://stackoverflow.com/questions/6446699/how-do-you-bind-a-collectioncontainer-to-a-collection-in-a-view-model – Frinavale Jun 23 '11 at 15:55
  • 2
    YES, It works! I use BindingProxy to workaround the binding issue: http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ – surfen Mar 15 '12 at 18:53
  • I also bind `SelectedItem` to my `ViewModel.SelectedItem`. I've created a `NullItemConverter:IValueConverter` that would convert null to string: "Select Item" and back. It didn't work because the first item is not string - it's `ComboBoxItem`. I've replaced it with a simple string: `Empty Item`. Now it works - I can set `SelectedItem=null` in my VM and Empty Item becomes selected. – surfen Mar 16 '12 at 20:41
  • 1
    I do something similar with a converter, however i allow selection of the null [here](http://sites.google.com/site/wpfprojects/projects/ComboBoxAllowingNullsTest.zip) – Aran Mulholland Jul 28 '09 at 07:23
  • 3
    @surfen since your blog has moved this is the new url: http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ – Xuntar Jun 02 '15 at 14:00
3
<UserControl.Resources>
    <CollectionViewSource x:Key="Modules" Source="{Binding Path=Modules}" />
</UserControl.Resources>

<abv:ComboBox SelectedIndex="0" IsNullable="True"
    SelectedItem="{Binding Path=SelectedModule, Mode=TwoWay}">
    <abv:ComboBox.ItemsSource>
        <CompositeCollection>
            <ComboBoxItem Content="{DynamicResource EmptyModuleComboBox}"/>
            <CollectionContainer Collection="{Binding Source={StaticResource Modules}}" />
        </CompositeCollection>
    </abv:ComboBox.ItemsSource>
</abv:ComboBox>

public class ComboBox : System.Windows.Controls.ComboBox
{
    public static readonly DependencyProperty IsNullableProperty =
        DependencyProperty.Register("IsNullable", typeof(bool), typeof(ComboBox));

    public bool IsNullable
    {
        get { return (bool)GetValue(IsNullableProperty); }
        set { SetValue(IsNullableProperty, value); }
    }

    public ComboBox()
    {
        Loaded += ComboBox_Loaded;
    }

    void ComboBox_Loaded(object sender, RoutedEventArgs e)
    {

        if (IsNullable)
        {
            this.ItemContainerStyle = new Style();

            this.ItemContainerStyle.Setters.Add(new EventSetter()
            {
                Event = ComboBoxItem.PreviewMouseUpEvent,
                Handler = new MouseButtonEventHandler(cmbItem_PreviewMouseUp)
            });
        }
    }

    public void cmbItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        if (Items.IndexOf(sender as ComboBoxItem) == 0)
        {
            SelectedItem = null;
        }
    }
}
2

For binding on MVVM object:

                        <ComboBox Name="cbbFiltres" SelectedItem="{Binding ElmtInfo, Mode=TwoWay}" Height="26" MinWidth="90" SelectedIndex="0" SelectedValuePath="Id">
                        <ComboBox.Resources>
                            <CollectionViewSource x:Key="cvsFiltres" Source="{Binding Elmts.items}"/>
                        </ComboBox.Resources>
                        <ComboBox.ItemsSource>
                            <CompositeCollection>
                                <model:tblFiltreChamps Desc="{x:Static resx:resMain.enumAucun}" Id="0"/>
                                <CollectionContainer Collection="{Binding Source={StaticResource cvsFiltres}}" />
                            </CompositeCollection>
                        </ComboBox.ItemsSource>
                    </ComboBox>

And for binding on :

<Label Visibility="{Binding Path=SelectedValue, ElementName=cbbFiltres, Converter={StaticResource NullToVisibility}}" />

And the generic converter :

    public class ConvNullToVisibility : IValueConverter {
    /// <summary>Convertisseur pour le Get.</summary>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) return Visibility.Visible; // Pour annuler l'effet dans le designer: http://stackoverflow.com/questions/33401900/wpf-detect-design-mode-in-a-converter
        return ((value == null) || (string.IsNullOrEmpty(value.ToString())) || (value.ToString() == "0")) ? Visibility.Collapsed : Visibility.Visible;
    }

    /// <summary>Convertisseur inverse, pour le Set (Binding).</summary>
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        if (value is Visibility) {
            return (((Visibility)value) == Visibility.Visible) ? true : false;
        } else return false;
    }
}

Just important to declare the SelectedValuePath in combobox. :-)

david
  • 170
  • 10
-2

Try the Mahapps combobox.

xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"

  <ComboBox x:Name="bars"  **controls:TextBoxHelper.ClearTextButton="True"**
              DisplayMemberPath="Name" 
              Height="21" 
              SelectedItem="{Binding Bar}"/>

Combo Box View

Anurag
  • 557
  • 6
  • 14