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)
.