Deselecting ComboBoxItems in MVVM

I use the standard wpf / mvvm application, where I bind combo fields to collections in the ViewModel.

I need to be able to deselect a dropdown. Meaning, users should be able to select something, and then decide that they want to cancel it (select none) for it. the problem is that there are no empty elements in my linked collection

my initial thought was to simply insert a new item into the collection, which would cause the collection to have an empty item.

this is a hack, but it affects all the code that uses this collection in the view model.

for example, if someone had to write

_myCollection.Frist(o => o.Name == "foo") 

this will result in a null reference exception.

Possible workaround:

_myCollection.Where(o => o != null).First(o => o.Name == "foo");

, , - .

/ , . ( CollectionView, - )

@hbarck CompositeCollection ( )

    public CompositeCollection MyObjects {
        get {
            var col = new CompositeCollection();

            var cc1 = new CollectionContainer();
            cc1.Collection = _actualCollection;

            var cc2 = new CollectionContainer();
            cc2.Collection = new List<MyObject>() { null }; // PROBLEM

            col.Add(cc2);
            col.Add(cc1);
            return col;
        }
    }

( SelectedItem), .

, , , SelectedItem .

:

            cc2.Collection = new List<MyObject>() { new MyObject() }; // PROBLEM

setter, null.. setter / reset, .

+5
5

, - CompositeCollection. , ( -, ) CompositeCollection ItemsSource ComboBox. , .

Update:

, , :

<Window x:Class="ComboBoxFallbackValue"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:TestWpfDataBinding"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    xmlns:w="clr-namespace:System.Windows;assembly=WindowsBase"
Title="ComboBoxFallbackValue" Height="300" Width="300">
<Window.Resources>
    <t:TestCollection x:Key="test"/>
    <CompositeCollection x:Key="MyItemsSource">
        <x:Static Member="t:TestCollection.NullItem"/>
        <CollectionContainer Collection="{Binding Source={StaticResource test}}"/>
    </CompositeCollection>
    <t:TestModel x:Key="model"/>
    <t:NullItemConverter x:Key="nullItemConverter"/>
</Window.Resources>
<StackPanel>
    <ComboBox x:Name="cbox" ItemsSource="{Binding Source={StaticResource MyItemsSource}}" IsEditable="true" IsReadOnly="True" Text="Select an Option" SelectedItem="{Binding Source={StaticResource model}, Path=TestItem, Converter={StaticResource nullItemConverter}, ConverterParameter={x:Static t:TestCollection.NullItem}}"/>
    <TextBlock Text="{Binding Source={StaticResource model}, Path=TestItem, TargetNullValue='Testitem is null'}"/>
</StackPanel>

, , singleton NullInstance , , , null VM. , ( VB, , ):

Public Class NullItemConverter
Implements IValueConverter

Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    If value Is Nothing Then
        Return parameter
    Else
        Return value
    End If
End Function

Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
    If value Is parameter Then
        Return Nothing
    Else
        Return value
    End If
End Function

, XAML; , , - singleton NullItem.

+3

"" , . , , , . , .

:

public class Foo
{
    public List<string> MyList { get; set;}
}

:

public class FooVM
{
    private readonly Foo _fooModel ;

    private readonly ObservableCollection<string> _col;
    public ObservableCollection<string> Col // Binds to the combobox as ItemsSource
    {
        get { return _col; }
    }

    public string SelectedString { get; set; } // Binds to the view

    public FooVM(Foo model)
    {
        _fooModel = model;
        _col= new ObservableCollection<string>(_fooModel.MyList);
        _col.Insert(0, string.Empty);
    }
}
+2

ComboBox, . (, ), SelectedItem null.

using System.Windows.Input;

public class NullableComboBox : ComboBox
{
    public NullableComboBox()
        : base()
    {
        this.KeyUp += new KeyEventHandler(NullableComboBox_KeyUp);

        var menuItem = new MenuItem();
        menuItem.Header = "Remove selection";
        menuItem.Command = new DelegateCommand(() => { this.SelectedItem = null; });
        this.ContextMenu = new ContextMenu();
        this.ContextMenu.Items.Add(menuItem);
    }

    void NullableComboBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (e.Key == Key.Escape || e.Key == Key.Delete) 
        {
            this.SelectedItem = null;
        }
    }
}

. .., .

+1

- , , "" . -, IList ( ​​ , ObservableCollection) INotifyCollectionChanged. INotifyCollectionChanged , , . .

public sealed class FirstEmptyAdapter<T> : IList<T>, IList, INotifyCollectionChanged
{
    public FirstEmptyCollection(ObservableCollection<T> wrapped)
    {
    }

    //Lots of adapter code goes here...
}

, IList, - INotifyCollectionChanged IEnumerable<T>.

0

One simple approach is to rebuild the ComboBox so that when you select an item, a small X appears on the right side of the window. A click that deletes the selected item.

This has the advantage of not making your ViewModels more complex.

0
source

All Articles