在 MVVM 中取消选择 ComboBoxItems [英] Deselecting ComboBoxItems in MVVM

查看:43
本文介绍了在 MVVM 中取消选择 ComboBoxItems的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用标准的 wpf/mvvm 应用程序,我将组合框绑定到 ViewModel 上的集合.

I am using a standard wpf/mvvm application where i bind combo boxes to collections on a ViewModel.

我需要能够从下拉列表中取消选择一个项目.意思是,用户应该能够选择一些东西,然后决定他们想要取消选择它(不选择).问题是我绑定的集合中没有空元素

I need to be able to de-select an item from the dropdown. Meaning, users should be able to select something, and later decide that they want to un-select it (select none) for it. the problem is that there are no empty elements in my bound collection

我最初的想法是简单地在集合中插入一个新项目,这将导致在集合顶部有一个空项目.

my initial thought was simply to insert a new item in the collection which would result having an empty item on top of the collection.

虽然这是一个hack,它会影响在视图模型上使用该集合的所有代码.

this is a hack though, and it affects all code that uses that collection on the view model.

例如如果有人要写

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

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

this will throw a null reference exception.

可能的解决方法是:

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

这会起作用,但无法确保该集合的任何未来使用不会造成任何中断.

this will work, but no way to ensure any future uses of that collection won't cause any breaks.

什么是能够添加空项目以便用户可以取消选择的好模式/解决方案.(我也知道 CollectionView 结构,但这对于如此简单的事情来说似乎有点矫枉过正)

what's a good pattern / solution for being able to adding an empty item so the user can de-select. (I am also aware of CollectionView structure, but that seems like a overkill for something so simple)

更新

采纳@hbarck 的建议并实施 CompositeCollection(概念的快速证明)

went with @hbarck suggestion and implemented CompositeCollection (quick proof of concept)

    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),这很棒.

this code work with existing bindings (including SelectedItem) which is great.

这样做的一个问题是,如果项目完全为空,则在选择它时永远不会调用 SelectedItem setter.

One problem with this is, that if the item is completely null, the SelectedItem setter is never called upon selecting it.

如果我将这一行修改为:

if i modify that one line to this:

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

setter 被调用,但现在我选择的项目只是一个基本的初始化类而不是 null.我可以在 setter 中添加一些代码来检查/重置,但这并不好.

the setter is called, but now my selected item is just a basic initialized class instead of null.. i could add some code in the setter to check/reset, but that's not good.

推荐答案

我认为最简单的方法是使用 CompositeCollection.只需将您的集合附加到另一个只包含空项目的集合(空或占位符对象,无论您需要什么),并使 CompositeCollection 成为 ComboBox 的 ItemsSource.这可能就是它的用途.

I think the easiest way would be to use a CompositeCollection. Just append your collection to another collection which only contains the empty item (null or a placeholder object, whatever suites your needs), and make the CompositeCollection the ItemsSource for the ComboBox. This is probably what it is intended for.

更新:

事实证明这比我最初想象的要复杂,但实际上,我想出了这个解决方案:

This turns out to be more complicated than I first thought, but actually, I came up with this solution:

<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,并使用 Converter 在设置 VM 属性时将此实例转换为 null.转换器可以通用的写成这样(是VB的,希望大家不要介意):

Basically, the pattern is that you declare a singleton NullInstance of the class you use as items, and use a Converter which converts this instance to null when setting the VM property. The converter can be written universally, like this (it's VB, I hope you don't mind):

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.

Since you can reuse the converter, you can set this all up in XAML; the only thing that remains to be done in code is to provide the singleton NullItem.

这篇关于在 MVVM 中取消选择 ComboBoxItems的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆