在上下文菜单打开时驻留的ItemContainerStyle中的悬停效果

时间:2012-01-06 11:47:52

标签: wpf listview hover mouseover

我创建了一个简单的剥离ListView样式,在IsMouseOver属性为true时突出显示一个元素。这是通过在ItemContainerStyle中触发来完成的。这很好用,xaml是这样的:

<ListView>

  <ListView.ItemTemplate>
    <DataTemplate>
      <!--UserControl with actual content goes here-->
     </DataTemplate>
   </ListView.ItemTemplate>

   <ListView.ItemContainerStyle>
     <Style TargetType="{x:Type ListViewItem}">

       <Setter Property="Template">
         <Setter.Value>
           <ControlTemplate TargetType="ListViewItem">
             <!--here is a  border with the ContentPresenter inside-->
           </ControlTemplate>
         </Setter.Value>
       </Setter>

       <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
           <Setter Property="Background" Value="Lime"/>                                         
         </Trigger>
      </Style.Triggers>

    </Style>
  <ListView.ItemContainerStyle>
</ListView>

但是我还希望通过右键单击显示实际元素的contextmenu时,悬停时停留的颜色会停留。基本上问题就像this one,除了我不能在那里使用(否则很好)答案:想法是添加一个触发器来检查上下文菜单何时打开:

<DataTrigger Binding="{Binding ContextMenu.IsOpen}" Value="True">
  <Setter Property="Background" Value="Lime"/>
</DataTrigger>

问题是:我输入什么绑定表达式,以便在DataTemplate中设置实际内容ContextMenu.IsOpen?我尝试了所有类似的东西,比如引用ContentPresenter.ContextMenu.IsOpen等,但都没有用。

除了使用ContextMenu.IsOpen之外,我已经在IsSelected上尝试了大量的触发器组合,在MouseLeave等上尝试了事件触发器,但也无济于事。所以第二个问题是:如果contextmenu技巧不起作用,还有另一种方法来实现这种效果吗?基本上我想要一个不支持选择任何类型的列表视图,但确实向用户显示鼠标所在的元素,无论菜单是否部分隐藏它。

1 个答案:

答案 0 :(得分:0)

当xaml中某些东西看起来很难时,这是完全可以解决的,使用附加属性并具有可重复性的额外奖励。基本原则是将行为附加到FrameworkElement,并挂钩它的MouseEneter/Leave事件。除此之外,还要寻找具有上下文菜单并挂钩ContextMenuOpening/Closing事件的任何孩子。我没有博客或存储库,所以这里是代码,我想这对其他人也很有用。

public static class HasMouseOver
{
  private static readonly DependencyProperty HasMouseOverBehaviorProperty =        DependencyProperty.RegisterAttached(
          "HasMouseOverBehavior", typeof( HasMouseOverBehavior ),
          typeof( FrameworkElement ), null );

  private static void AttachBehavior( FrameworkElement target )
  {
    var behavior = target.GetValue( HasMouseOverBehaviorProperty ) as HasMouseOverBehavior;
    if( behavior == null )
    {
      behavior = new HasMouseOverBehavior( target, HasMouseProperty );
      target.SetValue( HasMouseOverBehaviorProperty, behavior );
    }
  }

  private static void DetachBehavior( FrameworkElement target )
  {
    target.ClearValue( HasMouseOverBehaviorProperty );
  }

  public static readonly DependencyProperty RegisterProperty = DependencyProperty.RegisterAttached(
          "Register", typeof( bool ),
          typeof( HasMouseOver ), new PropertyMetadata( false, RegisterPropertyChanged ) );

  public static void SetRegister( FrameworkElement element, bool value )
  {
    element.SetValue( RegisterProperty, value );
  }    
  public static bool GetRegister( FrameworkElement element )
  {
    return (bool) element.GetValue( RegisterProperty );
  }

  private static void RegisterPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
  {
    var target = d as FrameworkElement;
    if( target == null )
      return;
    if( (bool) e.NewValue )
      AttachBehavior( target );
    else
      DetachBehavior( target );
  }

  public static readonly DependencyProperty HasMouseProperty = DependencyProperty.RegisterAttached(
      "HasMouse", typeof( bool ),
      typeof( HasMouseOver ), null );

  public static void SetHasMouse( FrameworkElement element, bool value )
  {
    element.SetValue( HasMouseProperty, value );
  }    
  public static bool GetHasMouse( FrameworkElement element )
  {
    return (bool) element.GetValue( HasMouseProperty );
  }
}

public class HasMouseOverBehavior
{
  private readonly DependencyProperty dep;
  private readonly FrameworkElement target;
  private bool isReallyLeaving;

  public HasMouseOverBehavior( FrameworkElement target, DependencyProperty dep )
  {
    this.target = target;
    this.dep = dep;
    target.Loaded += Loaded;
    target.Unloaded += Unloaded;
    target.MouseEnter += MouseEnter;
    target.MouseLeave += MouseLeave;
  }

  private void Loaded( object sender, RoutedEventArgs e )
  {
    var childrenWithMenu = target.FindChildren<FrameworkElement>( u => u.ContextMenu != null );
    foreach( var child in childrenWithMenu )
    {
      child.ContextMenuOpening += ContextMenuOpening;
      child.ContextMenuClosing += ContextMenuClosing;
    }
  }

  private void Unloaded( object sender, RoutedEventArgs e )
  {
    var childrenWithMenu = target.FindChildren<FrameworkElement>( u => u.ContextMenu != null );
    foreach( var child in childrenWithMenu )
    {
      child.ContextMenuOpening -= ContextMenuOpening;
      child.ContextMenuClosing -= ContextMenuClosing;
    }
  }

  private void ContextMenuOpening( object sender, ContextMenuEventArgs e )
  {
    isReallyLeaving = false;
  }

  private void ContextMenuClosing( object sender, ContextMenuEventArgs e )
  {
    if( !isReallyLeaving )  //else, mouse is still over element eg upon Esc.
      DoesNotHaveMouse();
  }

  private void MouseEnter( object sender, System.Windows.Input.MouseEventArgs e )
  {
    isReallyLeaving = true;
    HasMouse();
  }

  private void MouseLeave( object sender, System.Windows.Input.MouseEventArgs e )
  {
    if( isReallyLeaving )
    {
      isReallyLeaving = false;
      DoesNotHaveMouse();
    }
  }

  private void HasMouse()
  {
    target.SetValue( dep, true );
  }

  private void DoesNotHaveMouse()
  {
    target.SetValue( dep, false );
  }
}

在xaml:

<style>
  <Setter Property="behav:HasMouseOver.Register" Value="True"/>
  <Style.Triggers>
    <Trigger Property="behav:HasMouseOver.HasMouse" Value="True">
      ...
    </Trigger>
  </Style.Triggers>
</style>