1

I have a WPF TreeView and want to listen for doubleclicks on all TreeViewItems that have no Children. My TreeView looks like this:

simple tree view

The content of the TreeView is generated like this:

TreeViewItem tvi = new TreeViewItem();
tvi.Header = groupModel.Name;
treeView.Items.Add(tvi);

foreach(Model m in models) {
    TreeViewItem t = new TreeViewItem();
    t.Header = model.Name;
    tvi.Items.Add(t);
}

In my XAML File I have defined a EventSetter that should trigger when a TreeViewItem is clicked:

<TreeView x:Name="treeView" BorderThickness="0">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <EventSetter Event="MouseDoubleClick" Handler="OnTreeItemDoubleClick"/>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

In my Code Behind I define the Handler:

public void OnTreeItemDoubleClick(object sender, EventArgs args)
{
    TreeViewItem tvi = (TreeViewItem)sender;
}

When I doubleclick on any TreeViewItem, the Handler is triggered, but everytime the sender is TreeViewItem "Level 1". I want to get the actual TreeViewItem that was clicked. How do I accomplish this?

EDIT (workaround for now)

I have been thinking way to complicated. I just attached a Handler to the MouseDoubleClick Event when the TreeViewItem is generated:

t.MouseDoubleClick += OnTreeItemDoubleClick;

I am still interested in a solution to the described problem and want to know why the routed event is not triggered by the most nested child.

  • Are you sure? It seems to work fine for me. – AQuirky Apr 04 '17 at 11:30
  • @AQuirky yeah I am sure... – Basti Destruction Apr 04 '17 at 11:32
  • 1
    Since MouseDoubleClick is a routed event, the event handler should get invoked for each TreeViewItem in the element tree, i.e. if you double click on a child node the event handler should get invoked for the child node *and* the parent node. – mm8 Apr 04 '17 at 11:43
  • @mm8 my Handler is only invoked once. Is it a problem if the items are generated procedurally? – Basti Destruction Apr 04 '17 at 11:46
  • How do you set the ItemsSource property of or populate the TreeViewItem with items? Please edit your question and include some sample code. – mm8 Apr 04 '17 at 11:47
  • @mm8 updated my code – Basti Destruction Apr 04 '17 at 11:52
  • Not enough. How is your Model class defined and why don't you set the ItemsSource property of the TreeView to an IEnumerable instead of explicitly creating TreeViewItems? – mm8 Apr 04 '17 at 11:57
  • @mm8 This is just an abstraction of my Model. My model contains many dynamic Dictionaries and other Collections that I can not represent with an IEnumerable. – Basti Destruction Apr 04 '17 at 12:00

1 Answers1

2

I noticed that if you are not using HierarchicalDataTemplate and databinding, and instead add items in code, then the EventSetter sets the event only for the first level of the tree. I wonder why that happens (I haven't found an answer to his question myself yet).

The workaround is to just subscribe to the event in code (as you are adding the items manually anyway):

TreeViewItem tt = new TreeViewItem();
tt.Header = "some item";
tt.MouseDoubleClick += new MouseButtonEventHandler(OnTreeItemDoubleClick);

Beware you are using EventArgs in your handler, where it should be MouseButtonEventArgs.

Also, since every TreeViewItem is in the visual tree of its parent, the event will bubble down, so if you clicked the tree leaf with MouseDoubleClick assigned and it has 2 parents, then the event will be called 3 times. To only handle it once you can check its OriginalSource:

    public void OnTreeItemDoubleClick(object sender, MouseButtonEventArgs e)
    {
        TreeViewItem clickedItem = TryGetClickedItem(treeView, e);

        if (clickedItem == null || clickedItem!=sender)
            return;
        //e.Handled = true; // if you want to to cancel expanded/collapsed toggle

        Console.WriteLine(clickedItem.Header);
    }

    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;
    }
Arie
  • 5,251
  • 2
  • 33
  • 54