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);
}
}
}
精彩评论