aprendiz123456789
aprendiz123456789

Reputation: 65

wpf treeviewitem mouse double click

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

Answers (1)

David E
David E

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>

Depending on what you want, this might not work as you intend...

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:

Separate Alternative

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

Related Questions