在MVVM取消ComboBoxItems(Deselecting ComboBoxItems in

2019-09-23 04:07发布

我使用其中i绑定下拉框,在视图模型收藏标准的WPF / MVVM应用。

我需要能够从下拉菜单中取消选择一个项目。 意思是,用户应该能够选择的东西,后来决定他们要取消选择它(选择无)它。 问题是,有我的收藏界没有空的元素

我最初的想法是简单的,这将导致其在收集上的空项集合中插入一个新的项目。

这是一个黑客虽然,这会影响使用该集合的视图模型的所有代码。

例如,如果有人在写

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

这将抛出一个空引用异常。

可能的解决方法是:

_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

设置器调用,但现在我的选择的产品只是一个基本的初始化类,而不是空..我可以在二传手添加一些代码来检查/复位,但是这并不好。

Answer 1:

我认为,最简单的方法是使用一个CompositeCollection。 只要您的收藏追加到另一个集合只包含一个空项(null或一个占位符对象,无论您需求),使CompositeCollection的的ItemsSource的组合框。 这可能是它是用来干什么的。

更新:

这原来是更复杂,比我首先想到的,但实际上,我想出了这个解决方案:

<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>

基本上,模式是可以声明为项目使用类的单一NullInstance,并使用其设置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; 留在代码所要做的唯一一件事就是提供单NullItem。



Answer 2:

就个人而言,我倾向于添加任何物体在我的收藏,我结合的“空”版本。 因此,举例来说,如果你在你的视图模型绑定到一个字符串列表,然后,插入在集合的开头空字符串。 如果你的模型具有数据采集,然后在您的视图模型另一个集合包裹。

模型:

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);
    }
}


Answer 3:

您还可以扩展组合框,使去选择。 添加一个或多个挂钩(例如,按ESC键),允许用户设置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;
        }
    }
}

编辑只是注意到弗洛里安GI的评论,上下文菜单可能是另一个很好的取消挂钩的补充。



Answer 4:

一种选择是创建一个你想要的最初的“空”元素的消费者特别是暴露的适配器集合。 你将需要创建(如果你想相同的性能与的ObservableCollection)和INotifyCollectionChanged实现IList的一个包装类。 你要听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>



Answer 5:

一个简单的方法是重新模板组合框,这样,当有一个项目选择出现在对话框的右侧有一个小X。 点击即清除了所选择的项目。

这有没有让你的ViewModels的优势,任何更复杂



文章来源: Deselecting ComboBoxItems in MVVM
标签: wpf mvvm