C# WPF Event for doubleclick on last child item in

2019-07-26 10:55发布

问题:

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

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.

回答1:

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;
    }