48

在 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 中不起作用吗?

4

10 回答 10

19

好吧,我最近遇到了与ComboBox的值相同的问题。我已经通过使用两个转换器解决了这个问题:

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

    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,那么您将丢失更改通知。此外,您不希望集合中有多个值。

于 2015-04-28T13:50:24.640 回答
16

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

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

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

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

<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 的空数组.

于 2009-12-14T23:12:38.767 回答
9

我为这个问题找到了一个新的解决方案。“使用 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}"/>

在此处输入图像描述

在此处输入图像描述

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

谢谢。

于 2017-04-18T07:39:02.097 回答
7

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

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

如果您需要明确不选择某些东西的能力,我会建议您提到的解决方法(“-”、“na”或“none”作为值),或者更好

  • 用一个复选框包裹组合框,可以取消选中以禁用组合框。从用户的角度和编程的角度来看,这让我觉得它是最简洁的设计。
于 2009-02-17T08:08:03.387 回答
5

我花了一天的时间来解决在组合框中选择空值的问题,最后,是的,最后我在写在这个 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" />
于 2013-07-27T22:52:29.190 回答
1

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

  1. 你安装SP1了吗?

来自 Scott Gu 的博客:


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

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

强类型数据集的约束

NullValueDataSet 在这里解释

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

于 2009-02-06T00:49:06.967 回答
0

尝试 Binding.FallbackValue

来自我打赌你不知道的关于 WPF 中数据绑定的 6 件事

于 2009-02-06T13:32:13.853 回答
0

我遇到了同样的问题,我们做了一些工作,比如向集合项添加 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" />

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

于 2009-02-09T09:01:14.563 回答
0

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

bar1.Car.Color

所以它不能从

null.Car.Color

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

于 2009-02-17T07:41:47.630 回答
0

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

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

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

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

lcv.MoveCurrentToNext();

或者

lcv.MoveCurrentToPrevious();

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

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

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

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

    lcv.MoveCurrentTo(selectedItem);

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

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

于 2009-02-17T09:01:44.413 回答