wpf 为什么我不能在 ComboBox 中选择空值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/518579/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't I select a null value in a ComboBox?
提问by Matt Hamilton
In WPF, it seems to be impossible to select (with the mouse) a "null" value from a ComboBox. EditTo clarify, this is .NET 3.5 SP1.
在 WPF 中,从 ComboBox 中选择(用鼠标)一个“空”值似乎是不可能的。编辑澄清一下,这是 .NET 3.5 SP1。
Here's some code to show what I mean. First, the C# declarations:
这是一些代码来显示我的意思。首先,C#声明:
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar
{
public string Name { get; set; }
}
Next, my Window1 XAML:
接下来,我的 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>
And lastly, my Window1 class:
最后,我的 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();
}
}
With me? I have a ComboBox whose items are bound to a list of Bar instances, one of which is null. I have bound the window to an instance of Foo, and the ComboBox is displaying the value of its Bar property.
与我一起?我有一个 ComboBox,它的项目绑定到一个 Bar 实例列表,其中一个为空。我已将窗口绑定到 Foo 实例,并且 ComboBox 显示其 Bar 属性的值。
When I run this app, the ComboBox starts with an empty display because Foo.Bar is null by default. That's fine. If I use the mouse to drop the ComboBox down and select the "Hello" item, that works too. But then if I try to re-select the empty item at the top of the list, the ComboBox closes and returns to its previous value of "Hello"!
当我运行此应用程序时,ComboBox 以空显示开始,因为 Foo.Bar 默认为空。没关系。如果我使用鼠标将 ComboBox 放下并选择“Hello”项目,那也可以。但是,如果我尝试重新选择列表顶部的空项目,ComboBox 将关闭并返回其先前的值“Hello”!
Selecting the null value with the arrow keys works as expected, and setting it programatically works too. It's only selecting with a mouse that doesn't work.
使用箭头键选择空值可以按预期工作,并且以编程方式设置它也可以。它只是用鼠标选择不起作用。
I know an easy workaround is to have an instance of Bar that represents null and run it through an IValueConverter, but can someone explain why selecting null with the mouse doesn't work in WPF's ComboBox?
我知道一个简单的解决方法是让 Bar 的实例代表 null 并通过 IValueConverter 运行它,但是有人可以解释为什么在 WPF 的 ComboBox 中用鼠标选择 null 不起作用吗?
采纳答案by Tim Erickson
The null "item" is not being selected by the keyboard at all - rather the previous item is being unselected and no subsequent item is (able to be) selected.This is why, after "selecting" the null item with the keyboard, you are thereafter unable to re-select the previously selected item ("Hello") - except via the mouse!
键盘根本没有选择空的“项目”——而是取消选择前一个项目,并且没有(能够)选择后续项目。这就是为什么在用键盘“选择”空项目后,您无法重新选择先前选择的项目(“Hello”) - 除了通过鼠标!
In short, you can neither select nor deselect a null item in a ComboBox. When you think you are doing so, you are rather deselecting or selecting the previous or a new item.
简而言之,您既不能选择也不能取消选择 ComboBox 中的空项目。当您认为这样做时,您实际上是在取消选择或选择上一个或新项目。
This can perhaps best be seen by adding a background to the items in the ComboBox. You will notice the colored background in the ComboBox when you select "Hello", but when you deselect it via the keyboard, the background color disappears. We know this is not the null item, because the null item actually has the background color when we drop the list down via the mouse!
这可能最好通过为 ComboBox 中的项目添加背景来体现。当您选择“Hello”时,您会注意到 ComboBox 中的彩色背景,但是当您通过键盘取消选择它时,背景颜色消失。我们知道这不是空项,因为当我们通过鼠标下拉列表时,空项实际上具有背景颜色!
The following XAML, modified from that in the original question, will put a LightBlue background behind the items so you can see this behavior.
从原始问题中修改而来的以下 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>
If you want further validation, you can handle the SelectionChanged event on the ComboBox and see that "selecting the null item" actually gives an empty array of AddedItems in its SelectionChangedEventArgs, and "deselecting the null item by selecting 'Hello' with the mouse" gives an empty array of RemovedItems.
如果您想进一步验证,您可以处理 ComboBox 上的 SelectionChanged 事件,并看到“选择空项”实际上在它的 SelectionChangedEventArgs 中给出了一个空的 AddItems 数组,并“通过用鼠标选择“Hello”来取消选择空项”给出一个 RemovedItems 的空数组。
回答by Andrew Mikhailov
Well I recently ran into the same problem with nullvalue for ComboBox. I've solved it by using two converters:
好吧,我最近遇到了与ComboBox 的null值相同的问题。我已经通过使用两个转换器解决了这个问题:
For ItemsSourceproperty: it replaces nullvalues in the collection by any value passed inside converter's parameter:
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(); } }
For SelectedValueproperty: this one does the same but for the single value and in two ways:
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; } }
对于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(); } }
对于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; } }
Example of use:
使用示例:
<ComboBox
ItemsSource="{Binding MyValues, Converter={StaticResource EnumerableNullReplaceConverter}, ConverterParameter='(Empty)'}"
SelectedValue="{Binding SelectedMyValue, Converter={StaticResource NullReplaceConverter}, ConverterParameter='(Empty)'}"
/>
Result:
结果:
Note: If you bind to ObservableCollectionthen you will lose change notifications. Also you don't want to have more than one nullvalue in the collection.
注意:如果您绑定到ObservableCollection,那么您将丢失更改通知。此外,您不希望集合中有多个空值。
回答by Anurag
I got a new solution for this question. "USING Mahapps"
我得到了这个问题的新解决方案。“使用 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}"/>
You can use the close button to clear the content.
您可以使用关闭按钮清除内容。
Thanks.
谢谢。
回答by Galghamon
I know this answer isn't what you asked for (an explanation of why it doesn't work with the mouse), but I think the premise is flawed:
我知道这个答案不是您所要求的(解释为什么它不适用于鼠标),但我认为前提是有缺陷的:
From my perspective as a programmer and user (not .NET), selecting a null value is a bad thing. "null" is supposed to be the absence of a value, not something you select.
从我作为程序员和用户(不是 .NET)的角度来看,选择空值是一件坏事。“null”应该是缺少值,而不是您选择的值。
If you need the ability explicitly not to select something, I would suggest either the work-around you mentioned ("-", "n.a." or "none" as a value), or better
如果您需要明确不选择某些内容的能力,我建议您使用您提到的解决方法(“-”、“na”或“none”作为值),或者更好
- wrap the combobox with a checkbox that can be unchecked to disable the combobox. This strikes me as the cleanest design both from a user's perspective and programmatically.
- 用复选框包裹组合框,可以取消选中该复选框以禁用组合框。从用户的角度和程序化的角度来看,这让我觉得这是最干净的设计。
回答by John
I spent one day to find a solution about this problem of selecting a null value in combobox and finally, yeah finally I found a solution in an article written at this url:
我花了一天的时间来找到有关在组合框中选择空值的问题的解决方案,最后,是的,我终于在此 url 上写的一篇文章中找到了解决方案:
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" />
回答by Ric Tokyo
this might not address your answer completely, but hopefully its a hit in the right direction:
这可能无法完全解决您的答案,但希望它朝着正确的方向发展:
- Have you installed SP1?
- 你安装SP1了吗?
- NET 3.5 SP1 includes several data binding and editing improvements to
WPF. These include:- StringFormat support within {{ Binding }} expressions to enable easy formatting of bound values
- New alternating rows support within controls derived from ItemsControl, which makes it easier to set alternating properties on rows (for example: alternating background colors)
- Better handling and conversion support for null valuesin editable controls Item-level validation that applies validation rules to an entire bound item
- MultiSelector support to handle multi-selection and bulk editing scenarios
- IEditableCollectionView support to interface data controls to data sources and enable editing/adding/removing items in a transactional way
- Performance improvements when binding to IEnumerable data sources
- NET 3.5 SP1 包括对
WPF 的多项数据绑定和编辑改进。这些包括:- {{ Binding }} 表达式中的 StringFormat 支持可轻松设置绑定值的格式
- 从 ItemsControl 派生的控件中支持新的交替行,这使得在行上设置交替属性变得更加容易(例如:交替背景颜色)
- 对可编辑控件中的空值更好的处理和转换支持项目级验证,将验证规则应用于整个绑定项目
- MultiSelector 支持处理多选和批量编辑场景
- IEditableCollectionView 支持将数据控件连接到数据源,并支持以事务方式编辑/添加/删除项目
- 绑定到 IEnumerable 数据源时的性能改进
Sorry if I wasted your time and this was not even close..but I think the problem is inherited from:
对不起,如果我浪费了你的时间,这甚至不是很接近......但我认为这个问题是从以下继承而来的:
constraints of the strongly typed dataset
NullValueDataSet Explained here
But now the SP1 for .Net 3.5 should have addressed this issue..
但是现在 .Net 3.5 的 SP1 应该已经解决了这个问题。
回答by rudigrobler
Try Binding.FallbackValue
尝试 Binding.FallbackValue
From 6 Things I Bet You Didn't Know About Data Binding in WPF
回答by Dincer Uyav
I had the same kind of problem we did some work around like adding a value property to the collection item like this :
我遇到了同样的问题,我们做了一些工作,例如向集合项添加 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
}
}
Then while adding items instead of null I use the same object :
然后在添加项目而不是 null 我使用相同的对象:
comboBox1.ItemsSource= new ObservableCollection<Bar>
{
new Bar(),
new Bar { Name = "Hello" },
new Bar { Name = "World" }
};
And instead of selecteditem I bind it to selectedvalue :
而不是 selecteditem 我将它绑定到 selectedvalue :
<ComboBox Height="23" Margin="25,40,133,0" DisplayMemberPath="Name"
SelectedValuePath="Value"
SelectedValue="{Binding Bar}"
Name="comboBox1" VerticalAlignment="Top" />
I know It is not a complete solution, just one workaround I use
我知道这不是一个完整的解决方案,只是我使用的一种解决方法
回答by redHymanwong
ComboBox needs a DataTemplate to display the item no matter how simple it is. DataTemplate works like this: get a value from instance.[path], e.g.
ComboBox 需要一个 DataTemplate 来显示项目,无论它多么简单。DataTemplate 的工作方式如下:从 instance.[path] 获取值,例如
bar1.Car.Color
So it cannot get a value from
所以它不能从
null.Car.Color
It will throw a null reference exception. So, the null instance will not be displayed. But the the Color - if it is a reference type - is allowed to be null because there will be no exception in this case.
它将抛出一个空引用异常。因此,不会显示空实例。但是 Color - 如果它是引用类型 - 允许为空,因为在这种情况下不会有例外。
回答by redHymanwong
Just a guess, but I think it sounds reasonable.
只是猜测,但我认为这听起来很合理。
Assume combobox is using "ListCollectionView" (lcv as its instance) as its item collection, which it should be. If you are a programmer, what you gonna do?
假设组合框使用“ListCollectionView”(lcv 作为它的实例)作为它的项目集合,它应该是。如果你是程序员,你会做什么?
I will respons to both Keyboard and Mouse.
我将回应键盘和鼠标。
Once I get Keyboard input, I use
一旦我获得键盘输入,我就使用
lcv.MoveCurrentToNext();
or
或者
lcv.MoveCurrentToPrevious();
So, sure keyboard works well.
所以,确保键盘工作正常。
Then I am working on respons Mouse inputs. And it comes the problem.
然后我正在处理鼠标输入。问题来了。
I want to listen 'MouseClick' event of my item. But probably, my Item doesn't generated, it is just a placeholder. So when user click on this placeholder, I get nothing.
If I get the event successfully, what's next. I will invoke
lcv.MoveCurrentTo(selectedItem);
我想听我的项目的 'MouseClick' 事件。但可能,我的 Item 没有生成,它只是一个占位符。所以当用户点击这个占位符时,我什么也得不到。
如果我成功举办了活动,接下来会发生什么。我会调用
lcv.MoveCurrentTo(selectedItem);
the "selectedItem" which would be null is not an acceptable parameter here I think.
我认为此处为 null 的“selectedItem”不是可接受的参数。
Anyway, it's just guessing. I don't have time to debug into it though I am able to. I have a bunch of defects to fix. Good Luck. :)
无论如何,这只是猜测。我没有时间调试它,尽管我有能力。我有一堆缺陷要修复。祝你好运。:)