反映序列化对象以设置 PropertyChanged 事件
我有一个通过反序列化某些 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 集合属性类型问题,因为它在遍历对象时抛出异常。我还突然意识到,我不知道这个对象结构是否会有循环引用。
有人可以帮我计算出这段代码,以便我可以遍历对象图。现在我不太担心循环引用的可能性,但如果有一个解决方案能够防止这种情况,那将非常非常有帮助!
根据卡雷尔的回答,我创建了这个“助手”类:
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);
}
}
}
}
I have a object that is created through deserialization of some XML. I used Visual Studio's tool to generate the XSD from the vendor mockup XML. Then using the XSD tool I got classes from them. I set the XSD tool so that it would make the generated classes be INotifyPropertyChanged.
Now I'm trying to display this object in a "tab" on my WPF application. I want a "dirty" indicator whenever someone makes a change. The issue is that this object, being generated classes from generated XSD is not the prettiest structure. The display for this object is does not mimic its data structure. I thought of creating display objects (not using MMVM ATM) and using those to bind my changes to and then persist those changes to the object as a data object. I would essentially just throw out my current display to do this since I'm now just adding a check for if there were edits.
My thought is to reflect across the object and set the PropertyChanged event for every property I come across (walking through the graph of objects and properties). My reflection fu is failing me and I'm also probably making some short-sighted mistakes.
Here is the code I've written thus far:
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[] { }));
}
}
I think I'm running into Observable collection property type issues as its throwing an exception when I'm traversing my object. It also just hit me that I don't know if this object structure would have circular references.
Can someone help me work out this code so that I can traverse the object graph. Right now I'm not too concerned about the possibility of circular references, but if a solution presents itself that prevents that situation it would be very, very helpful!
Based on Karel's answer I've created this "helper" class:
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);
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
保留已为其注册事件的属性(组件)列表,并使函数递归。您可以将组件直接转换为
INotifyPropertyChanged
。Keep a list of properties (components) for which you've already registered the event and make your function recursive. And you can cast component to
INotifyPropertyChanged
directly.