tcables
tcables

Reputation: 1307

WPF Double Click TreeviewItem Child Node

I have a treeview Item as such in a treeview that will have a list bound to it:

        <TreeViewItem Name="tviOffline" Foreground="Red" FontWeight="Bold"
                      Header="Offline">
            <TreeViewItem.ItemTemplate>
                <DataTemplate DataType="{x:Type local:Buddy}">
                    <StackPanel>
                        <TextBlock Text="{Binding Nick}" FontSize="10" Foreground="#8CFFD528" />
                    </StackPanel>
                </DataTemplate>
            </TreeViewItem.ItemTemplate>
        </TreeViewItem>

I cannot figure out how to get each of its childs to have a double click event.

any help is appreciated. thanks much.

Upvotes: 14

Views: 25475

Answers (4)

Maxence
Maxence

Reputation: 13296

The WPF TreeView is a really tricky control.

You need to set the event handler with a style:

<TreeView>
  <TreeView.Resources>
    <Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}"> 
      <EventSetter Event="MouseDoubleClick" Handler="TreeViewItem_OnMouseDoubleClick"/>
    </Style>
   </TreeView.Resources>
</TreeView>

By putting the style in the resources of the TreeView, the style will be applied to all the items (and not only to the first level like when you use ItemContainerStyle).

If the user clicks on a child, the event will also be raised on all the parents because the child is inside its parent (it's not bubbling, the double click event is handled by the TreeViewItem so it does not go up in the hierarchy ; setting Handled in your event handler does not change anything here). So you have to filter the event in your handler to only do work when the element which has been clicked (OriginalSource) is in the TreeViewItem which has raised the event.

void TreeViewItem_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    var senderTreeViewItem = (TreeViewItem)sender;
    TreeViewItem treeViewItemWhichHasBeenClicked = ((DependencyObject)e.OriginalSource!).GetVisualParentOrDefault<TreeViewItem>()!;
    if (senderTreeViewItem == treeViewItemWhichHasBeenClicked)
    {
        Debug.WriteLine("Do stuff");
    }
}

static class DependencyObjectExtensions
{
    public static T? GetVisualParentOrDefault<T>(this DependencyObject depObj) where T : DependencyObject
    {
        while (true)
        {
            ArgumentNullException.ThrowIfNull(depObj);

            DependencyObject? parent = VisualTreeHelper.GetParent(depObj);
            switch (parent)
            {
                case null:
                    return null;

                case T result:
                    return result;

                default:
                    depObj = parent;
                    break;
            }
        }
    }
}

Upvotes: 0

ezolotko
ezolotko

Reputation: 1783

This is the only way I managed to get it to work for all the cases:

    void MyTreeView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var clickedItem = TryGetClickedItem(myTreeView, e);
        if (clickedItem == null)
            return;

        e.Handled = true; // to cancel expanded/collapsed toggle
        DoStuff(clickedItem);
    }

    TreeViewItem TryGetClickedItem(TreeView treeView, MouseButtonEventArgs e)
    {
        var hit = e.OriginalSource as DependencyObject;
        while (hit != null && !(hit is TreeViewItem))
            hit = VisualTreeHelper.GetParent(hit);

        return hit as TreeViewItem;
    }

Upvotes: 5

Robin Davies
Robin Davies

Reputation: 7817

<TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
        <EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
        ...

And THEN, the handler has to be written as follows in order to prevent the double-click from firing on successive parent TreeViewItems:

   private void OnItemMouseDoubleClick(object sender, MouseButtonEventArgs args)
    {
        if (sender is TreeViewItem)
        {
            if (!((TreeViewItem)sender).IsSelected)
            {
                return;
            }
        }

        .... do stuff.

    }

Thanks to Aurelien Ribon for getting 90% of the way there. The double-click problem seems to be well-known in other postings on Stack Exchange. Just consolidating both solutions into one answer.

Upvotes: 20

Aurelien Ribon
Aurelien Ribon

Reputation: 7634

<TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
        <EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
        ...

Upvotes: 30

Related Questions