3
votes

I am using a HierarchicalDataTemplate to bind my classes to a TreeView with checkboxes. I have the code working fine and everything is displayed fine, but I'd like to be able to get a list of children of an item in my treeview.

When a checkbox is clicked, I want to be able to select the parent nodes and child nodes. If I had access to the TreeViewItem that is supposed to wrap the checkbox then I could easily do this, but the Parent property of the Checkbox is null... I can only seem to gain access to my classes that are mapped in the HierarchicalDataTemplate.

<TreeView Margin="12" Name="trv1" SelectedItemChanged="trv1_SelectedItemChanged">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type src:Location}" ItemsSource="{Binding Path=Sublocations}">
                <CheckBox Content="{Binding Name}" Tag="{Binding}" IsChecked="{Binding IsChecked}" Click="checkBox_Click"/>
            </HierarchicalDataTemplate>

            <HierarchicalDataTemplate DataType="{x:Type src:Sublocation}" ItemsSource="{Binding Path=Children}">
                <CheckBox Content="{Binding Name}" Tag="{Binding}" IsChecked="{Binding IsChecked}" Click="checkBox_Click"/>
            </HierarchicalDataTemplate>

            <DataTemplate DataType="{x:Type src:Child}">
                <CheckBox Content="{Binding Name}" Tag="{Binding}" IsChecked="{Binding IsChecked}" Click="checkBox_Click"/>
            </DataTemplate>

        </TreeView.Resources>
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected" Value="{Binding IsChecked}"/>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>
4
I'm not sure what you mean by using checkboxes. If you could give us a code example or perhaps a better explenation of what you are trying to accomplish we could help you better. You can also modify the controlTemplate of the TreeViewItem if that helps at all. I will provide an answer if I can when I understand your question better.Ingó Vals

4 Answers

1
votes

You need to add Children and Parent node in your TreeViewItem classes. You need to set Parent/Children on initialization.

<HierarchicalDataTemplate x:Key="TreeViewItem" ItemsSource="{Binding Children}">
    <CheckBox Margin="2" IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Name}" />
</HierarchicalDataTemplate>
<TreeView ItemsSource="{Binding Countries}" ItemTemplate="{StaticResource TreeViewItem}" />

In your viewModels.

public class MainPageViewModel
{
    public ObservableCollection<Country> Countries {get;set;}
}

public class Country
{
    public string Name {get; set;}
    public bool IsChecked {get;set;}
    public IEnumerable<State> Children {get; set;}
    // Do not need parent for this.
}

public class State
{
    public string Name {get; set;}
    public bool IsChecked {get; set;}
    public Country Parent {get; set;}
    public IEnumerable<City> Children {get; set;}
}

public class City
{
    public string Name {get; set;}
    public bool IsChecked {get; set;} 
    public State Parent {get; set;}
}
0
votes

I came up with a solution that may not be the optimal one, but it works. I added an EventSetter in the TreeView style and assigned a click event for TreeViewItem objects.

            <TreeView.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsSelected" Value="{Binding IsChecked}"/>
                    <EventSetter Event="Selected" Handler="tvi_Selected"/>
                </Style>
            </TreeView.ItemContainerStyle>

This way I can access the sender object, which is a TreeViewItem, and navigate through the nodes.

EDIT: This "solution" only gives me the top TreeViewItem object and not the selected one, which is a child of the object.

EDIT 2: The treeviewitems don't seem to actually let me access child treeviewitems or parent treeviewitems. I guess I was wrong.

0
votes

FYI: instead of having to get the the TreeViewItem I find it better to include a reference to the parent in your model so in your Nodes class:

Node.Parent {get{return this._parent;}}

Then when building your collection you would have to have set the values but it makes life fantastically easy thereon: imagin getting you the the parent any number of levels up:

myNode.Parent.Parent.Parent

To get your TreeViewItem: (from my answer here: How to get TreeViewItem from HierarchicalDataTemplate item?)

I had to set a the last selected TreeViewItem in the routed TreeViewItem.Selected event which bubbles up to the tree view (the TreeViewItem's themselves do not exist at design time as we are using a HierarchicalDataTemplate).

The event can be captured in XAML as so:

Then the last TreeViewItem selected can be set in the event as so:

private void TreeViewItemSelected(object sender, RoutedEventArgs e)
{
    TreeViewItem tvi = e.OriginalSource as TreeViewItem;

    // set the last tree view item selected variable which may be used elsewhere as there is no other way I have found to obtain the TreeViewItem container (may be null)
    this.lastSelectedTreeViewItem = tvi;

    ...
 }
0
votes

If you want to stick to the MVVM pattern, you should do the following: trigger a command when the selection changes (I'm talking about the TreeView.SelectedItemChanged event).

Then, use this command to update the 'SelectedItems' property in your view model.

public class MyViewModel
{
    // your view model code...
    // ........................

    // this object better be more strongly typed
    private object _mySelectedItem; 

    public object MySelectedItem
    {
        get { return _mySelectedItem; }
        set { 
              _mySelectedItem = value;

              // the following method will handle the changed item. Problem solved              
              HandleTheNewChangedItem(value);
            }
    }
}