使用反射获取对象属性并显示到Treeview
Using Reflection to obtain object properties and display onto Treeview
我一直在寻找最好地完成这项任务的方法并最终确定了这一点。
https://rbrundritt.wordpress.com/2012/01/30/view-object-properties-in-wpf-treeview/
它似乎确实给了我我所希望的,但是当我尝试获取线程的对象属性时,我最终得到了一个 Whosebug 异常。因此,似乎 objectNode class 以递归方式实例化自身太多次,然后发生导致堆栈溢出的事情?我不确定如何着手解决这个问题并使其即使对于具有许多属性(如线程)的 class 也能正常工作,我们将不胜感激。
对象节点class如下图
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
namespace Server.Host
{
public class ObjectNode
{
#region Private Properties
private string _name;
private object _value;
private Type _type;
#endregion
#region Constructor
public ObjectNode(object value)
{
ParseObjectTree("root", value, value.GetType());
}
public ObjectNode(string name, object value)
{
ParseObjectTree(name, value, value.GetType());
}
public ObjectNode(object value, Type t)
{
ParseObjectTree("root", value, t);
}
public ObjectNode(string name, object value, Type t)
{
ParseObjectTree(name, value, t);
}
#endregion
#region Public Properties
public string Name
{
get { return _name; }
}
public object Value
{
get { return _value; }
}
public Type Type
{
get { return _type; }
}
public ObservableCollection<ObjectNode> Children { get; set; }
#endregion
#region Private Methods
private void ParseObjectTree(string name, object value, Type type)
{
Children = new ObservableCollection<ObjectNode>();
_type = type;
_name = name;
if (value != null)
{
if (value is string && type != typeof(object))
{
if (value != null)
{
_value = "\"" + value + "\"";
}
}
else if (value is double || value is bool || value is int || value is float || value is long || value is decimal)
{
_value = value;
}
else
{
_value = "{" + value.ToString() + "}";
}
}
PropertyInfo[] props = type.GetProperties();
if (props.Length == 0 && type.IsClass && value is IEnumerable && !(value is string))
{
IEnumerable arr = value as IEnumerable;
if (arr != null)
{
int i = 0;
foreach (object element in arr)
{
Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
i++;
}
}
}
foreach (PropertyInfo p in props)
{
if (p.PropertyType.IsPublic)
{
if (p.PropertyType.IsClass || p.PropertyType.IsArray || p.PropertyType.IsInterface)
{
if (p.PropertyType.IsArray)
{
try
{
object v = p.GetValue(value, null);
IEnumerable arr = v as IEnumerable;
ObjectNode arrayNode = new ObjectNode(p.Name, arr.ToString(), typeof(object));
if (arr != null)
{
int i = 0, k = 0;
ObjectNode arrayNode2;
foreach (object element in arr)
{
//Handle 2D arrays
if (element is IEnumerable && !(element is string))
{
arrayNode2 = new ObjectNode("[" + i + "]", element.ToString(), typeof(object));
IEnumerable arr2 = element as IEnumerable;
k = 0;
foreach (object e in arr2)
{
arrayNode2.Children.Add(new ObjectNode("[" + k + "]", e, e.GetType()));
k++;
}
arrayNode.Children.Add(arrayNode2);
}
else
{
arrayNode.Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
}
i++;
}
}
Children.Add(arrayNode);
}
catch { }
}
else
{
object v = p.GetValue(value, null);
if (v != null)
{
Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
}
}
}
else if (p.PropertyType.IsValueType && !(value is string))
{
try
{
object v = p.GetValue(value, null);
if (v != null)
{
Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
}
}
catch { }
}
}
}
}
#endregion
}
}
因此,可以将 TreeView 添加到 WPF 中,就像
<TreeView Name="ResultTreeView" BorderThickness="0">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ObjectNode}"
ItemsSource="{Binding Path=Children}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
然后在我的代码中,我简单地写
private Foo foo = new Foo();
ObservableCollection<ObjectNode> nodes = new ObservableCollection<ObjectNode>();
nodes.Add(new ObjectNode("result", foo));
ResultTreeView.ItemsSource = nodes;
其中 Foo 可以是任何 class。对于大多数事情,它工作正常。但是如果我在 class 中有一个线程,它将抛出异常。例如只是一个简单的
public class Foo
{
public Foo()
{
Bar = new Thread(Baz);
}
public Thread Bar { get; set; }
private static void Baz()
{
}
}
抛出的错误是
"An unhandled exception of type 'System.WhosebugException' occurred in mscorlib.dll"
再一次,非常欢迎任何建议。
从 ObjectNode class 的构造函数调用的 ParseObjectTree 方法似乎递归且无休止地实例化此 class 的其他实例。这就是 WhosebugException 的原因。您需要一种算法来限制递归,可能是通过计算世代数。
我一直在寻找最好地完成这项任务的方法并最终确定了这一点。
https://rbrundritt.wordpress.com/2012/01/30/view-object-properties-in-wpf-treeview/
它似乎确实给了我我所希望的,但是当我尝试获取线程的对象属性时,我最终得到了一个 Whosebug 异常。因此,似乎 objectNode class 以递归方式实例化自身太多次,然后发生导致堆栈溢出的事情?我不确定如何着手解决这个问题并使其即使对于具有许多属性(如线程)的 class 也能正常工作,我们将不胜感激。
对象节点class如下图
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
namespace Server.Host
{
public class ObjectNode
{
#region Private Properties
private string _name;
private object _value;
private Type _type;
#endregion
#region Constructor
public ObjectNode(object value)
{
ParseObjectTree("root", value, value.GetType());
}
public ObjectNode(string name, object value)
{
ParseObjectTree(name, value, value.GetType());
}
public ObjectNode(object value, Type t)
{
ParseObjectTree("root", value, t);
}
public ObjectNode(string name, object value, Type t)
{
ParseObjectTree(name, value, t);
}
#endregion
#region Public Properties
public string Name
{
get { return _name; }
}
public object Value
{
get { return _value; }
}
public Type Type
{
get { return _type; }
}
public ObservableCollection<ObjectNode> Children { get; set; }
#endregion
#region Private Methods
private void ParseObjectTree(string name, object value, Type type)
{
Children = new ObservableCollection<ObjectNode>();
_type = type;
_name = name;
if (value != null)
{
if (value is string && type != typeof(object))
{
if (value != null)
{
_value = "\"" + value + "\"";
}
}
else if (value is double || value is bool || value is int || value is float || value is long || value is decimal)
{
_value = value;
}
else
{
_value = "{" + value.ToString() + "}";
}
}
PropertyInfo[] props = type.GetProperties();
if (props.Length == 0 && type.IsClass && value is IEnumerable && !(value is string))
{
IEnumerable arr = value as IEnumerable;
if (arr != null)
{
int i = 0;
foreach (object element in arr)
{
Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
i++;
}
}
}
foreach (PropertyInfo p in props)
{
if (p.PropertyType.IsPublic)
{
if (p.PropertyType.IsClass || p.PropertyType.IsArray || p.PropertyType.IsInterface)
{
if (p.PropertyType.IsArray)
{
try
{
object v = p.GetValue(value, null);
IEnumerable arr = v as IEnumerable;
ObjectNode arrayNode = new ObjectNode(p.Name, arr.ToString(), typeof(object));
if (arr != null)
{
int i = 0, k = 0;
ObjectNode arrayNode2;
foreach (object element in arr)
{
//Handle 2D arrays
if (element is IEnumerable && !(element is string))
{
arrayNode2 = new ObjectNode("[" + i + "]", element.ToString(), typeof(object));
IEnumerable arr2 = element as IEnumerable;
k = 0;
foreach (object e in arr2)
{
arrayNode2.Children.Add(new ObjectNode("[" + k + "]", e, e.GetType()));
k++;
}
arrayNode.Children.Add(arrayNode2);
}
else
{
arrayNode.Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
}
i++;
}
}
Children.Add(arrayNode);
}
catch { }
}
else
{
object v = p.GetValue(value, null);
if (v != null)
{
Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
}
}
}
else if (p.PropertyType.IsValueType && !(value is string))
{
try
{
object v = p.GetValue(value, null);
if (v != null)
{
Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
}
}
catch { }
}
}
}
}
#endregion
}
}
因此,可以将 TreeView 添加到 WPF 中,就像
<TreeView Name="ResultTreeView" BorderThickness="0">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ObjectNode}"
ItemsSource="{Binding Path=Children}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
然后在我的代码中,我简单地写
private Foo foo = new Foo();
ObservableCollection<ObjectNode> nodes = new ObservableCollection<ObjectNode>();
nodes.Add(new ObjectNode("result", foo));
ResultTreeView.ItemsSource = nodes;
其中 Foo 可以是任何 class。对于大多数事情,它工作正常。但是如果我在 class 中有一个线程,它将抛出异常。例如只是一个简单的
public class Foo
{
public Foo()
{
Bar = new Thread(Baz);
}
public Thread Bar { get; set; }
private static void Baz()
{
}
}
抛出的错误是 "An unhandled exception of type 'System.WhosebugException' occurred in mscorlib.dll"
再一次,非常欢迎任何建议。
从 ObjectNode class 的构造函数调用的 ParseObjectTree 方法似乎递归且无休止地实例化此 class 的其他实例。这就是 WhosebugException 的原因。您需要一种算法来限制递归,可能是通过计算世代数。