views:

104

answers:

1

I have a bit of a complex situation with WPF Treeview Binding. I have spent the last 2 days trying Google it, and this is the closed I came up with, but it doesn't solve the issue.

Here is the situation:

I have an object that looks like this:

public class Category
{
  public string Name { get; set; }
  public List<Category> Categories { get; set; }
  public List<Product> Products { get; set; }
}

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

Each Category can have a list of objects, and child categories. I have a reason for doing this that makes complete sense to me, and the app I am writing.

The actual object construction may look Something like this:

Category - Pharmacy
  |-Product - Aspirin
  |-Product - Tylenol
  |-Category - Tooth Paste
  |  |-Product - Crest
  |  |-Product - Colgate
  |-Category - Paper Products
   |-Category - Toilet Paper
   |  |-Product - NoName
   |  |-Product - Charmin
   |-Category - Facial Tissue
      |-Product - Kleenex
Category - Household
  |-Product - Pinesol Cleaner
  |-Product - Garbage Bags

Now, I am trying to databind this relationship to a treeview. I would like the TreeView to look almost identical to the above object construct.

So far I have my XAML Treeview look like this:

  <TreeView x:Name="CategoryList" Margin="8" Grid.Row="2" Grid.RowSpan="2" ItemsSource="{Binding Path=Categories}">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type src:Category}" ItemsSource="{Binding Products}">
                    <StackPanel>
                        <TextBlock Text="{Binding Path=Name}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="{x:Type src:Product}">
                    <StackPanel>
                        <TextBlock Text="{Binding Path=Name}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>

This works great for the main list of Categories, and each of it's sub products. But it doesn't go deeper and display the sub categories under each category.

Is there any way to do this directly with Templates so that each item (category or product) is selected? I am using an MVVM pattern and don't want to resort to using the code behind, but will if it is necessary.

+2  A: 

Since you want the elements in the TreeView to have a list of children that consists of both Categories Products, you will want your Category ViewModel to have a collection that consists of both Categories and Products. For example, you could use a CompositeCollection to combine your existing collections:

public class Category
{
    public string Name { get; set; }
    public List<Category> Categories { get; set; }
    public List<Product> Products { get; set; }

    public IList Children
    {
        get
        {
            return new CompositeCollection()
            {
                new CollectionContainer() { Collection = Products },
                new CollectionContainer() { Collection = Categories }
            };
        }
    }
}

(In real code, you would probably want to keep a reference to the same collection object rather than creating a new one each time.)

Then in your HierarchicalDataTemplate, use the combined list as the ItemsSource:

<HierarchicalDataTemplate DataType="{x:Type src:Category}"
                          ItemsSource="{Binding Children}">

The items will be a mix of Product and Category objects, and WPF will use the appropriate DataTemplate for each one.

Quartermeister
This is a perfect solution, thanks! Just as a note for anyone else, the order you add the collection containers to the composite collection is the order they appear in the treeview. If you add the categories first, they would appear before the products
thorkia