WPF is a good way to take a list of trees

I have a list that looks like this:

  Base / Level1 / Item1
 Base / Level1 / Item2
 Base / Level1 / Sub1 / Item1
 Base / Level2 / Item1
 Base / Level3 / Sub1 / Item1

I would like an easy way to put this into a ListView. (Same)

  Base
   |
   + -> Level1
   |  |
   |  + = Item1
   |  + = Item2
   |  |
   |  + -> Sub1
   |  |
   |  + = Item1
   |
   + -> Level2
   |  |
   |  + = Item1

   |
   + -> Level3
        |
        + -> Sub1
            |
            + = Item1

Is there an established way to do this conversion, or do I just need to collapse my own parser?

(In case this may be relevant, the real elements in my code are Tter Iteration Paths.)

+4
source share
3 answers

This will lead to your list of strings and turn it into a tree suitable for viewing with TreeView, as you described:

public IList BuildTree(IEnumerable<string> strings) { return from s in strings let split = s.Split("/") group s by s.Split("/")[0] into g // Group by first component (before /) select new { Name = g.Key, Children = BuildTree( // Recursively build children from s in grp where s.Length > g.Key.Length+1 select s.Substring(g.Key.Length+1)) // Select remaining components }; } 

This will return a tree of anonymous types, each of which contains the Name property and the Children property. This can be linked directly to the TreeView by specifying a HierarchicalDataTemplate with ItemsSource="{Binding Children}" and content consisting of <TextBlock Text="{Binding Name}"> or similar.

Alternatively, you can define the node tree class in the code if you need additional members or semantics. For example, this node class:

 public class Node { public string Name { get; set; } public List<Node> Children { get; set; } } 

your BuildTree function will be slightly different:

 public List<Node> BuildTree(IEnumerable<string> strings) { return ( from s in strings let split = s.Split("/") group s by s.Split("/")[0] into g // Group by first component (before /) select new Node { Value = g.Key, Children = BuildTree( // Recursively build children from s in grp where s.Length > g.Key.Length+1 select s.Substring(g.Key.Length+1)) // Select remaining components } ).ToList(); } 

Again, this can be linked directly using the HierarchicalDataTemplate . I usually use the first solution (anonymous types) if I don't want to do something special with the nodes of the tree.

+5
source

WPF TreeView can display hierarchical data using HierarchicalDataTemplates . However, your data is currently flat, so you have to convert it to a hierarchical data structure. There is no built-in way for you to do this ...

For example, you can create a class like this:

 class Node { public string Name { get; set; } public List<Node> Children { get; set; } } 

Parse your flat data into a list of Node objects with sub-numbers and create a TreeView with the following HierarchicalDataTemplate in resources:

 <TreeView ItemsSource="{Binding ListOfNodes}"> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}" /> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> 

Tree nodes will be automatically created based on your data. If you need different classes for different hierarchy levels, create a different HierarchicalDataTemplate for each class.

+2
source

A more general implementation might be this. Imagine a Node class defined as:

 public class Node<TItem, TKey> { public TKey Key { get; set; } public int Level { get; set; } public IEnumerable<TItem> Data { get; set; } public List<Node<TItem, TKey>> Children { get; set; } } 

and two common extension methods IEnumerable<T> :

 public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, params Func<TItem, TKey>[] keySelectors) { return list.ToTree(0, keySelectors); } public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, int nestingLevel, params Func<TItem, TKey>[] keySelectors) { Stack<Func<TItem, TKey>> stackSelectors = new Stack<Func<TItem, TKey>>(keySelectors.Reverse()); if (stackSelectors.Any()) { return list .GroupBy(stackSelectors.Pop()) .Select(x => new Node<TItem, TKey>() { Key = x.Key, Level = nestingLevel, Data = x.ToList(), Children = x.ToList().ToTree(nestingLevel + 1, stackSelectors.ToArray()) }) .ToList(); } else { return null; } 

You can use these methods to combine a flat list of user objects into a tree with an arbitrary aggregation level and more elegant syntax. The only limitation is that the aggregation keys must be of the same type.

Example:

 class A { public int a { get;set; } public int b { get;set; } public int c { get;set; } public int d { get;set; } public string s { get;set; } public A(int _a, int _b, int _c, int _d, string _s) { a = _a; b = _b; c = _c; d = _d; s = _s; } } void Main() { A[] ls = { new A(0,2,1,10,"one"), new A(0,1,1,11,"two"), new A(0,0,2,12,"three"), new A(0,2,2,13,"four"), new A(0,0,3,14,"five"), new A(1,0,3,15,"six"), new A(1,1,4,16,"se7en"), new A(1,0,4,17,"eight"), new A(1,1,5,18,"nine"), new A(1,2,5,19,"dunno") }; var tree = ls.ToTree(x => xa, x => xb, x => xc, x => xd); } 

Notes. This implementation is not a β€œreal” tree, since there is not a single root node, but you can easily implement the Tree<TItem, TKey> .

NTN

+1
source

Source: https://habr.com/ru/post/1298940/


All Articles