Search code examples
wpfstoryboardeventtrigger

Adjusting the visual state of a button when ContextMenu is open or closed in WPF app


In a WPF app I'm working on, there is a Button Style which defines it's own Template (ControlTemplate).

I handles visual updates via ControlTemplate.Triggers, like the following for IsMouseOver:

<Trigger Property="IsMouseOver" Value="True">
  <Trigger.EnterActions>
      <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation 
              Storyboard.TargetName="IsMouseOverBorder" 
              Storyboard.TargetProperty="Opacity" 
              To="1" 
              Duration="0:0:0.08" />
          </Storyboard>
      </BeginStoryboard>
  </Trigger.EnterActions>
  <Trigger.ExitActions>
      <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation 
              Storyboard.TargetName="IsMouseOverBorder" 
              Storyboard.TargetProperty="Opacity" 
              To="0" 
              Duration="0:0:0.16" />
          </Storyboard>
      </BeginStoryboard>
  </Trigger.ExitActions>
</Trigger>

I've added a context menu to some Buttons which use this style. Having done so, when right-clicking on a Button, the context menu shows, but the IsMouseOver trigger then immediately does it's Exit action. I would prefer that the IsMouseOver visual state is maintained while the context menu is open – it would look much nicer.

How can I do this?

Here's a couple of additional triggers which I thought would achieve what I want, but it doesn't. The first trigger (ContextMenuOpening) works, but the second trigger does not - the button is left in a permanent IsMouseOver looking state.

<EventTrigger RoutedEvent="ContextMenuService.ContextMenuOpening">
  <BeginStoryboard Name="ContextMenuOpeningStoryboard">
    <Storyboard>
      <DoubleAnimation 
        Storyboard.TargetName="IsMouseOverBorder" 
        Storyboard.TargetProperty="Opacity" 
        To="1" 
        Duration="0" />
    </Storyboard>
  </BeginStoryboard>
</EventTrigger>

<EventTrigger RoutedEvent="ContextMenuService.ContextMenuClosing">
  <RemoveStoryboard BeginStoryboardName="ContextMenuOpeningStoryboard" />
</EventTrigger>

Any idea why this won't work, or an alternate method that would work?


Solution

  • I got this to work using the styles as defined in my question, but I had to manually raise the ContextMenuService.ContextMenuClosingEvent event (using some reflection to do so):

    <ContextMenu Closed="ContextMenu_Closed">...</ContextMenu>
    

    Code behind:

    private void ContextMenu_Closed(object sender, RoutedEventArgs args)
    {
    
      var type = typeof(ContextMenuEventArgs);
    
      var contextMenuEventArgs = (ContextMenuEventArgs) type.Assembly.CreateInstance
      (
        type.FullName, false, BindingFlags.Instance | BindingFlags.NonPublic, null, 
        new object[] { this, false }, null, null
      );
    
      if (contextMenuEventArgs != null) 
      {
        contextMenuEventArgs.RoutedEvent = ContextMenuService.ContextMenuClosingEvent;
        _myToggleButton.RaiseEvent(contextMenuEventArgs);
      }
    
    }