Reputation: 65
i have problems identifying the name of the selected TreeViewItem; i have the follow structure in a treeview:
+ Module 1 (name k1)
- Sub Module 1 (name s1)
+ Module 2 (name k2)
- Sub Module 2 (name s2)
+ Module 3 Name (k3)
- Sub Module 3 (name s3)
- Sub Module 4 (name s4)
Me xaml is this:
<TreeView x:Name="treview_Menu">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
In code i have this:
Private Sub OnItemMouseDoubleClick(sender As Object, args As MouseButtonEventArgs)
MsgBox(sender.name)
End Sub
in the above code always returns the name of the top node, for example, if i double click on sub_module 3 o sub_module 4 always returns the name of Module_3, same happens with sub_module 1 and sub_module 2 ... as I can fix this ?
Upvotes: 4
Views: 4917
Reputation: 1444
The issue I believe is that ItemContainerStyle
only applies to the first level of nodes (this I believe is due to the way TreeView
inherits from ItemsControl
and ItemContainerStyle
is inherited from there - where is just applies to the basic Items
of the ItemsControl
. Anyway, I'm going off on one...)
You can fix it by instead of assigning the style to ItemContainerStyle
, just move it to Resources
, like so:
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick"/>
</Style>
</TreeView.Resources>
Because this adds a trigger to every TreeViewItem - and, unfortunately, they nest like so:
<TreeViewItem Header="Module 1">
<TreeViewItem Header="Sub Module 1"/>
</TreeViewItem>
<TreeViewItem Header="Module 2">
<TreeViewItem Header="Sub Module 1"/>
<TreeViewItem Header="Sub Module 2"/>
</TreeViewItem>
So, depending on what you're actually going to use the code for, it might be a problem that the event fires on both the outer and inner module.
The usual way of dealing with this in a ClickEvent
is to set the args.Handled
to be True
in the code, which stops the event 'bubbling' up to the higher levels. But unfortunately this doesn't work on MouseDoubleClick
events, due to the way they're triggered. (Nice one Microsoft... xD)
A possible answer instead is here: https://stackoverflow.com/a/6326181/3940783
Basically, we dispense with the MouseDoubleClick
event, and instead use the PreviewMouseLeftButtonDown
event (which Microsoft uses to trigger the MouseDoubleClick
event apparently). Anyway, we can get it to fire once like so:
(Please excuse the C#, I tried to answer this from a wpf perspective, but I don't know the equivalent VB code)
OnItemPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
if (e.ClickCount == 2) {
e.Handled = true;
var treeViewItem = sender as TreeViewItem;
MessageBox.Show(treeViewItem.Header.ToString());
}
}
HOWEVER, because a Preview event tunnels down, it unfortunately fires once on the containing item, not the contained item, so we're back to square 1. We could do some looking into the args.OriginalSource
here but we're basically getting to the stage where a different solution is advisable:
A separate alternative is to just add an event listener to the whole TreeView
on its double click event, which then simply finds in which TreeViewItem
its OriginalSource
is located. (If you want to handle it and stop it propogating / causing other effects, you will need to instead hook onto the PreviewMouseLeftButtonDown
event as above instead).
To do so, you can take the MouseButtonEventArgs args
and consider args.OriginalSource
(this is the element top-most on the visual tree that you clicked on) and then you have a few cases: either it's not inside any TreeViewItem
at all, or it is itself the TreeViewItem
you require, or the third possibility is that the OriginalSource is an element somewhere inside the TreeViewItem
you require (this is the most likely case - from testing, you usually click on a TextBlock
contained within a TreeViewItem
), in which case you can recursively find parents till you hit the first parent of type TreeViewItem
. (You can do this using the VisualTreeHelper
GetParent
method.)
Basically, the above algorithm can be summed up as just recursively check the current item to see if it's of type TreeViewItem
or TreeView
else you make the current item its parent and recurse... If you end on TreeViewItem
, you have the required item, else if you clicked outside an item, you will end up on TreeView
.
PS: As I can't write VB, I think explaining the above algorithm's probably better than attempting to write it - but I hope the above explanations will still be useful to you :)
Upvotes: 5