klutch1991
klutch1991

Reputation: 239

Binding ComboBoxItem to a command in WPF?

I want to have a <ComboBox>, which ItemsSource is bound to some ViewModel collection, but I want the first <ComboBoxItem> to be bound to ICommand property, and name it (for example) "Add new element to a collection...". Any suggestions?

Upvotes: 0

Views: 349

Answers (1)

Szabolcs D&#233;zsi
Szabolcs D&#233;zsi

Reputation: 8843

You could do this:

In your view model you have the Command and the Items collection you want to set as the ItemsSource:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public MainWindowViewModel()
    {
        Items = new ObservableCollection<string>()
        {
            "John Doe",
            "Lara Croft",
            "Sam Fisher"
        };

        AddNewItemCommand = new DelegateCommand(OnAddNewItem);
    }

    private void OnAddNewItem()
    {
        Items.Add("New Item");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<string> _items;
    public ObservableCollection<string> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            NotifyPropertyChanged(nameof(Items));
        }
    }

    public ICommand AddNewItemCommand { get; set; }

    [NotifyPropertyChangedInvocator]
    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

You use a CompositeCollection in your XAML to add the "Add new item" in front of your bound collection:

<ComboBox>
    <ComboBox.Resources>
        <CollectionViewSource x:Key="ItemsCollection" Source="{Binding Path=Items}" />
    </ComboBox.Resources>
    <ComboBox.ItemsSource>
        <CompositeCollection>
            <ComboBoxItem FontStyle="Italic">
                <i:Interaction.Behaviors>
                    <local:UnselectableComboBoxItemBehavior />
                </i:Interaction.Behaviors>
                <TextBlock>
                <Hyperlink Command="{Binding AddNewItemCommand}">Add new item</Hyperlink>
                </TextBlock>
            </ComboBoxItem>
            <CollectionContainer Collection="{Binding Source={StaticResource ItemsCollection}}" />
        </CompositeCollection>
    </ComboBox.ItemsSource>
</ComboBox>

And create a Behavior to suppress selection of the first item which is the "Add new item" item:

public class UnselectableComboBoxItemBehavior : Behavior<ComboBoxItem>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseDown += AssociatedObjectOnPreviewMouseDownAndMouseLeftButtonUp;
        AssociatedObject.PreviewMouseLeftButtonUp += AssociatedObjectOnPreviewMouseDownAndMouseLeftButtonUp;
    }

    private void AssociatedObjectOnPreviewMouseDownAndMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (e.Source == sender)
        {
            e.Handled = true;
        }
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewMouseDown -= AssociatedObjectOnPreviewMouseDownAndMouseLeftButtonUp;
        AssociatedObject.PreviewMouseLeftButtonUp -= AssociatedObjectOnPreviewMouseDownAndMouseLeftButtonUp;
        base.OnDetaching();
    }
}

Result:

enter image description here

Upvotes: 5

Related Questions