如何制作WPF触发器,使其对TreeViewItem的IsMouseOver不影响所有父控件?

11

我理解为什么会发生这种情况。父 TreeViewItem 的边界框包含其子项的边界框,因此当鼠标悬停在 TreeViewItem 上时,树中所有父级都会被视为鼠标悬停。除了 IsMouseOver 外,我是否应该使用其他内容?

4个回答

11

http://blogs.msdn.com/mikehillberg/archive/2006/09/21/MyTreeViewHelperIsMouseDirectlyOverItem.aspx

这个链接解决了问题,我没有尝试原始来源的想法。
  <Style TargetType="TreeViewItem">
    <Style.Triggers>
      <Trigger Property="local:MyTreeViewHelper.IsMouseDirectlyOverItem" Value="True">
        <Setter Property="Background" Value="Green" />
      </Trigger>
    </Style.Triggers>
  </Style>

其中 local:MyTreeViewHelper.IsMouseDirectlyOverItem 是附加属性

public static class MyTreeViewHelper
{
    //
    // The TreeViewItem that the mouse is currently directly over (or null).
    //
    private static TreeViewItem _currentItem = null;

    //
    // IsMouseDirectlyOverItem:  A DependencyProperty that will be true only on the 
    // TreeViewItem that the mouse is directly over.  I.e., this won't be set on that 
    // parent item.
    //
    // This is the only public member, and is read-only.
    //

    // The property key (since this is a read-only DP)
    private static readonly DependencyPropertyKey IsMouseDirectlyOverItemKey =
        DependencyProperty.RegisterAttachedReadOnly("IsMouseDirectlyOverItem",
                                            typeof(bool),
                                            typeof(MyTreeViewHelper),
                                            new FrameworkPropertyMetadata(null, new CoerceValueCallback(CalculateIsMouseDirectlyOverItem)));

    // The DP itself
    public static readonly DependencyProperty IsMouseDirectlyOverItemProperty =
        IsMouseDirectlyOverItemKey.DependencyProperty;

    // A strongly-typed getter for the property.
    public static bool GetIsMouseDirectlyOverItem(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsMouseDirectlyOverItemProperty);
    }

    // A coercion method for the property
    private static object CalculateIsMouseDirectlyOverItem(DependencyObject item, object value)
    {
        // This method is called when the IsMouseDirectlyOver property is being calculated
        // for a TreeViewItem.  

        if (item == _currentItem)
            return true;
        else
            return false;
    }

    //
    // UpdateOverItem:  A private RoutedEvent used to find the nearest encapsulating
    // TreeViewItem to the mouse's current position.
    //

    private static readonly RoutedEvent UpdateOverItemEvent = EventManager.RegisterRoutedEvent(
        "UpdateOverItem", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyTreeViewHelper));

    //
    // Class constructor
    //

    static MyTreeViewHelper()
    {
        // Get all Mouse enter/leave events for TreeViewItem.
        EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.MouseEnterEvent, new MouseEventHandler(OnMouseTransition), true);
        EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.MouseLeaveEvent, new MouseEventHandler(OnMouseTransition), true);

        // Listen for the UpdateOverItemEvent on all TreeViewItem's.
        EventManager.RegisterClassHandler(typeof(TreeViewItem), UpdateOverItemEvent, new RoutedEventHandler(OnUpdateOverItem));
    }


    //
    // OnUpdateOverItem:  This method is a listener for the UpdateOverItemEvent.  When it is received,
    // it means that the sender is the closest TreeViewItem to the mouse (closest in the sense of the tree,
    // not geographically).

    static void OnUpdateOverItem(object sender, RoutedEventArgs args)
    {
        // Mark this object as the tree view item over which the mouse
        // is currently positioned.
        _currentItem = sender as TreeViewItem;

        // Tell that item to re-calculate the IsMouseDirectlyOverItem property
        _currentItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);

        // Prevent this event from notifying other tree view items higher in the tree.
        args.Handled = true;
    }

    //
    // OnMouseTransition:  This method is a listener for both the MouseEnter event and
    // the MouseLeave event on TreeViewItems.  It updates the _currentItem, and updates
    // the IsMouseDirectlyOverItem property on the previous TreeViewItem and the new
    // TreeViewItem.

    static void OnMouseTransition(object sender, MouseEventArgs args)
    {
        lock (IsMouseDirectlyOverItemProperty)
        {
            if (_currentItem != null)
            {
                // Tell the item that previously had the mouse that it no longer does.
                DependencyObject oldItem = _currentItem;
                _currentItem = null;
                oldItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);
            }

            // Get the element that is currently under the mouse.
            IInputElement currentPosition = Mouse.DirectlyOver;

            // See if the mouse is still over something (any element, not just a tree view item).
            if (currentPosition != null)
            {
                // Yes, the mouse is over something.
                // Raise an event from that point.  If a TreeViewItem is anywhere above this point
                // in the tree, it will receive this event and update _currentItem.

                RoutedEventArgs newItemArgs = new RoutedEventArgs(UpdateOverItemEvent);
                currentPosition.RaiseEvent(newItemArgs);

            }
        }
    }
}

3
这个链接提供了一个对我非常有用的解决方案。
实际上,您可以重写ControlTemplate并为IsMouseOver Setter指定TreeViewItem的标题部分的SourceName。因此,技术上您在子TreeViewItems和父TreeViewItems上都有鼠标悬停,但触发器仅在光标悬停在标题上的项目上触发。

1
[[PART_Header]] 元素未覆盖左侧的 Expander ToggleButton,并且在 TreeView 控件的整个宽度上无效。因此,我更喜欢 Kamiikoneko 的答案。 - Kim Homann
还应该注意到,您可以将SourceName目标更改为包含您的边框的任何其他控件。对我来说,我针对围绕它的边框进行了定位,因为仅使用“PART_HEADER”无法跨越整个区域。如果您有一个可以做到这一点的边框,那么您可以选择针对它进行定位。 - Krythic
@KimHomann 请看我的评论,了解如何修复这个问题。 - Krythic

0

好的 - 我曾经遇到过你的问题,浪费了将近一天的时间来解决它... 你所需要做的就是从代码后台进行处理。 例如:

在XAML中:

<TreeViewItem Header="{Binding diskName}" 
Background="Transparent" Mouse.MouseEnter="changeBackground">

在cs文件中:
private void changeBackground(object sender, MouseEventArgs e)
{
        TreeViewItem t = (TreeViewItem)sender; 
        t.Background = (SolidColorBrush)(new BrushConverter().ConvertFrom("#CCE8FF"));
        e.Handled = false;
}

应该就可以了。祝你好运!


-1

听起来像是正确的事件。你可以做的一件事情是检查TreeViewItems的MouseOver事件以防止它发生。如果你的RoutedEventArgs.OriginalSource不等于父级,则只需返回并手动管理你的事件处理程序。

public void TreeViewItem_MouseOver(object sender, RoutedEventArgs e)
{
  if (sender != e.OriginalSource) return;
}

非常抱歉。实际上,我在写完这篇后意识到我们使用了一个覆盖了TreeViewItem类并实现MouseOver的类。如果它确实起作用了,Mahop的解决方案将变得非常简洁优雅。 - Jeff Wain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接