【问题标题】:How to get property name from the sender object of an INotifyPropertyChanged PropertyChanged event如何从 INotifyPropertyChanged PropertyChanged 事件的发送者对象获取属性名称
【发布时间】:2010-11-02 16:18:39
【问题描述】:

我有一个基本的 DependencyObject 类,其中我有一个获取对象、获取属性的方法,并且对于作为实现 INotifyPropertyChanged 的​​类型的每个属性,我添加一个新的 PropertyChangedEventHandler。现在在处理程序方法中,它获取对象“sender”和 PropertyChangedEventArgs“e”的参数。我的问题是,如果 sender 是实现 INotifyPropertyChanged 的​​类型的属性,是否有人知道如何动态获取属性名称。

这是我的工作:

public class BaseDependencyObject : DependencyObject, INotifyPropertyChanged
{
    public BaseDependencyObject()
    {

    }

    protected void SetValues(Object thisObject, Object entity)
    {
        try
        {
            PropertyInfo[] properties = entity.GetType().GetProperties();
            foreach (PropertyInfo property in properties)
            {
                var value = property.GetValue(entity, null);
                var valueIsEntity = value is System.ServiceModel.DomainServices.Client.Entity;
                var thisObjectsProperty = thisObject.GetType().GetProperty(property.Name);

                if (thisObjectsProperty != null && value != null)
                {
                    if (valueIsEntity)
                    {
                        if (thisObjectsProperty.PropertyType.GetInterface("INotifyPropertyChanged", true) != null)
                        {
                            var propertyInstance = Activator.CreateInstance(thisObjectsProperty.PropertyType);

                            ((INotifyPropertyChanged)propertyInstance).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
                        }

                        SetValues(thisObjectsProperty, value);
                    }

                    else if (thisObjectsProperty.PropertyType.GetInterface("ICollection", true) != null
                        && thisObjectsProperty.PropertyType.GetGenericArguments().Count() > 0)
                    {
                        Type genericType = thisObjectsProperty.PropertyType.GetGenericArguments()[0];

                        var observableCollection = Activator.CreateInstance(thisObjectsProperty.PropertyType) as IList;

                        if (observableCollection is INotifyCollectionChanged)
                            ((INotifyCollectionChanged)observableCollection).CollectionChanged += this.Object_CollectionChanged;

                        if (observableCollection is INotifyPropertyChanged)
                            ((INotifyPropertyChanged)observableCollection).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);

                        foreach (var item in (IEnumerable)value)
                        {
                            var newItem = Activator.CreateInstance(genericType);

                            if (newItem != null)
                            {
                                SetValues(newItem, item);
                                observableCollection.Add(newItem);
                            }
                        }
                    }

                    else
                    {
                        thisObjectsProperty.SetValue(thisObject, value, null);

                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    protected void Object_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var item in e.NewItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
                    }
                }
                break;

            case NotifyCollectionChangedAction.Remove:
                foreach (var item in e.OldItems)
                {
                    if (item is INotifyPropertyChanged)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged -= this.Object_PropertyChanged;
                    }
                }
                break;
        }

    }

    protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.NotifyPropertyChanged(e.PropertyName);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;

        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));

    }
}

SetValues 方法的第一个参数是将在视图模型中使用的 DependencyObject 类型。第二个参数是从 DomainService 的 Context.LoadOperation 返回的实体。

我的问题归结为当 INotifyCollectionChanged.CollectionChanged 触发时,我需要能够使用集合的属性名称引发 PropertyChanged 事件。因此,如果有人有任何建议,我将不胜感激。提前致谢。

编辑
弄清楚如何获取触发事件的属性名称。这是我的 PropertyChangedEventHandler 的编辑版本。

protected void Object_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var properties = this.GetType().GetProperties().Where(x => x.PropertyType == sender.GetType()).ToArray();

        foreach (var property in properties)
        {
            this.NotifyPropertyChanged(property.Name);
        }

        //this.NotifyPropertyChanged(e.PropertyName);
    }

基本上这可以满足我的要求,但显然我仍然没有做正确的事情。当添加另一个类型的属性 ObservableCollection 时,UIElement 仍未更新。

这是我的 DependencyObjects 和 ViewModel 的示例:

public class LOB : DependencyObject
{
    public Int32 ID
    {
        get { return (Int32)GetValue(IDProperty); }
        set
        {
            SetValue(IDProperty, value);
            NotifyPropertyChanged("ID");
        }
    }

    public static readonly DependencyProperty IDProperty =
    DependencyProperty.Register("ID", typeof(Int32), typeof(LOB), null);

    public ObservableCollection<Group> Groups
    {
        get { return (ObservableCollection<Group>)GetValue(GroupsProperty); }
        set
        {
            SetValue(GroupsProperty, value);
            NotifyPropertyChanged("Groups");
        }
    }

    public static readonly DependencyProperty GroupsProperty =
    DependencyProperty.Register("Groups", typeof(ObservableCollection<Group>), typeof(LOB), new PropertyMetadata(null, OnGroupsPropertyChanged));

    static void OnGroupsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            ((INotifyCollectionChanged)e.NewValue).CollectionChanged += new NotifyCollectionChangedEventHandler(((LOB)obj).Object_CollectionChanged);
            ((INotifyPropertyChanged)e.NewValue).PropertyChanged += new PropertyChangedEventHandler(((LOB)obj).Object_PropertyChanged);
        }
        if (e.OldValue != null)
        {
            ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= ((LOB)obj).Object_CollectionChanged;
            ((INotifyPropertyChanged)e.OldValue).PropertyChanged -= ((LOB)obj).Object_PropertyChanged;
        }
    }

}

public class Group : DependencyObject
{
    public Int32 ID
    {
        get { return (Int32)GetValue(IDProperty); }
        set
        {
            SetValue(IDProperty, value);
            NotifyPropertyChanged("ID");
        }
    }

    public static readonly DependencyProperty IDProperty =
    DependencyProperty.Register("ID", typeof(Int32), typeof(Group), null);

    public String GroupName
    {
        get { return (String)GetValue(GroupNameProperty); }
        set
        {
            SetValue(GroupNameProperty, value);
            NotifyPropertyChanged("GroupName");
        }
    }

    public static readonly DependencyProperty GroupNameProperty =
    DependencyProperty.Register("GroupName", typeof(String), typeof(Group), null);

}

public class MyViewModel : DependencyObject
{
    public static readonly DependencyProperty LobCollectionProperty =
        DependencyProperty.Register("LobCollection",
            typeof(ObservableCollection<LOB>),
            typeof(MyViewModel),
            new PropertyMetadata(null, LobCollectionPropertyChanged));

    public ObservableCollection<LOB> LobCollection
    {
        get { return (ObservableCollection<MainBusinessLine>)GetValue(LobCollectionPropertyChanged); }
        set
        {
            SetValue(MainBusinessLineCollectionProperty, value);
            NotifyPropertyChanged("LobCollection");
        }
    }

    static void LobCollectionPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var viewModel = obj as MyViewModel;

        if (viewModel == null)
            return;

        if (e.OldValue != null)
        {
            ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= viewModel.LobCollection_Changed;
        }

        if (e.NewValue != null)
        {
            ((INotifyCollectionChanged)e.NewValue).CollectionChanged += viewModel.LobCollection_Changed;
        }

    }

    void LobCollection_Changed(object sender, NotifyCollectionChangedEventArgs e)
    {
        NotifyPropertyChanged("LobCollection");
    }
}  

【问题讨论】:

  • 让我确定我正确理解了你的问题:你有这个类:“public class Foo : INotifyPropertyChanged { INotifyCollectionChanged collection; }”,当 CollectionChanged 事件触发时,你想要 PropertyChanged 事件向拥有的班级开火。对吗?
  • 是的,你是对的。另外,我编辑了我的原始帖子以显示一个更新的版本,它做了一些事情。当然这不是最好的解决方案,但目前我只想能够在另一个可观察集合中添加到可观察集合。
  • 您在 LOB 和 Group 课程中所做的事情对我来说是最有意义的。在基类中处理它当然是可能的,但是混合使用 DependancyProperty 和 INotifyPropertyChanged 确实会使它变得更难。另外,请注意,从技术上讲,我认为您没有遵循 INotifyPropertyChanged 的​​合同:属性的值没有改变,它里面的集合有。执行此操作的标准方法是让 CollectionChanged 事件触发,然后将其保留。
  • 首先我想我现在应该补充一点,当我对 xaml 进行一些更改时,我的 UIElement 会更新并正确显示新项目。所以这就提出了一个问题,“我现在是否通过使用 DependencyProperty 和 INotifyPropertyChanged 对其进行了过度设计???”根据你上面的陈述,我猜我做到了。那么,您认为更好的做法是 DP 还是 INotify?而且您还说将其保留在 CollectionChanged 事件中。我不需要为该集合属性引发 PropertyChanged 事件吗?感谢大卫的回复。撞到墙上我的头越来越疼。
  • 我对 DependancyObject 和 INotifyPropertyChanged 的​​一般理念是使用其中一个。如果有必要在同一个类上同时使用两者,我不会为 DependancyProperty 触发 PropertyChanged 事件:无论您绑定到该属性已经收到来自 DP 的更改通知,PropertyChanged 都是不必要的。

标签: c# inotifypropertychanged dependencyobject inotifycollectionchanged


【解决方案1】:

在我们上面的谈话之后,这相当没有意义,但我考虑了如何实现一个基类,当子类定义的属性中的集合发生更改时,该基类会触发 PropertyChanged 事件。正如我所说,这有点不标准,但这是我的做法。

class FancyCollectionAndPropertyChangedBase : INotifyPropertyChanged
{
    private Dictionary<ICollectionChanged, String> collectionNameLookup = new Dictionary<ICollectionChanged, String>();

    protected FancyCollectionAndPropertyChangedBase()
    {
        this.PropertyChanged += MyPropertyChanged;
    }

    private void MyPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if(this.collectionNameLookup.ContainsValue(e.PropertyName)
        {
            KeyValuePair<INotifyCollectionChanged, String> oldValue = this.collectionNameLookup.First(kvp => kvp.Value == e.Name);
            oldValue.Key -= MyCollectionChanged;
            this.collecitonNameLookup.Remove(oldValue.Key);

            INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
            collection.CollectionChanged += MyCollectionChanged;
            this.collectionNameLookup.Add(collection, e.Name);
        }
        else if(typeof(INotifyCollectionChanged).IsAssignableFrom(this.GetType().GetProperty(e.PropertyName,  BindingFlags.FlattenHierarchy).PropertyType))
        {
            // Note: I may have gotten the IsAssignableFrom statement, above, backwards. 
            INotifyCollectionChanged collection = this.GetType().GetProperty(e.PropertyName, BindingFlags.FlattenHierarchy).GetValue(this, null);
            collection.CollectionChanged += MyCollectionChanged;
            this.collectionNameLookup.Add(collection, e.Name);
        }
    }

    private void MyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        this.NotifyPropertyChanged(this.collectionNameLookup[sender];
    }
}

【讨论】:

  • David,我只是想让您知道我使用了您在上面发布的代码的基础并将其实现到我的 Base 类中。现在一切都很顺利。我非常感谢您抽出时间来帮助我解决这个问题。我会将此标记为答案,因为一切都被证明是诀窍。另外,我从 DependencyObject 中删除了继承,只实现了 INotify 接口。如果将来有人需要查看完成的代码,我很乐意发布它。再次感谢。
猜你喜欢
  • 2010-12-08
  • 2022-08-23
  • 2011-11-18
  • 2019-08-13
  • 1970-01-01
  • 2013-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多