C# | 通过反射将对象属性展示在TreeView中

简介: 在编程过程中,我们经常需要处理复杂的对象和数据结构。将这些数据结构展示在UI界面上是很有用的,可以帮助开发者更好地理解和分析数据。而TreeView作为一种常见的控件,可以以树状结构的方式展示数据,非常适合用于展示层次化的对象。本文将介绍如何使用TreeView控件展示一个对象,并且可以动态处理对象中的属性和子对象。通过本文的学习,您将学会如何更好地理解和展示数据,提高编程效率。

image.png

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());
}
相关文章
|
1月前
|
C# 数据库
40.c#:TreeView 控件
40.c#:TreeView 控件
19 1
|
3月前
|
设计模式 C#
C#反射机制实现开闭原则的简单工厂模式
C#反射机制实现开闭原则的简单工厂模式
18 0
|
4月前
|
安全 C# 图形学
C#的反射机制
C#的反射机制
|
5月前
|
存储 Java API
C#反射(Reflection)详解
反射是.NET中的重要机制,通过反射可以得到*.exe或*.dll等程序集内部的接口、类、方法、字段、属性、特性等信息,还可以动态创建出类型实例并执行其中的方法。反射指程序可以访问、检测和修改它本身状态或行为的一种能力。通俗一点:我们在获取其他实体类的字段名或实列,只能获取公有的,而有了反射之后可以获取私有的,可以获取他的基类等等,可以说把家底查得清清楚楚。
31 1
|
7月前
|
开发框架 缓存 安全
C#OOP之十三 组件、程序集与反射
C#OOP之十三 组件、程序集与反射
38 0
|
7月前
|
开发框架 .NET Java
C#下反射动态加载dll后如何卸载?
C#下反射动态加载dll后如何卸载?
|
7月前
|
Oracle 架构师 关系型数据库
C#反射应用之实现动态可配置可扩展框架的简单示例
C#反射应用之实现动态可配置可扩展框架的简单示例
|
7月前
|
Java 关系型数据库 MySQL
C#反射(Reflection)详解及于java反射的对比
C#反射(Reflection)详解及于java反射的对比
|
9月前
|
C# 数据安全/隐私保护
C#机房重构-修改密码之职责链模式+反射
C#机房重构-修改密码之职责链模式+反射
42 0
|
10月前
|
运维 Devops C#
【C#编程最佳实践 十七】反射工厂最佳实践
【C#编程最佳实践 十七】反射工厂最佳实践
58 0