0

I have a list of OBJs, that I've stored into List<OBJ> OBJS property, and I've created a HierarchicalDataTemplate for the Data, which works (see below).

 <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type EntityType:Projectiles}" 
                                ItemsSource="{Binding Value}">
           <TextBlock Text="{Binding RelativeSource={RelativeSource Self},
                      Converter={StaticResource NameConverter}}"/>
      </HierarchicalDataTemplate>
 </TreeView.Resources>

Which gave me the below TreeView:

  • Projectile A
  • Projectile B
  • Projectile C
  • Particle A
  • Particle B
  • Particle C

However, because my data is actually a List of OBJ, there are child classes that are in that same list, I wanted to group the classes under its own Type. i.e. new List<OBJ>() { new Projectiles(), new Particles() } should have a node for Projectiles, Particles, etc. I created a Converter that changes it into a Dictionary, which then doesn't work with the above HierarchicalDataTemplate because it is now a Dictionary<string, List<OBJ>.

I then created a new HierarchicalDataTemplate that handled the Dictionary<string, List<OBJ>, see below.

 <TreeView Name="MyTreeView" ItemsSource="{Binding OBJS, 
           Converter={StaticResource ItemsSourceConverter}}"
 <TreeView.ItemTemplate>
      <HierarchicalDataTemplate ItemsSource="{Binding Value}">
           <TextBlock Text="{Binding Key}" />
      </HierarchicalDataTemplate>
 </TreeView.ItemTemplate>

And the Converter:

 class ItemsSourceConverter : IValueConverter {
      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
           List<OBJ> objs = new List<OBJ>(value as List<OBJ>);
           var query = (from a in objs
                          group a by a.GetType() into b
                          select new {
                               EntityName = b.Key.ToString().Split('.').Last().Substring(0,1).ToUpper() + b.Key.ToString().Split('.').Last().Substring(1).ToLower(),
                               Entities = b.OrderBy(a=>a.retrieveName()).ToList()
                          }).ToDictionary(kvp => kvp.EntityName, kvp => kvp.Entities);
           return query;
      }

      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
           return null;
      }
 }

Which gave me the below TreeView, creating the proper groups:

  • Projectiles
  • Particles

But expanding them would give me the following two errors for each node inside Particles or Projectiles.

System.Windows.Data Error: 40 : BindingExpression path error: 'Value' property not found on 'object' 'Projectiles' (HashCode=37857370)'. BindingExpression:Path=Value; DataItem='Projectiles' (HashCode=37857370); target element is 'TreeViewItem' (Name=''); target property is 'ItemsSource' (type 'IEnumerable') System.Windows.Data Error: 40 : BindingExpression path error: 'Key' property not found on 'object' 'Projectiles' (HashCode=37857370)'. BindingExpression:Path=Key; DataItem='Projectiles' (HashCode=37857370); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

It seems that once you set TreeView.ItemTemplate, it ignores all the DataTemplates that you have defined in TreeView.Resources?

In my first attempt, I was able to use DataType="{x:Type EntityType:Projectiles}" to specify that I wanted the HierarchicalDataTemplate to be used for Projectiles objects, is there a DataType syntax that will let me specify DataType="{x:Type Dictionary<string, List<OBJ>>}"? So I can do something like the below?

 <HierarchicalDataTemplate ItemsSource="{Binding Value}"
                           DataType="{x:Type Dictionary<string, List<OBJ>>}">
      <TextBlock Text="{Binding Key}" />
 </HierarchicalDataTemplate>

That way, in the end, I'll have the below:

  • Projectiles
    • Projectile A
    • Projectile B
    • Projectile C
  • Particles
    • Particle A
    • Particle B
    • Particle C

Edit: This should also work if I have sub-levels, see below.

  • Projectiles
    • Projectile SubType A
      • Projectile A
    • Projectile SubType B
      • Projectile B
      • Projectile C
  • Particles
    • Particle A
    • Particle B
    • Particle C
Bob.
  • 3,894
  • 4
  • 44
  • 76
  • So, just to clarify, you want the projectile node to have particle and projectile child nodes and the particle node to have particle and projectile child nodes. Is this correct? –  Jul 04 '13 at 18:33
  • @Killingsworth No no, sorry. It should be a node for `Projectiles`, then in `Items`, it'll have the names of the `Projectiles`. I'll add what it should finally end up being. – Bob. Jul 04 '13 at 18:36

1 Answers1

0

Try this:

<TreeView Name="treeView1" ItemsSource="{Binding}" >
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Value}">
            <TextBlock FontWeight="Bold" Text="{Binding Path=Key}" />
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Sample classes:

class OBJ
{
    public string Name { get; set; }
}

class Projectiles : OBJ {}

class Particles : OBJ {}

Sample data initialization:

List<OBJ> _source = new List<OBJ>() 
    {
        new Projectiles{ Name = "Projectile A"},
        new Projectiles{ Name = "Projectile B"},
        new Projectiles{ Name = "Projectile C"},
        new Particles { Name = "Particle A"},
        new Particles { Name = "Particle B"},
        new Particles { Name = "Particle C"},
    };

var query = (from a in _source
             group a by a.GetType() into b
             select new
             {
                 EntityName = b.Key.ToString().Split('.').Last().Substring(0, 1).ToUpper() + b.Key.ToString().Split('.').Last().Substring(1).ToLower(),
                 Entities = b.OrderBy(a => a.Name).ToList()
             }).ToDictionary(kvp => kvp.EntityName, kvp => kvp.Entities);

treeView1.DataContext = query;
kmatyaszek
  • 19,016
  • 9
  • 60
  • 65
  • Is there any way to make it so that when I expand `Projectile` etc. into sub-types it'll work too? It looks like it only works with one level, and not sub-levels. i.e. Projectiles -> Projectiles Type A. I've updated the proposed final result, I know I can just set the DataTemplate inside a Hierarchical, but what happens if there is another level under that? I don't want to have to copy/paste and it wouldnt work if there isn't a sub-level. – Bob. Jul 04 '13 at 20:00