假设我有对象:
public interface ITest
{
string Data { get; set; }
}
public class Test1 : ITest, INotifyPropertyChanged
{
private string _data;
public string Data
{
get { return _data; }
set
{
if (_data == value) return;
_data = value;
OnPropertyChanged("Data");
}
}
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged;
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
及其持有人:
private BindingList<ITest> _listTest1;
public BindingList<ITest> ListTest1 { get { return _listTest1 ?? (_listTest1 = new BindingList<ITest>() { RaiseListChangedEvents = true }); }
}
另外,我订阅了ListChangedEvent
public MainWindow()
{
InitializeComponent();
ListTest1.ListChanged += new ListChangedEventHandler(ListTest1_ListChanged);
}
void ListTest1_ListChanged(object sender, ListChangedEventArgs e)
{
MessageBox.Show("ListChanged1: " + e.ListChangedType);
}
2个测试处理程序: 用于添加对象
private void AddITestHandler(object sender, RoutedEventArgs e)
{
ListTest1.Add(new Test1 { Data = Guid.NewGuid().ToString() });
}
和更改
private void ChangeITestHandler(object sender, RoutedEventArgs e)
{
if (ListTest1.Count == 0) return;
ListTest1[0].Data = Guid.NewGuid().ToString();
//if (ListTest1[0] is INotifyPropertyChanged)
// MessageBox.Show("really pch");
}
发生了ItemAdded,但没有ItemChanged。在内部预订“数据”中,我发现我的事件PropertyChanged没有订阅者:
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged; // h is null! why??
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
深入挖掘我采用反射器并发现BindingList:
protected override void InsertItem(int index, T item)
{
this.EndNew(this.addNewPos);
base.InsertItem(index, item);
if (this.raiseItemChangedEvents)
{
this.HookPropertyChanged(item);
}
this.FireListChanged(ListChangedType.ItemAdded, index);
}
private void HookPropertyChanged(T item)
{
INotifyPropertyChanged changed = item as INotifyPropertyChanged;
if (changed != null) // Its seems like null reference! really??
{
if (this.propertyChangedEventHandler == null)
{
this.propertyChangedEventHandler = new PropertyChangedEventHandler(this.Child_PropertyChanged);
}
changed.PropertyChanged += this.propertyChangedEventHandler;
}
}
我哪里错了?或者这是已知的bug,我需要找到一些解决方法? 谢谢!
答案 0 :(得分:5)
BindingList<T>
不会检查每个特定项目是否实现INotifyPropertyChanged
。相反,它会检查一次通用类型参数。因此,如果您的BindingList<T>
声明如下:
private BindingList<ITest> _listTest1;
然后,ITest
应继承INotifyPropertyChanged
,以便BindingList
举起ItemChanged
个事件。
答案 1 :(得分:1)
我想我们可能没有从您的代码中获得完整的图片,因为如果我采用ITest
接口和Test1
类逐字(编辑哎呀 - 不完全 - 因为,正如Nikolay所说,你的代码中使用ITest作为BindingList<T>
的通用类型参数(我不在这里)并且写下这个测试,这对你来说是失败的:
[TestClass]
public class UnitTest1
{
int counter = 0;
[TestMethod]
public void TestMethod1()
{
BindingList<Test1> list = new BindingList<Test1>();
list.RaiseListChangedEvents = true;
int evtCount = 0;
list.ListChanged += (object sender, ListChangedEventArgs e) =>
{
Console.WriteLine("Changed, type: {0}", e.ListChangedType);
++evtCount;
};
list.Add(new Test1() { Data = "yo yo" });
Assert.AreEqual(1, evtCount);
list[0].Data = "ya ya";
Assert.AreEqual(2, evtCount);
}
}
测试正确传递 - evtCount
最终为2
,应该是这样。
答案 2 :(得分:1)
使用(BindingList<>
在您的情况下)参数化ITest
的元素类型必须从INotifyPropertyChanged继承。 选项:
答案 3 :(得分:0)
我在构造函数中找到了一些有趣的东西:
public BindingList()
{
// ...
this.Initialize();
}
private void Initialize()
{
this.allowNew = this.ItemTypeHasDefaultConstructor;
if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T))) // yes! all you're right
{
this.raiseItemChangedEvents = true;
foreach (T local in base.Items)
{
this.HookPropertyChanged(local);
}
}
}
快速修复4此行为:
public class BindingListFixed<T> : BindingList<T>
{
[NonSerialized]
private readonly bool _fix;
public BindingListFixed()
{
_fix = !typeof (INotifyPropertyChanged).IsAssignableFrom(typeof (T));
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (RaiseListChangedEvents && _fix)
{
var c = item as INotifyPropertyChanged;
if (null!=c)
c.PropertyChanged += FixPropertyChanged;
}
}
protected override void RemoveItem(int index)
{
var item = base[index] as INotifyPropertyChanged;
base.RemoveItem(index);
if (RaiseListChangedEvents && _fix && null!=item)
{
item.PropertyChanged -= FixPropertyChanged;
}
}
void FixPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!RaiseListChangedEvents) return;
if (_itemTypeProperties == null)
{
_itemTypeProperties = TypeDescriptor.GetProperties(typeof(T));
}
var propDesc = _itemTypeProperties.Find(e.PropertyName, true);
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOf((T)sender), propDesc));
}
[NonSerialized]
private PropertyDescriptorCollection _itemTypeProperties;
}
感谢您的回复!