How to add a separator to a WPF combobox that is databound?

Is there any way I can add a seprator into a WPF combobox that is databound? ie in my xaml, the combobox's ItemsSource="{Binding TheList}". The list is an observable collection of objects, one of i want separated from the rest. This list is also generated from sql, so its not hard-coded or anything. I wouldn't want the seaprator to be selectable, either. Thanks!

Upvotes: 4

Views: 10551

Answers (7)

Addio
Addio

Reputation: 137

All you need to do is separate your ComboBox items from your data items. You can do this by creating a property that builds the vstructure from a getter, and returns a collection of objects. Just make sure when your data has changed, trigger the PropertyChanged event for ComboItems property as well.

Here is a little example that creates a ComboBox that contains a collection of Versions, with a label and separator control at the top.

You can set the type of SelectedComboItem to the same as your data type, but it will invalidate and display the invalid style if the selected item's type doesn't match.

// Header Label and Separator to display at top of ComboBox
private object[] _defaultComboItems = new object[] { new Label() { Content = "Versions"}, new Separator() };

// Property that builds the structure for the ComboBox.
public IEnumerable<object>? ComboItems 
    => Versions == null 
    ? _defaultComboItems 
    : _defaultComboItems.Concat(Versions);

// Observable collection for the data items
private ObservableCollection<Version> _versions;
public ObservableCollection<Version> Versions
{
    get => _versions;
    set
    {
        if (_versions != value)
        {
            if (_versions != null)
            {
                _versions.CollectionChanged -= signalComboItemsChanged;
            }

            // Notify that Versions and ComboItems are changing.
            OnPropertyChanging(nameof(ComboItems));
            OnPropertyChanging(nameof(Versions));

            _versions = value;

            // Notify that Versions and ComboItems have changed.
            OnPropertyChanged(nameof(Versions));
            OnPropertyChanged(nameof(ComboItems));

            if (value != null)
            {
                // Subscribe to CollectionChanged to rebuild ComboItems when Versions changes.
                value.CollectionChanged += signalComboItemsChanged;
            }
        }

        // Callback to signal ComboItems changes.
        void signalComboItemsChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged(nameof(ComboItems));
        }
    }
}

// Selected item in the ComboBox, which can be a control or data item.
private object? _selectedComboItem;
public object? SelectedComboItem
{
    get => _selectedComboItem;
    set
    {
        if (_selectedComboItem != value)
        {
            OnPropertyChanging(nameof(SelectedComboItem));
            _selectedComboItem = value;
            OnPropertyChanged(nameof(SelectedComboItem));
        }
    }
}

// Property to retrieve selected data item.
public Version? SelectedVersion
    => SelectedComboItem as Version;
<ComboBox ItemsSource="{Binding ComboItems}"
          SelectedItem="{Binding SelectedComboItem}"/>

Upvotes: 0

Steve S
Steve S

Reputation: 89

XAML only solution for every item. You could probably add triggers based on ItemsControl AlternationCount if it was always the same list of items.

This is the result. enter image description here

<ComboBox.ItemContainerStyle>
  <Style TargetType="ComboBoxItem">
    <Setter Property="BorderBrush" Value="DarkGray" />
    <Setter Property="BorderThickness" Value="0,1,0,0" />
  </Style>
</ComboBox.ItemContainerStyle>

Upvotes: 1

The Ashen Wolf
The Ashen Wolf

Reputation: 76

I was building my combo box within the .cs file dynamically and without the use of Bindings, but I believe it could help you. The separators can't be clicked on and selected.

ComboBox frameColor = new ComboBox()
{
    Width = 200,
    Margin = new Thickness(180, -780, 0, 0),
    VerticalAlignment = VerticalAlignment.Center,
    IsEditable = false,
    Items =
    {
        new ComboBoxItem(){...},
        new Separator(),
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
        new Separator(),
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
        new ComboBoxItem(){...},
    }

This is the result:
Combo box with separators

Upvotes: 3

TorATB
TorATB

Reputation: 1

If you want to do the same in C# code:

Cbx.Items.Add("ABC");
Cbx.Items.Add("DEF");
ComboBoxItem item = new ComboBoxItem();
item.Content = "GHI";
item.BorderBrush = Brushes.Black;
item.BorderThickness = new Thickness(0, 0, 0, 2);
Cbx.Items.Add(item);
Cbx.Items.Add("KLM");
Cbx.Items.Add("NOP");

Upvotes: 0

nrod
nrod

Reputation: 398

Although my ComboxBox (at the moment) is not DataBound I achieved the concept of a separator by adding a bottom border to an element. In this example two lines before and two lines after the separator.

<ComboBox x:Name="Cbx" SelectionChanged="Cbx_SelectionChanged">
  <ComboBoxItem Content="select one..." Foreground="DarkGray" IsSelected="True" /
  <ComboBoxItem Content="ABC" />
  <ComboBoxItem Content="DEF" />
  <ComboBoxItem Content="GHI" BorderBrush="Black" BorderThickness="0,0,0,2" />
  <ComboBoxItem Content="KLM" />
  <ComboBoxItem Content="NOP" />
</ComboBox>

Upvotes: 1

rodolfoprado
rodolfoprado

Reputation: 126

you need to use a ComboBox.ItemTemplate to draw your itens

http://www.silverlightshow.net/items/Silverlight-ComboBox.aspx

Upvotes: 0

bouvierr
bouvierr

Reputation: 3791

Check this solution. It uses a Style to change the Template of certain ComboBoxItem objects

Upvotes: 4

Related Questions