C# | 通过反射将对象属性展示在TreeView中
@[toc]
前言
在编程过程中,我们经常需要处理复杂的对象和数据结构。将这些数据结构展示在UI界面上是很有用的,可以帮助开发者更好地理解和分析数据。
而TreeView作为一种常见的控件,可以以树状结构的方式展示数据,非常适合用于展示层次化的对象。
本文将介绍如何使用TreeView控件展示一个对象,并且可以动态处理对象中的属性和子对象。通过本文的学习,您将学会如何更好地理解和展示数据,提高编程效率。
完整源码
话不多说,先上扩展类型的完整源码:
public static class TreeViewExtensions
{
public static void PopulateWithObject(this TreeView treeView, object obj)
{
// 清空treeView原有的节点
treeView.Nodes.Clear();
// 创建一个根节点
var rootNode = new TreeNode(obj.GetType().Name);
treeView.Nodes.Add(rootNode);
// 将对象转换为属性集合
var properties = TypeDescriptor.GetProperties(obj);
foreach (PropertyDescriptor property in properties)
{
// 创建一个节点,节点名称为属性名
var propertyNode = new TreeNode(property.Name);
// 获取属性值
var value = property.GetValue(obj);
// 如果属性值为null,则设置节点的文本为“null”
if (value == null)
{
propertyNode.Text += " = null";
}
else
{
// 如果属性值是一个可枚举类型,则递归调用此方法将其添加到节点上
if (value is IEnumerable enumerable)
{
foreach (var item in enumerable)
{
var childNode = new TreeNode(item.GetType().Name);
childNode.PopulateWithObject(item);
propertyNode.Nodes.Add(childNode);
}
}
else
{
// 如果属性值不是可枚举类型,则直接将其文本添加到节点上
propertyNode.Text += " = " + value.ToString();
}
}
// 将此节点添加到根节点下
rootNode.Nodes.Add(propertyNode);
}
// 展开根节点
rootNode.Expand();
}
}
调用示例:
treeView.PopulateWithObject(obj); // 调用PopulateWithObject方法
改进版本
在TreeView的扩展方法中,我们可以通过检查对象的类型和属性,判断是否存在循环引用,如果存在,我们可以设置一个默认值或者跳过该属性的添加,避免在TreeView中形成无限循环。
以下是一个示例代码,其中我们使用了一个HashSet来存储已经添加到TreeView中的对象,如果对象已经存在,则跳过不再添加。同时,我们在遍历对象的属性时,判断属性是否为对象本身或者其子对象,如果是,则跳过该属性的添加。
public static void PopulateTreeView<T>(this TreeView treeView, T obj, string rootText = "Root")
{
TreeNode rootNode = new TreeNode(rootText);
treeView.Nodes.Add(rootNode);
HashSet<object> hashSet = new HashSet<object>();
PopulateNode(obj, rootNode, hashSet);
}
private static void PopulateNode(object obj, TreeNode treeNode, HashSet<object> hashSet)
{
if (obj == null || hashSet.Contains(obj))
{
return;
}
hashSet.Add(obj);
var properties = obj.GetType().GetProperties();
foreach (var property in properties)
{
if (IsCircularReference(obj, property))
{
continue;
}
var value = property.GetValue(obj);
if (value != null)
{
if (value.GetType().IsPrimitive || value.GetType() == typeof(string) || value.GetType() == typeof(decimal))
{
var propertyNode = new TreeNode($"{property.Name}: {value}");
treeNode.Nodes.Add(propertyNode);
}
else if (value is IEnumerable enumerable)
{
var collectionNode = new TreeNode(property.Name);
treeNode.Nodes.Add(collectionNode);
foreach (var item in enumerable)
{
PopulateNode(item, collectionNode, hashSet);
}
}
else
{
var objectNode = new TreeNode(property.Name);
treeNode.Nodes.Add(objectNode);
PopulateNode(value, objectNode, hashSet);
}
}
}
}
private static bool IsCircularReference(object obj, PropertyInfo property)
{
return property.PropertyType == obj.GetType() || property.PropertyType.IsSubclassOf(obj.GetType());
}