WPF:带重置项的ComboBox

我想在WPF中创建一个顶部有一个null项的ComboBox,当选中此项时,SelectedItem应设置为null(重置为默认状态)。 我一直在搜索,但没有找到令人满意的解决方案。

如果可能的话,我希望它只使用XAML代码或附加行为来实现,因为我不喜欢在ViewModel中为View更改内容,或者重写标准控件。

这是我到目前为止提出的(缩短代码):

 [...]     (None)      [...] 

OpenCombo

我认为最好的方法是以某种方式添加一个事件触发器,当项目被选中时将SelectedIndex设置为-1 ,但这里是我遇到的问题。

任何想法如何做到这一点? 或者更好的方式,如附加行为?

考虑为“无”combobox项目实现Null对象模式 ,并将此项目添加到项目列表中。 然后实现用于在该类中保存null对象的自定义逻辑,或者仅检查所选项是否为NullItem类型。

我使用以下解决方案来解决类似的问题。 它利用绑定的Converter属性在内部表示(null是一个合理的值)和我想要出现在ComboBox中之间来回传递。 我喜欢没有必要在模型或视图模型中添加显式列表,但我不喜欢转换器中的字符串文字与ComboBox中的字符串文字之间的脆弱连接。

    (none)     

然后转换器看起来像:

 public class MyPropertySelectionConverter : IValueConverter { public static MyPropertySelectionConverter Instance { get { return s_Instance; } } public const String NoneString = "(none)"; public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Object retval = value as MyPropertyType; if (retval == null) { retval = NoneString; } return retval; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Object retval = null; if (value is MyPropertyType) { retval = value; } else if (String.Equals(NoneString, value as String, StringComparison.OrdinalIgnoreCase)) { retval = null; } else { retval = DependencyProperty.UnsetValue; } return retval; } private static MyPropertySelectionConverter s_Instance = new MyPropertySelectionConverter(); } 

如果选择项目,则可以重置选择。

                First Item Second Item   

不幸的是,这不适用于ItemsSourceCompositeCollection将此重置项添加到任意列表。 原因是WPF无法解析此范围内的Storyboard.TargetName 。 但也许这可以帮助您继续重新模拟ComboBox

虽然我同意WPF ComboBox空项目问题有很多解决方案,但Andrei Zubov对Null对象模式的引用激发了我尝试一种不太过分的替代方案,其中包括在注入之前用值(也包裹)包装每个源项目allow整个包装集合到ComboBox.ItemsSource属性中。 所选项目将可用于SelectedWrappedItem属性。

所以,首先你定义你的通用Wrapper ……

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ComboBoxWrapperSample { ///  /// Wrapper that adds supports to null values upon ComboBox.ItemsSource ///  /// Source combobox items collection datatype public class ComboBoxNullableItemWrapper { string _nullValueText; private T _value; public T Value { get { return _value; } set { _value = value; } } ///  /// ///  /// Source object /// Text to be presented whenever Value argument object is NULL public ComboBoxNullableItemWrapper(T Value, string NullValueText = "(none)") { this._value = Value; this._nullValueText = NullValueText; } ///  /// Text that will be shown on combobox items ///  ///  public override string ToString() { string result; if (this._value == null) result = _nullValueText; else result = _value.ToString(); return result; } } } 

定义您的商品模型……

 using System.ComponentModel; namespace ComboBoxWrapperSample { public class Person : INotifyPropertyChanged { // Declare the event public event PropertyChangedEventHandler PropertyChanged; public Person() { } // Name property private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } } // Age property private int _age; public int Age { get { return _age; } set { _age = value; OnPropertyChanged("Age"); } } protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } // Don't forget this override, since it's what defines ao each combo item is shown public override string ToString() { return string.Format("{0} (age {1})", Name, Age); } } } 

定义ViewModel ……

 using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; namespace ComboBoxWrapperSample { public partial class SampleViewModel : INotifyPropertyChanged { // SelectedWrappedItem- This property stores selected wrapped item public ComboBoxNullableItemWrapper _SelectedWrappedItem { get; set; } public ComboBoxNullableItemWrapper SelectedWrappedItem { get { return _SelectedWrappedItem; } set { _SelectedWrappedItem = value; OnPropertyChanged("SelectedWrappedItem"); } } // ListOfPersons - Collection to be injected into ComboBox.ItemsSource property public ObservableCollection> ListOfPersons { get; set; } public SampleViewModel() { // Setup a regular items collection var person1 = new Person() { Name = "Foo", Age = 31 }; var person2 = new Person() { Name = "Bar", Age = 42 }; List RegularList = new List(); RegularList.Add(person1); RegularList.Add(person2); // Convert regular collection into a wrapped collection ListOfPersons = new ObservableCollection>(); ListOfPersons.Add(new ComboBoxNullableItemWrapper(null)); RegularList.ForEach(x => ListOfPersons.Add(new ComboBoxNullableItemWrapper(x))); // Set UserSelectedItem so it targes null item this.SelectedWrappedItem = ListOfPersons.Single(x => x.Value ==null); } // INotifyPropertyChanged related stuff public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } } } 

并且,最终你的视图(好吧,它是一个窗口)

   Favorite teacher    Selected wrapped value:     

达到这一点,我是否提到您可以通过SelectedWrappedItem.Value属性检索未包装的选定项目?

在这里你可以得到一个工作样本

希望它可以帮助别人

这是这个问题的终极超简单解决方案:

您可以使用DbNull.Value作为项目或项目的value属性,而不是在ItemsSource中使用值为null的项目。

就这样。 你完成了。 没有值转换器,没有代码隐藏,没有xaml触发器,没有包装器,没有控制后代……

它简直有效!

这是绑定枚举值的简短示例,包括“null item”:

像这样创建ItemsSource:

  var enumValues = new ArrayList(Enum.GetValues(typeof(MyEnum))); enumValues.Insert(0, DBNull.Value); return enumValues; 

将它绑定到ComboBox的ItemsSource。

将ComboBox的SelectedValue绑定到任何具有MyEnum类型的属性? (即Nullable )。

完成!

背景:这种方法有效,因为DbNull.Value与C#null值不同,而另一方面,框架包含许多强制方法,可以在这两种方法之间进行转换。 最终,这类似于上面提到的“Null对象模式”,但不需要创建单独的null对象,也不需要任何值转换器。

删除以下行并添加CheckBox,然后您可以执行自定义操作。

  (None) 

仍然不是100%满意这个解决方案,但到目前为止我发现的最好的事情,你只需要覆盖ComboBox样式并应用AttachedBehaviour

  

来源: http : //xamlblog.com/PostPage.aspx?postId = 16#/Posts/16

请使用以下代码。

  (None) 

在viewmodel中,捕获“ClearSelectedItems”更改通知并清除ItemsControl的SelectedItems。