Эффект наведения в ItemContainerStyle, который находится при открытии контекстного меню - PullRequest
0 голосов
/ 06 января 2012

Я создал простой раздетый стиль 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>

Однако мне также хотелось бы, чтобы цвет, установленный при наведении, остался, когда контекстное меню фактического элемента показывается щелчком правой кнопкой мыши по нему. По сути, этот вопрос похож на этот , за исключением того, что я не могу использовать (в противном случае отличный) ответ: идея состоит в том, чтобы добавить триггер для проверки, когда открыто контекстное меню:

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

Вопрос в том, какое связывающее выражение я должен ввести, чтобы выяснить, что ContextMenu.IsOpen для фактического содержимого, установленного в DataTemplate? Я пробовал все виды вещей, таких как ссылки на ContentPresenter.ContextMenu.IsOpen и т. Д., Но ничего не получалось.

Помимо использования ContextMenu.IsOpen, я уже пробовал множество комбинаций триггеров на IsSelected, триггеров событий на MouseLeave и т. Д., Но также безрезультатно. Итак, второй вопрос: если трюк с контекстным меню не работает, есть ли другой способ получить этот эффект? По сути, мне нужно представление списка, которое не поддерживает выбор любого вида, но показывает пользователю, у какого элемента находится мышь, независимо от того, частично ли оно скрывает меню или нет.

1 Ответ

0 голосов
/ 10 января 2012

Как ни странно, когда что-то кажется сложным в 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>
...