【问题标题】:Why can't I select a null value in a ComboBox?为什么我不能在 ComboBox 中选择空值?
【发布时间】:2010-10-05 20:06:01
【问题描述】:

在 WPF 中,似乎不可能(用鼠标)从 ComboBox 中选择“null”值。 编辑澄清一下,这是 .NET 3.5 SP1。

这里有一些代码来说明我的意思。首先,C# 声明:

public class Foo
{
    public Bar Bar { get; set; }
}

public class Bar 
{
    public string Name { get; set; }
}

接下来,我的 Window1 XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" 
                  DisplayMemberPath="Name" 
                  Height="21" 
                  SelectedItem="{Binding Bar}"
                  />
    </StackPanel>
</Window>

最后,我的 Window1 类:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        bars.ItemsSource = new ObservableCollection<Bar> 
        {
            null, 
            new Bar { Name = "Hello" }, 
            new Bar { Name = "World" } 
        };
        this.DataContext = new Foo();
    }
}

和我一起?我有一个 ComboBox,其项目绑定到 Bar 实例列表,其中一个为空。我已将窗口绑定到 Foo 的一个实例,并且 ComboBox 正在显示其 Bar 属性的值。

当我运行这个应用程序时,ComboBox 以空显示开始,因为 Foo.Bar 默认为空。没关系。如果我使用鼠标将 ComboBox 放下并选择“Hello”项目,那也可以。但是,如果我尝试重新选择列表顶部的空项目,ComboBox 将关闭并返回到之前的“Hello”值!

使用箭头键选择空值按预期工作,并且以编程方式设置它也可以工作。它只是用不起作用的鼠标进行选择。

我知道一个简单的解决方法是拥有一个表示 null 的 Bar 实例并通过 IValueConverter 运行它,但是有人可以解释为什么用鼠标选择 null 在 WPF 的 ComboBox 中不起作用吗?

【问题讨论】:

  • 这与我的问题有关:stackoverflow.com/questions/422078/…
  • 如何用方向键选择空值?箭头指向它并关闭标签,还是箭头指向它并按 Enter?有区别吗?
  • 箭头键工作正常,无论我如何离开控件。鼠标根本不允许选择空值。这似乎是一个仅限鼠标的限制。
  • 我偶然发现了完全相同的问题。不幸的是,代表nullBar 实例对我来说不是一个可行的解决方案,因为我的对象无法“在空中”实例化(它们在构造函数中向所有者对象注册),因此我无法创建任何其他实例.尽管如此,为这个问题 +1。

标签: wpf data-binding combobox


【解决方案1】:

我为这个问题找到了一个新的解决方案。 “使用 Mahapps”

  xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"


  <ComboBox x:Name="bars"  **controls:TextBoxHelper.ClearTextButton="True"**
              DisplayMemberPath="Name" 
              Height="21" 
              SelectedItem="{Binding Bar}"/>

您可以使用关闭按钮清除内容。

谢谢。

【讨论】:

  • 有没有办法在不改变其他控件的情况下使用它?
  • 我正在寻找一种将 null 添加到组合框项目的方法。但是删除值按钮是一个更好的主意,并且更容易实现。
【解决方案2】:

我最近在 ComboBoxnull 值方面遇到了同样的问题。 我已经通过使用两个转换器解决了它:

  1. 对于 ItemsSource 属性:它将集合中的 null 值替换为转换器参数内部传递的任何值:

    class EnumerableNullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var collection = (IEnumerable)value;
    
            return
                collection
                .Cast<object>()
                .Select(x => x ?? parameter)
                .ToArray();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
  2. 对于 SelectedValue 属性:这个属性与单个值相同,但有两种方式:

    class NullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value ?? parameter;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value.Equals(parameter) ? null : value;
        }
    }
    

使用示例:

<ComboBox 
    ItemsSource="{Binding MyValues, Converter={StaticResource EnumerableNullReplaceConverter}, ConverterParameter='(Empty)'}" 
    SelectedValue="{Binding SelectedMyValue, Converter={StaticResource NullReplaceConverter}, ConverterParameter='(Empty)'}"
    />

结果:

注意: 如果您绑定到 ObservableCollection,那么您将丢失更改通知。此外,您不希望集合中有多个 null 值。

【讨论】:

  • 不错的解决方案,对 EnumerableNullReplaceConverter.Convert 的改进。然后你可以使用它而不添加空值`var collection = (IEnumerable)value; var list = collection.Cast().ToList(); list.Insert(0, 参数);返回列表;
【解决方案3】:

我花了一天时间寻找解决方案,解决在组合框中选择空值的问题,最后,是的,我终于在写在这个 url 的文章中找到了解决方案:

http://remyblok.tweakblogs.net/blog/7237/wpf-combo-box-with-empty-item-using-net-4-dynamic-objects.html

public class ComboBoxEmptyItemConverter : IValueConverter 
{ 
/// <summary> 
/// this object is the empty item in the combobox. A dynamic object that 
/// returns null for all property request. 
/// </summary> 
private class EmptyItem : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
        // just set the result to null and return true 
        result = null; 
        return true; 
    } 
} 

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    // assume that the value at least inherits from IEnumerable 
    // otherwise we cannot use it. 
    IEnumerable container = value as IEnumerable; 

    if (container != null) 
    { 
        // everything inherits from object, so we can safely create a generic IEnumerable 
        IEnumerable<object> genericContainer = container.OfType<object>(); 
        // create an array with a single EmptyItem object that serves to show en empty line 
        IEnumerable<object> emptyItem = new object[] { new EmptyItem() }; 
        // use Linq to concatenate the two enumerable 
        return emptyItem.Concat(genericContainer); 
    } 

    return value; 
} 

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    throw new NotImplementedException(); 
} 

}

 <ComboBox ItemsSource="{Binding  TestObjectCollection, Converter={StaticResource ComboBoxEmptyItemConverter}}" 
      SelectedValue="{Binding SelectedID}" 
      SelectedValuePath="ID" 
      DisplayMemberPath="Name" />

【讨论】:

  • 在 ConvertBack 上,只需输入:返回值为 EmptyItem ?空:值;
  • 和在SelectedItem 绑定上使用相同的转换器。为我工作!
【解决方案4】:

键盘根本没有选择空的“项目” - 而是取消选择了前一个项目,并且没有(能够)选择后续项目。这就是为什么,在“使用键盘选择“空项目,此后您将无法重新选择先前选择的项目(“Hello”) - 除非通过鼠标!

简而言之,您既不能选择也不能取消选择 ComboBox 中的空项。当您认为这样做时,您实际上是在取消选择或选择上一个或一个新项目。

这可以通过为 ComboBox 中的项目添加背景来最好地看到。当您选择“Hello”时,您会注意到 ComboBox 中的彩色背景,但是当您通过键盘取消选择它时,背景颜色会消失。我们知道这不是空项,因为当我们通过鼠标下拉列表时,空项实际上具有背景色!

以下 XAML(根据原始问题中的内容进行了修改)将在项目后面放置浅蓝色背景,以便您看到此行为。

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <Grid Background="LightBlue" Width="200" Height="20">
                        <TextBlock Text="{Binding Name}" />
                    </Grid>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </StackPanel>
</Window>

如果您想进一步验证,您可以处理 ComboBox 上的 SelectionChanged 事件,并看到“选择 null 项”实际上在其 SelectionChangedEventArgs 中给出了一个空的 addedItems 数组,并且“通过选择 'Hello' 取消选择 null 项鼠标”给出了一个空的 RemovedItems 数组。

【讨论】:

  • 太棒了!这就说得通了。谢谢蒂姆。
  • 您是否尝试使用 SelectedIndex 进行检查?该属性更改为空项目的索引。因此将选择 null-item。 (-1)
【解决方案5】:

只是猜测,但我认为这听起来很合理。

假设组合框使用“ListCollectionView”(lcv 作为它的实例)作为它的项目集合,它应该是。 如果你是程序员,你会做什么?

我会同时响应键盘和鼠标。

一旦我得到键盘输入,我就会使用

lcv.MoveCurrentToNext();

lcv.MoveCurrentToPrevious();

所以,确定键盘工作正常。

然后我正在研究响应鼠标输入。问题来了。

  1. 我想听我的项目的“鼠标点击”事件。但很可能,我的 Item 没有生成,它只是一个占位符。所以当用户点击这个占位符时,我什么也得不到。

  2. 如果我成功举办了活动,接下来是什么。我会调用

    lcv.MoveCurrentTo(selectedItem);

我认为此处为 null 的“selectedItem”不是可接受的参数。

无论如何,这只是猜测。尽管我可以,但我没有时间调试它。我有一堆缺陷要修复。祝你好运。 :)

【讨论】:

    【解决方案6】:

    我知道这个答案不是你所要求的(解释为什么它不适用于鼠标),但我认为这个前提是有缺陷的:

    从我作为程序员和用户(不是 .NET)的角度来看,选择空值是一件坏事。 “null”应该是没有值,而不是你选择的东西。

    如果您需要明确不选择某些内容的能力,我建议您使用您提到的解决方法(“-”、“n.a.”或“none”作为值),或者更好

    • 使用复选框包裹组合框,取消选中该复选框可禁用组合框。从用户的角度和编程的角度来看,这让我觉得这是最简洁的设计。

    【讨论】:

    • 是的,这正是我们所做的。是的,我同意它没有回答这个问题。 :)
    • 我不相信这个解决方案。从用户的角度来看,为组合框中明确包含的内容添加额外的控件(作为空项目,或标记为“(无)”的项目或类似的东西),因此需要额外的点击并进一步复杂化输入表单,这可能被认为是糟糕的 UI 设计。在编程方面,null 确实表示没有值,同时保持与实际属性类型的类型兼容,而不是任何占位符“未选择”对象。
    • 有时(作为用户)您希望将某个值重置为不存在的值。用户不明白为什么他/她不能选择 null。默认情况下,Null 在很多地方被用作未知或无。因此,想要将某些东西重置为 null 一点也不奇怪。如果它是null,我可以设置一个值,为什么我不能将它重置为null?
    • ...这就是为什么程序员不应该被允许设计用户界面
    • 没错,添加一个复选框,因为 null 不能做某事将是最糟糕的 UI 选择。与破坏 UI 相比,我更愿意使用代码或手动转换来反映我无法反映的内容
    【解决方案7】:

    无论多么简单,ComboBox 都需要一个 DataTemplate 来显示项目。 DataTemplate 的工作方式如下:从 instance.[path] 中获取值,例如

    bar1.Car.Color
    

    所以它无法从中获取值

    null.Car.Color
    

    它会抛出一个空引用异常。因此,不会显示空实例。但是 Color - 如果它是引用类型 - 允许为 null,因为在这种情况下不会有异常。

    【讨论】:

    • 但是空实例 is 显示 - 它在列表中显示为空白项,并且可以使用键盘进行选择。只有当您尝试使用鼠标选择它时,ComboBox 才会恢复为之前的值。我理解你的逻辑,但证据不支持它。
    • 对不起,我没有尝试就快速回答。我将在下面给出另一个答案。
    【解决方案8】:

    我遇到了同样的问题,我们做了一些工作,比如像这样向集合项添加 value 属性:

     public class Bar
    
       {
          public string Name { get; set; }
          public Bar Value
          {
             get { return String.IsNullOrEmpty(Name) ?  null :  this; } // you can define here your criteria for being null
          }
       }
    

    然后在添加项目而不是 null 时,我使用相同的对象:

      comboBox1.ItemsSource=  new ObservableCollection<Bar> 
            {
                new Bar(),
                new Bar { Name = "Hello" }, 
                new Bar { Name = "World" } 
            };
    

    而不是 selecteditem 我将它绑定到 selectedvalue :

    <ComboBox Height="23" Margin="25,40,133,0" DisplayMemberPath="Name"
                  SelectedValuePath="Value" 
                  SelectedValue="{Binding Bar}"
                  Name="comboBox1" VerticalAlignment="Top" />
    

    我知道这不是一个完整的解决方案,只是我使用的一种解决方法

    【讨论】:

    • 是的,这正是我在问题的最后一段中所说的“有一个代表 null 的 Bar 实例”的意思。
    【解决方案9】:

    【讨论】:

    • Rudi,我知道 FallbackValue 在使用空对象模式时会很有用,但是当我想明确地将“null”作为选项时,它会有什么帮助呢?你能用一些 XAML 或代码更新你的答案吗?
    【解决方案10】:

    这可能无法完全解决您的问题,但希望它朝着正确的方向发展:

    1. 您是否安装了 SP1?

    From Scott Gu's Blog:

    • NET 3.5 SP1 包括对数据绑定和编辑的多项改进
      WPF。其中包括:
    • 在 {{ Binding }} 表达式中支持 StringFormat 以轻松实现 绑定值的格式
    • 在派生的控件中支持新的交替行 来自 ItemsControl,这使得 更容易在行上设置交替属性(例如:交替背景颜色)
    • 对空值的更好处理和转换支持 在可编辑控件中 将验证规则应用于整个绑定项的验证
    • MultiSelector 支持处理多选和批量 编辑场景
    • IEditableCollectionView 支持接口数据控件 到数据源并启用以事务方式编辑/添加/删除项目
    • 绑定到 IEnumerable 数据时的性能改进 来源

    对不起,如果我浪费了你的时间,这甚至没有关闭..但我认为问题是继承自:

    constraints of the strongly typed dataset

    NullValueDataSet Explained here

    但现在 .Net 3.5 的 SP1 应该已经解决了这个问题..

    【讨论】:

    • 是的,.NET 3.5 SP1。在两台不同的机器上确认(第一次在同事的不同项目中看到它,所以我自己编写了这个测试程序)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 2015-06-30
    • 1970-01-01
    • 2023-03-24
    • 2021-10-02
    相关资源
    最近更新 更多