Search code examples
wpfdata-bindingtreeviewcheckboxhierarchicaldatatemplate

Developing checkboxes in databound WPF treeview leafs


I have the next WPF treeview:

        <TreeView HorizontalAlignment="Left" Margin="6,0,0,32" Name="tvProductos" Width="158">
            <TreeViewItem Header="Securities" IsExpanded="True" FontWeight="Bold">
                <TreeViewItem Header="Country" Name="Country" FontWeight="Normal" />
                <TreeViewItem Header="Currency" Name="Currency" FontWeight="Normal" />
                <TreeViewItem Header="Type" Name="Type" FontWeight="Normal" />
                <TreeViewItem Header="ISIN" Name="ISIN" FontWeight="Normal" />
                <TreeViewItem Header="Description" Name="Description" FontWeight="Normal" />
            </TreeViewItem>
            <TreeViewItem Header="Issuer" IsExpanded="True" FontWeight="Bold">
                <TreeViewItem Header="Name" Name="IssuerName" FontWeight="Normal" />
                <TreeViewItem Header="Type" Name="IssuerType" FontWeight="Normal" />
                <TreeViewItem Header="Market" Name="IssuerMarket" FontWeight="Normal" />
            </TreeViewItem>
        </TreeView>

This structure is fixed. Then I bind low level TreeViewItems using code like this:

        Country.ItemsSource = (from d in db.PAISES
                               join p in db.PRODUCTOS on d.IDPAIS equals p.IDPAIS
                               select d.NOMBREPAIS).Distinct();
        Currency.ItemsSource = (from d in db.DIVISAS
                                join p in db.PRODUCTOS on d.IDDIVISA equals p.IDDIVISA
                                select d.NOMBREDIVISA).Distinct();
        Type.ItemsSource = (from d in db.TIPOSPRODUCTO
                            join p in db.PRODUCTOS on d.IDTIPOPRODUCTO equals p.IDTIPOPRODUCTO
                            select d.NOMBRETIPOPRODUCTO).Distinct();
        ...

The problem is that I need to add one checkbox on each node (low and high level). I have been looking for a solution and the best one is using HierarchicalDataTemplate. But I never found a example with fixed and dynamic nodes at same time. I tried with several examples but I couldn't solve it.

Can you help me on this?

Thank you in advance.

Kind Regards.


Solution

  • The way I'd do it is using a helper class that represents the items that can be checked (some would call it a "view model"):

    public class SelectableItem
    {
        public bool IsSelected { get; set; }
        public string Label { get; set; }
    }
    

    This allows you to specify a DataTemplate for this specific type:

    <TreeView HorizontalAlignment="Left" Margin="6,0,0,32" Name="tvProductos" Width="158">
        <TreeView.Resources>
            <DataTemplate DataType="{x:Type local:SelectableItem}" xmlns:local="clr-namespace:WpfApplication1">
                <StackPanel Orientation="Horizontal">
                    <CheckBox IsChecked="{Binding IsSelected}" />
                    <TextBlock Text="{Binding Label}" />
                </StackPanel>
            </DataTemplate>
        </TreeView.Resources>
        <TreeViewItem Header="Securities" IsExpanded="True" FontWeight="Bold">
            <TreeViewItem Header="Country" Name="Country" FontWeight="Normal" />
            <TreeViewItem Header="Currency" Name="Currency" FontWeight="Normal" />
            <TreeViewItem Header="Type" Name="Type" FontWeight="Normal" />
            <TreeViewItem Header="ISIN" Name="ISIN" FontWeight="Normal" />
            <TreeViewItem Header="Description" Name="Description" FontWeight="Normal" />
        </TreeViewItem>
        <TreeViewItem Header="Issuer" IsExpanded="True" FontWeight="Bold">
            <TreeViewItem Header="Name" Name="IssuerName" FontWeight="Normal" />
            <TreeViewItem Header="Type" Name="IssuerType" FontWeight="Normal" />
            <TreeViewItem Header="Market" Name="IssuerMarket" FontWeight="Normal" />
        </TreeViewItem>
    </TreeView>
    

    All you need to do is modify slightly your data sources:

    Country.ItemsSource = (from d in db.PAISES
                           join p in db.PRODUCTOS on d.IDPAIS equals p.IDPAIS
                           select new SelectableItem { Label = d.NOMBREPAIS }).Distinct();
    

    And that should do the trick.