BindingList< T>inotifyproperty改变了意外行为



假设我有对象:

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没有出现。在查看属性"Data"时,我发现事件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;

深入挖掘,我使用了reflector并发现了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;
        }
    }

我错在哪里?或者这是已知的错误,我需要找到一些解决方案?谢谢!

BindingList<T>不检查每个特定项目是否实现了INotifyPropertyChanged。相反,它只检查一次泛型类型参数。因此,如果您的BindingList<T>声明如下:

private BindingList<ITest> _listTest1;

ITest应从INotifyPropertyChanged继承,以获得BindingList引发ItemChanged事件。

我想我们可能没有完整的图片从你的代码在这里,因为如果我采取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,正如它应该的那样。

我在构造函数中发现了一些有趣的东西:

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

谢谢你的回复!

参数化BindingList<>的元素类型(在您的例子中是ITest)必须从INotifyPropertyChanged继承。选项:

  1. 更改您的继承树test: INotifyPropertyChanged
  2. 将具体类传递给泛型BindingList

最新更新