1
votes

This is probably not so hard, but I am getting more and more confused.

I want to show a TreeView in my View, but I can't get the databinding right.

The TemplateFamilyList collection is the root object. It will contain a list of TemplateFamily objects. Each of these TemplateFamily objects will contain a Template object and a list of TemplateParameter objects, called TemplateParameterMembers. The actual Template class and the actual TemplateParameter class both have a Name property amongst other properties.

So, in my ViewModel class I have this method that populates the collections:

 public void LoadTemplateTree()
    {
        TemplateFamilyList = new ObservableCollection<TemplateFamily>();

        List<Template> templateList = BuilderService.GetAllTemplates();
        List<TemplateParameter> templatesParametersList;

        foreach (Template template in templateList)
        {
            templatesParametersList = BuilderService.GetATemplatesParameters(template.TemplateID);
            TemplateFamilyList.Add(new TemplateFamily(template, new ObservableCollection<TemplateParameter>(templatesParametersList)));              
        }
    }

Here is the TemplateFamily class:

 public class TemplateFamily
{
    public Template Template { get; set; }

    public ObservableCollection<TemplateParameter> TemplateParameterMembers { get; set; }

    public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
    {
        Template = aTemplate;
        TemplateParameterMembers = templatesParameters;
    }
}

What I would like is a TreeView such as:

  1. NameOfTemplateA
  2. NameOfTemplateB

    • NameOfTemplateParameterB1
    • NameOfTemplateParameterB2

etc.

In my XAML I have this: (but it is not working, it gives some blank treeview with only arrows but no names).

<TabItem Header="Template Files" Height="22" VerticalAlignment="Top">
            <TreeView Height="Auto" Name="treeView1" Width="Auto" ItemsSource="{Binding TemplateFamilyList}">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding TemplateParameterMembers}" >
                        <TextBlock Text="{Binding Path=Name}" Margin="2" ></TextBlock>
                    </HierarchicalDataTemplate >
                </TreeView.ItemTemplate>
            </TreeView>
        </TabItem>

The ViewModel is bound to the View with a ViewModelLocator, and it works fine in other cases. I know that if I bind to TemplateFamilyList, it will get the right object, I just don't know how to show the name of each TemplateFamily in that list (which should be the Name property of the Template property of the TemplateFamily object) and then for the next level in the tree if there is a list of TemplateParameterMembers, how to show the Name property of the TemplateParameter property of each item in the TemplateParameterMembers collection.

I guess I could try having another property Name in the TemplateFamily class, and simply duplicate the Template's Name property in the constructor, such as:

public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
    {
        Template = aTemplate;
        Name = aTemplate.Name;
        TemplateParameterMembers = templatesParameters;
    }

but I don't like this approach, and besides, I don't have a special "container" class for the collection of TemplateParameters, so I would still need to somehow drill down and fetch the Name property of the TemplateParameter in each item of TemplateParameterMembers collection.

I saw this post and it seems that in the answer, Gishu uses DataType attributes such as:

DataType="{x:Type local:Group}

The local namespace being defined in the OP post as:

xmlns:local="clr-namespace:TaskManager.Domain;assembly=TaskManager.Domain"

However, in my case, the Template and TemplateParameter classes are part of my model, they are not even in the same project as the ViewModel and View. I have this as far as my local namespace goes:

xmlns:local="clr-namespace:S.VisualStudioExtension"           
         local:ViewModelLocator.AutoWireViewModel="True"

Should I then add a namespace for the Template and TemplateParameter classes, such as:

xmlns:domain="clr-namespace:S.ModelDomain" 

and try to use that in the DataType attribute?

I also suspect there is something wrong with my XAML, perhaps I should have another Hierarchical DataTemplate?

1

1 Answers

1
votes

The best and most versatile (in my opinion) solution in this situation is to define a template for each type of items that will be presented in the tree, and put them in the tree's resources dictionary. Each template should be associated with corresponding item type by using the DataType property. That way suitable template will be automatically selected to present each item based on its type. Assuming that your TemplateParameter class is defined in a project called S.ModelDomain in namespace S.ModelDomain and local prefix is properly defined in your XAML, this should give you expected result (I took the liberty of stripping your XAML of irrelevant attributes):

<TreeView xmlns:models="clr-namespace:S.ModelDomain;assembly=S.ModelDomain"
          ItemsSource="{Binding TemplateFamilyList}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TemplateFamily}"
                                  ItemsSource="{Binding TemplateParameterMembers}" >
            <TextBlock Text="{Binding Template.Name}" />
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type models:TemplateParameter}">
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

It is common practice though to define namespace mappings on the root element of XAML (usually a Window or a UserControl). See more here: XAML Namespaces and Namespace Mapping for WPF XAML.

Note that templates do not have x:Key parameter specified despite being put in a resource dictionary. It is a requirement for the template to be considered during automatic template selection process. This kind of templates are called implicit templates. Here's a useful link on that: Data Templating Overview.