反映序列化对象以设置PropertyChanged事件

时间:2011-09-29 13:06:25

标签: c# wpf reflection inotifypropertychanged

我有一个通过反序列化一些XML创建的对象。我使用Visual Studio的工具从供应商模型XML生成XSD。然后使用XSD工具我从他们那里得到了类。我设置了XSD工具,以便使生成的类成为INotifyPropertyChanged。

现在我正在尝试在WPF应用程序的“选项卡”中显示此对象。每当有人做出改变时我都想要一个“脏”指示器。问题是这个对象,从生成的XSD生成的类不是最漂亮的结构。此对象的显示不会模仿其数据结构。我想过创建显示对象(不使用MMVM ATM)并使用它们来绑定我的更改,然后将这些更改作为数据对象保存到对象。我基本上只是抛弃我当前的显示来执行此操作,因为我现在只是添加一个检查是否有编辑。

我的想法是反映整个对象并为我遇到的每个属性设置PropertyChanged事件(遍历对象和属性的图形)。我的反思是失败了,我也可能犯了一些短视的错误。

这是我到目前为止编写的代码:

void SetupPropertyChanged(INotifyPropertyChanged component)
    {
        component.PropertyChanged += CAMConfig_PropertyChanged;

        Type componentType = component.GetType();

        foreach (PropertyInfo info in componentType.GetProperties())
        {
            Type[] types =
                info.PropertyType.FindInterfaces((a, b) => { return a.ToString() == b.ToString(); }, typeof(INotifyPropertyChanged));

            bool isINotify = types.Contains(typeof(INotifyPropertyChanged));


            if (isINotify)
                this.SetupPropertyChanged((INotifyPropertyChanged)info.GetValue(component, new object[] { }));
        }
    }

我想我正在遇到Observable集合属性类型问题,因为它在遍历我的对象时抛出异常。它也让我觉得我不知道这个对象结构是否会有循环引用。

有人可以帮我解决这个代码,以便我可以遍历对象图。现在我不太关心循环引用的可能性,但是如果一个解决方案能够阻止这种情况,那将会非常非常有用!

根据Karel的回答,我创建了这个“助手”类:

    public static class NotifyPropertyChangedHelper
{
    public delegate void ChangeOccuredHandler(object sender);

    public static void SetupPropertyChanged(INotifyPropertyChanged component, ChangeOccuredHandler changedHandler)
    {
        SetupPropertyChanged(new List<object>(), component, changedHandler);
    }

    static void SetupPropertyChanged(IList<object> closed, INotifyPropertyChanged component, ChangeOccuredHandler changedHandler)
    {
        if (closed.Contains(component)) return; // event was already registered

        closed.Add(component); //adds the property that is to be processed

        //sets the property changed event if the property isn't a collection
        if (!(component is INotifyCollectionChanged))
            component.PropertyChanged += (sender, e) =>
                {
                    changedHandler(sender);
                };

        /*
         * If the component is an enumerable there are two steps. First check to see if it supports the INotifyCollectionChanged event.
         * If it supports it add and handler on to this object to support notification.  Next iterate through the collection of objects
         * to add hook up their PropertyChangedEvent.
         * 
         * If the component isn't a collection then iterate through its properties and attach the changed handler to the properties.
         */
        if (component is IEnumerable<object>)
        {
            if (component is INotifyCollectionChanged)
            {
                //((INotifyCollectionChanged)component).CollectionChanged += collectionHandler;
                ((INotifyCollectionChanged)component).CollectionChanged += (sender, e) =>
                    {
                        changedHandler(sender);
                    };
            }

            foreach (object obj in component as IEnumerable<object>)
            {
                if (obj is INotifyPropertyChanged)
                    SetupPropertyChanged(closed, (INotifyPropertyChanged)obj, changedHandler);
            }
        }
        else
        {
            foreach (PropertyInfo info in component.GetType().GetProperties())
            {
                var propertyValue = info.GetValue(component, new object[] { });
                var inpc = propertyValue as INotifyPropertyChanged;
                if (inpc == null) continue;
                SetupPropertyChanged(closed, inpc, changedHandler);
            }
        }
    }
}

1 个答案:

答案 0 :(得分:3)

保留已经为其注册事件的属性(组件)列表,并使您的函数递归。您可以直接将组件转换为INotifyPropertyChanged

void SetupPropertyChanged(IList<object> closed, INotifyPropertyChanged component)  
{
  if(closed.Contains(component)) return; // event was already registered

  closed.Add(component);

  component.PropertyChanged += CAMConfig_PropertyChanged;            
  foreach (PropertyInfo info in componentType.GetProperties())          
  {
    var propertyValue = info.GetValue(component, new object[] { });
    var inpc = propertyValue as INotifyPropertyChanged;
    if(inpc == null) continue;
    this.SetupPropertyChanged(closed, inpc);
  }  
}