为什么我不能在ComboBox中选择空值?

时间:2009-02-06 00:03:44

标签: wpf data-binding combobox

在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实例列表,其中一个为null。我已将窗口绑定到Foo的实例,并且ComboBox正在显示其Bar属性的值。

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

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

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

10 个答案:

答案 0 :(得分:15)

键盘根本没有选择空“项目” - 而是取消选择前一项目,而不选择后续项目。这就是“选择“带键盘的空项目,之后你无法重新选择之前选择的项目(”你好“) - 除非通过鼠标!

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

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

以下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事件,看看“选择空项”实际上在SelectionChangedEventArgs中给出了一个空数组AddedItems,并“通过选择'Hello'来取消选择'null'鼠标“给出一个空的RemovedItems数组。

答案 1 :(得分:11)

我最近遇到了与 ComboBox null 值相同的问题。 我用两个转换器解决了这个问题:

  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;
        }
    }
    
  3. 使用示例:

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

    结果:

    enter image description here

    注意: 如果绑定到 ObservableCollection ,则会丢失更改通知。此外,您不希望集合中包含多个 null 值。

答案 2 :(得分:8)

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

enter image description here

enter image description here

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

感谢。

答案 3 :(得分:5)

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

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

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

  • 使用可以取消选中的复选框包装组合框以禁用组合框。从用户的角度和编程方式来看,这是最干净的设计。

答案 4 :(得分:2)

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

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

答案 5 :(得分:1)

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

  1. 您安装了SP1吗?
  2. 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应该已经解决了这个问题..

答案 6 :(得分:0)

答案 7 :(得分:0)

我遇到了同样的问题,比如将值属性添加到集合项中,如下所示:

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

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

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

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

答案 8 :(得分:0)

ComboBox需要一个DataTemplate来显示项目,无论它有多简单。 DataTemplate的工作原理如下:从实例中获取一个值。[path],例如

bar1.Car.Color

因此无法从

获取值
null.Car.Color

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

答案 9 :(得分:0)

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

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

我将对键盘和鼠标都做出回应。

一旦我获得键盘输入,我使用

lcv.MoveCurrentToNext();

lcv.MoveCurrentToPrevious();

所以,确保键盘运行良好。

然后我正在处理响应鼠标输入。这就是问题所在。

  1. 我想听我的项目'MouseClick'事件。但可能,我的Item没有生成,它只是一个占位符。因此,当用户点击此占位符时,我什么也得不到。

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

    lcv.MoveCurrentTo(将selectedItem);

  3. 我认为这里的“selectedItem”是一个不可接受的参数。

    无论如何,这只是猜测。虽然我能够,但我没有时间调试它。我有一堆缺陷要修复。祝好运。 :)