WPF TreeView HierarchicalDataTemplate get TreeViewItem
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>
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;}
}
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.
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;
    ...
 }
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);
            }
    }
}
 
         加载中,请稍侯......
 加载中,请稍侯......
      
精彩评论