我正在尝试制作一个在对象之间切换的ComboBox。一般要点是一个对象有一个出现在ComboBox中的Key和一个理论上可以是任何东西的Data组件。数据组件很复杂,而Key只是一个字符串。对于下面的例子,Data只是一个Uri,它实际上证明数据的类型并不重要。
基本意图是将ComboBox的SelectedItem绑定到Model,以便可以通过其他交互修改SelectedItem的Data。
设置代码,以便将几个项添加到ComboBox,然后选择SelectedItem作为第一个元素。这很好用。当我单击按钮时,SelectedItem被指定为null,我抛出异常。
为什么SelectedItem被赋值为null?
这是完整的工作代码;我的目标是.NET 4.0,但我猜它并不重要。 Xaml如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Sandbox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Model Model { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Model = new Model();
this.Model.Items.Add(
new ObservableKeyValuePair<string, Uri>()
{
Key = "Apple",
Value = new Uri("http://apple.com")
});
this.Model.Items.Add(
new ObservableKeyValuePair<string, Uri>()
{
Key = "Banana",
Value = new Uri("http://Banana.net")
});
this.Model.SelectedItem = this.Model.Items.First();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Model.SelectedItem.Value = new Uri("http://cranberry.com");
}
}
public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public TrulyObservableCollection()
{
CollectionChanged += FullObservableCollectionCollectionChanged;
}
public TrulyObservableCollection(IEnumerable<T> pItems)
: this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
public class ObservableKeyValuePair<TKey, TValue> :
INotifyPropertyChanged,
IEquatable<ObservableKeyValuePair<TKey, TValue>>
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
public override bool Equals(object rhs)
{
var obj = rhs as ObservableKeyValuePair<TKey, TValue>;
if (obj != null)
{
return this.Key.Equals(obj.Key);
}
return false;
}
public bool Equals(ObservableKeyValuePair<TKey, TValue> other)
{
return this.Key.Equals(other.Key);
}
public override int GetHashCode()
{
return this.Key.GetHashCode();
}
protected TKey _Key;
public TKey Key
{
get
{
return _Key;
}
set
{
if (value is INotifyPropertyChanged)
{
(value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(KeyChanged);
}
_Key = value;
OnPropertyChanged("Key");
}
}
void KeyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Key");
}
protected TValue _Value;
public TValue Value
{
get
{
return _Value;
}
set
{
if (value is INotifyPropertyChanged)
{
(value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(ValueChanged);
}
_Value = value;
OnPropertyChanged("Value");
}
}
void ValueChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Value");
}
}
public class Model : INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public Model()
{
Items = new TrulyObservableCollection<ObservableKeyValuePair<string, Uri>>();
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
public TrulyObservableCollection<ObservableKeyValuePair<string, Uri>> Items { get; set; }
public ObservableKeyValuePair<string, Uri> _SelectedItem = null;
public ObservableKeyValuePair<string, Uri> SelectedItem
{
get
{
return Items.FirstOrDefault(x => _SelectedItem != null && x.Key == _SelectedItem.Key);
}
set
{
if (value == null)
{
throw new Exception("This is the problem");
}
if (_SelectedItem != value)
{
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
}
}
的Xaml:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Click="Button_Click" Content="Clsick Me"/>
<ComboBox IsEditable="False" ItemsSource="{Binding Model.Items}" SelectedItem="{Binding Model.SelectedItem, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}">
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
我总是试图解释为什么“价值”为空。
答案 0 :(得分:0)
该值变为null,因为您尝试分配不在集合
中的选定项目值答案 1 :(得分:0)
根本问题在于Selector.OnItemsChanged
的实施。接近方法的末尾,当前SelectedItem
被取消。
我通过派生新的ComboBox
类来覆盖OnItemsChanged
,保存当前SelectedItem
,调用base.OnItemsChanged
然后重置SelectedItem
,从而解决了这个问题。如果不希望有效的SelectedItem
从valid =&gt; null =&gt;有效转换,则可能需要将“InhibitEvents”标记传播到模型中。