C# | 使用TreeView展示一个对象(通过反射实现)

#C# | 使用TreeView展示一个对象

文章目录

    • 前言
    • 完整源码
    • 改进版本

前言

在编程过程中,我们经常需要处理复杂的对象和数据结构。将这些数据结构展示在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());
}

你可能感兴趣的:(c#,c#,.NET,开发语言,TreeView,反射)