开发者

Get the correct treeview Item from WPF PreviewMouseRightButtonDown event of the TreeView Item

I have a WPF treeview with dynamic levels (TreeView Item uses a Hierachical data template which has a bindable richtextbox) for my application I need to handle TreeViewItem's Preview开发者_如何转开发MouseRightButtonDown event.

When I right click on a treenode based on the node's level this event gets hit multiple times (equal to the treeview item's level. I think this is because of the tunnelling nature of this preview events)

Can someone please help me to identify the correct hit which gives the exact treeview item I have right clicked on?


The easiest approach is to use the bubbling event MouseRightButtonDown instead of the tunneling event PreviewMouseRightButtonDown. You can mark the routed event as handled by setting the Handled property of the EventArgs to true, which will stop further event handlers from being called. That way, only the deepest TreeViewItem will receive the event.

If you can't use the Preview event, the other approach is to use the OriginalSource property from the EventArgs to find the UI element that was actually clicked. This will probably be your RichTextBox, so you will need to use a method to find a visual ancestor of type TreeViewItem. There is an example of a method to get an ancestor of a given type at http://www.wpftutorial.net/LogicalAndVisualTree.html:

public static class VisualTreeHelperExtensions
{
    public static T FindAncestor<T>(DependencyObject dependencyObject)
        where T : class
    {
        DependencyObject target = dependencyObject;
        do
        {
            target = VisualTreeHelper.GetParent(target);
        }
        while (target != null && !(target is T));
        return target as T;
    }
}

So, you could call ((DependencyObject)e.OriginalSource).FindAncestor<TreeViewItem>() to find the TreeViewItem that was clicked. If you do it this way, you should attach the event handler to the TreeView itself rather than the TreeViewItems. This will catch a click in any TreeViewItem, since they are all within the tree, but it will only be called a single time.


Edit: As you note, that method doesn't work if the target is a FrameworkContentElement because it is not a Visual. You can do something like this instead:

public static class VisualTreeHelperExtensions
{
    public static T FindAncestor<T>(object dependencyObject)
        where T : DependencyObject
    {
        var target = (DependencyObject)dependencyObject;
        do
        {
            var visualParent = target is Visual ? VisualTreeHelper.GetParent(target) : null;
            if (visualParent != null)
            {
                target = visualParent;
            }
            else
            {
                var logicalParent = LogicalTreeHelper.GetParent(target);
                if (logicalParent != null)
                {
                    target = logicalParent;
                }
                else
                {
                    return null;
                }
            }
        }
        while (!(target is T));
        return (T)target;
    }
}

Then you should be able to get the TreeViewItem from the OriginalSource by doing VisualTreeHelperExtensions.FindAncestor<TreeViewItem>(e.OriginalSource).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜