【问题标题】:BindingList<T> INotifyPropertyChanged unexpected behaviorBindingList<T> INotifyPropertyChanged 意外行为
【发布时间】:2013-06-07 00:26:57
【问题描述】:

假设,我有对象:

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

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

【问题讨论】:

  • 你是怎么做测试的?如果要触发属性更改事件,应先订阅该事件。如果您有 GUI,这意味着您应该将 Data 属性绑定到指定的控件,例如文本框。
  • @JasonLi 这个想法是 BindingList 应该订阅该事件,但似乎没有

标签: c# inotifypropertychanged bindinglist


【解决方案1】:

BindingList&lt;T&gt; 不检查每个特定项目是否实现INotifyPropertyChanged。相反,它会检查一次通用类型参数。因此,如果您的BindingList&lt;T&gt; 声明如下:

private BindingList<ITest> _listTest1;

那么ITest 应该继承自INotifyPropertyChanged 以便获得BindingList raise ItemChanged 事件。

【讨论】:

  • +1 很棒的收获 - 没有发现(在我的代码完整性检查中,我声明了 BindingList&lt;Test1&gt;
【解决方案2】:

我认为我们可能无法从您的代码中获得完整的图片,因为如果我采用 ITest 接口和 Test1 类逐字(edit哎呀 - 不完全是 - 因为,作为 Nikolay说,这对你来说是失败的,因为你使用 ITest 作为你的代码中BindingList&lt;T&gt; 的泛型类型参数,我不在这里)并编写这个测试:

[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);

  }
}

测试正确通过 - evtCount2 结束,应该是这样。

【讨论】:

    【解决方案3】:

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

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

    感谢回复!

    【讨论】:

      【解决方案4】:

      您参数化 BindingList&lt;&gt; 的元素类型(在您的情况下为 ITest)必须从 INotifyPropertyChanged 继承。 选项:

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

      【讨论】:

        猜你喜欢
        • 2021-05-03
        • 2012-10-28
        • 2018-11-27
        • 1970-01-01
        • 2015-06-22
        • 2011-09-09
        • 1970-01-01
        • 2011-02-17
        • 1970-01-01
        相关资源
        最近更新 更多