views:

47

answers:

2

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 put that into a ListView. (Ie similar to this)

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

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

Is there an established way to make this kind of conversion or do I just need to roll my own parser?

(In case it may be relevant the real items in my code are TFS Iteration Paths.)

+2  A: 

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

For instance, you can create a class like that :

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

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

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

The tree nodes will be automatically generated based on your data. If you need different classes for different levels of the hierarchy, create a different HierarchicalDataTemplate for each class

Thomas Levesque
+1  A: 

This will take 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 containing a Name property and a Children property. This can be bound directly to the TreeView by specifying a HierarchicalDataTemplate with ItemsSource="{Binding Children}" and content consisting of a <TextBlock Text="{Binding Name}"> or similar.

Alternatively you could define a tree node class in code if you want additional members or semantics. For example, given this node class:

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

your BuildTree function would 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 bound directly using a HierarchicalDataTemplate. I generally use the first solution (anonymous types) unless I want to do something special with the tree nodes.

Ray Burns