C# 將扁平狀數據鏈接成樹狀結構的通用方法


在項目中經常會遇到從數據庫查詢數據綁定到TreeVIew上,這時我們需要將查詢出來的數據轉換成樹形結構數據,每次寫覺得工作會很重復,所以寫了一個通用的轉換類。

 

第一步,我們需要建一個基類,這個基類的意義主要是擴展數據庫實體類做連接用,用於確定樹形結構中節點與子項的關系,

其中Parent為當前節點的父節點,Children為當前節點的子節點,IsLinked是判斷當前節點是否已連接,用於防止數據中有循環依賴導致創建樹的時候形成死循環。

TreeModel基類

public class TreeBase<T>
    {
        private bool isLinked = false;
        /// <summary>
        /// 是否已創建連接
        /// </summary>
        public bool IsLinked
        {
            get { return isLinked; }
            set { isLinked = value; }
        }

        /// <summary>
        /// 父節點
        /// </summary>
        public T Parent { get; set; }

        /// <summary>
        /// 子節點
        /// </summary>
        public ObservableCollection<T> Children { get; set; }
    }

第二步,我們需要創建一個連接方法,將輸入的扁平狀數據轉換為樹狀結構,我希望在使用的時候可以自己指定實體的哪個屬性為ID,哪個為父ID

,這里我們使用了Expression,這樣就可以使用linq表達式去指定屬性了,剩下的就是利用反射獲取實體的值與遞歸連接了,這樣一個簡單的通用創建樹的方法就有了。

    public class TreeHelper
    {
        /// <summary>
        /// 創建樹
        /// </summary>
        /// <typeparam name="T">實體類型</typeparam>
        /// <param name="root">根節點</param>
        /// <param name="list">所有數據</param>
        /// <param name="idProperty">節點唯一標識屬性表達式</param>
        /// <param name="parentIdProperty">父節點屬性表達式</param>
        public static void CreateTree<T>(T root, IList<T> list, string idPropertyName, string parentIdPropertyName) where T : TreeBase<T>
        {
            root.Children = new ObservableCollection<T>();
            list.Where(e => (string)GetPropertyValue(e, parentIdPropertyName) == (string)GetPropertyValue(root, idPropertyName) && !e.IsLinked).ToList().ForEach(e => { root.Children.Add(e); e.IsLinked = true; });
            foreach (var leaf in root.Children)
            {
                leaf.Parent = root;
                CreateTree<T>(leaf, list, idPropertyName, parentIdPropertyName);
            }
        }

        /// <summary>
        /// 創建多個根節點的樹
        /// </summary>
        /// <typeparam name="T">實體類型</typeparam>
        /// <param name="root">根節點</param>
        /// <param name="list">所有數據</param>
        /// <param name="idProperty">節點唯一標識屬性表達式</param>
        /// <param name="parentIdProperty">父節點屬性表達式</param>
        public static ObservableCollection<T> CreateTree<T>(IList<T> list, Expression<Func<T, object>> idProperty, Expression<Func<T, object>> parentIdProperty) where T : TreeBase<T>
        {
            //查找父節點不存在的leaf,作偽根節點
            var roots = new ObservableCollection<T>();
            var idPropertyName = GetMemberName(idProperty);
            var parentIdPropertyName = GetMemberName(parentIdProperty);
            list.Where(e => list.Count(item =>
                    (string)GetPropertyValue(item, idPropertyName) == (string)GetPropertyValue(e, parentIdPropertyName)) == 0).ToList().ForEach(e => roots.Add(e));
            foreach (var root in roots)
            {
                CreateTree<T>(root, list, idPropertyName, parentIdPropertyName);
            }
            return roots;
        }

        private static object GetPropertyValue<T>(T t, string propertyName)
        {
            return t.GetType().GetProperty(propertyName).GetValue(t, null);
        }

        private static string GetMemberName<T, TMember>(Expression<Func<T, TMember>> propertySelector)
        {
            var propertyExp = propertySelector.Body as MemberExpression;
            if (propertyExp == null)
            {
                throw new ArgumentException("不合理的表達式!");
            }
            return propertyExp.Member.Name;
        }



    }

 

 

 使用示例:

先聲明一個綁定實體,繼承自TreeBase

public class Item : TreeBase<Item>
{
  public string Id{get;set;}
  public string ParentID{get;set;}
  public string DisplayText{get;set;}
}

創建測試數據

            IList<Item> data = new List<Item>();
            var group1= new Item(){ Id = Guid.NewGuid().ToString(), DisplayText="Root"};
            var item1 = new Item { Id = Guid.NewGuid().ToString(), ParentId = group1.Id, DisplayText = "Leaf1" };
            var item2 = new Item { Id = Guid.NewGuid().ToString(), ParentId = group1.Id, DisplayText = "Leaf2" };
            var item3 = new Item { Id = Guid.NewGuid().ToString(), ParentId = group1.Id, DisplayText = "Leaf3" };
            var item4 = new Item() { Id = Guid.NewGuid().ToString(), ParentId = item3.Id, DisplayText = "Leaf3:Leaf1" };
            var item5 = new Item() { Id = Guid.NewGuid().ToString(), ParentId = item3.Id, DisplayText = "Leaf3:Leaf2" };
            var item6 = new Item() { Id = Guid.NewGuid().ToString(), ParentId = item3.Id, DisplayText = "Leaf3:Leaf3" };
            data.Add(group1);
            data.Add(item1);
            data.Add(item2);
            data.Add(item3);
            data.Add(item4);
            data.Add(item5);
            data.Add(item6);

界面模板綁定

        <TreeView Height="245" HorizontalAlignment="Left" Margin="46,35,0,0" Name="treeView1" VerticalAlignment="Top" Width="156" >
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding DisplayText}"></TextBlock>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

后台數據綁定

            treeView1.ItemsSource = TreeHelper.CreateTree<Item>(data, item => item.Id, item => item.ParentId);

以后綁定樹形數據是不是很簡單呢?

這種方法不適應主鍵為Guid的數據類型

因為以下代碼無法獲取Guid類型的名稱,歡迎各位大叔,大嬸,大神們指點

        private static string GetMemberName<T, TMember>(Expression<Func<T, TMember>> propertySelector)
        {
            var propertyExp = propertySelector.Body as MemberExpression;
            if (propertyExp == null)
            {
                throw new ArgumentException("不合理的表達式!");
            }
            return propertyExp.Member.Name;
        }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM