我有一个通过反序列化一些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);
}
}
}
}
答案 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);
}
}