将MouseEnter / MouseLeave事件与ContextMenus一起使用

时间:2013-07-03 16:08:27

标签: c# wpf events contextmenu mouseenter

ContextMenu上有一个Button,我想在MouseEnter事件中打开,而不是默认的右键单击事件。这是我的XAML:

<Button Content="Button" MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave">
    <Button.ContextMenu>
        <ContextMenu>
            .... context menu items ....
        </ContextMenu>
    </Button.ContextMenu>
</Button>

以及相关的事件处理程序:

private void Button_MouseEnter(object sender, MouseEventArgs e)
{  
    backButtonContextMenu.Placement = PlacementMode.Bottom;
    backButtonContextMenu.IsOpen = true;
    Console.WriteLine("MouseEnter called");
}

private void Button_MouseLeave(object sender, MouseEventArgs e)
{  
    backButtonContextMenu.IsOpen = false;
    Console.WriteLine("MouseLeave called");
}

非常直接。不幸的是,当鼠标悬停在按钮上时,这两种方法被无限调用,来回交替。以下是我的控制台中输出的示例:

MouseEnter called
MouseLeave called
MouseEnter called
MouseLeave called
MouseEnter called
MouseLeave called
MouseEnter called
MouseLeave called
  ... etc. ...

我认为这与ContextMenu在打开时窃取焦点有关,或者可能会放置某种不可见的“辅助”背景来检测何时需要关闭它。有没有人对这种行为有解释,有没有办法避免它?从UI的角度来看,这是一个非常简单的效果。

2 个答案:

答案 0 :(得分:2)

ContextMenu被调用Button松开鼠标然后关闭ContextMenu然后鼠标重新返回Button时,它进入循环cos你得到了你的循环。

你可以去:

private void Button_MouseLeave(object sender, MouseEventArgs e) {
  if (backButtonContextMenu.IsMouseOver)
    return;
  backButtonContextMenu.IsOpen = false;
  Debug.WriteLine("MouseLeave called");
}
然而,现在你遇到了不得不在另一个时刻关闭菜单的问题。

在这种情况下,当我需要一个像ContextMenu这样的菜单而不是它的默认行为时,我倾向于选择Popup

Popup

说同样的话
<StackPanel>
  <Button x:Name="button"
          Content="Button" />
  <Button x:Name="button2"
          Content="Button 2" />
  <Popup Placement="Right"
          PlacementTarget="{Binding ElementName=button}">
    <Menu>
      <MenuItem Header="AAA" />
    </Menu>
    <Popup.Style>
      <Style TargetType="{x:Type Popup}">
        <Setter Property="IsOpen"
                Value="True" />
        <Style.Triggers>
          <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
              <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                            Path=PlacementTarget.IsMouseOver}"
                          Value="False" />
              <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                            Path=IsMouseOver}"
                          Value="False" />
            </MultiDataTrigger.Conditions>
            <Setter Property="IsOpen"
                    Value="False" />
          </MultiDataTrigger>
        </Style.Triggers>
      </Style>
    </Popup.Style>
  </Popup>
</StackPanel>

您可以根据需要调整条件。

答案 1 :(得分:0)

最近我遇到了这个问题,当鼠标悬停在按钮上时,我需要一个菜单​​来打开和关闭。 Viv的出色答案(https://stackoverflow.com/a/17453601/3403999)解决了一个小问题:当鼠标从按钮切换到弹出窗口时,我注意到了闪烁的效果(反之亦然)。基本上,弹出窗口在不到一秒钟的时间内关闭并重新打开。最初,我尝试使用Horizo​​ntalOffset属性使弹出窗口重叠,但闪烁仍然存在。

我的最终解决方案是在一个非常基本的情节提要中添加Enter / Exit动作,我想与他人分享,以防其他人看到Viv的答案并试图弄清楚如何防止闪烁:

<StackPanel>
    <Button x:Name="button"
            Content="Button" />
    <Button x:Name="button2"
            Content="Button 2" />
    <Popup Placement="Right"
           PlacementTarget="{Binding ElementName=button}">
        <Menu>
            <MenuItem Header="AAA" />
        </Menu>
        <Popup.Style>
            <Style TargetType="{x:Type Popup}">
                <Setter Property="IsOpen"
                        Value="True" />
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                                         Path=PlacementTarget.IsMouseOver}"
                                       Value="False" />
                            <Condition Binding="{Binding RelativeSource={RelativeSource Self},
                                                         Path=IsMouseOver}"
                                       Value="False" />
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.EnterActions>
                            <BeginStoryboard x:Name="HidePopupStoryboard">
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen"
                                                                    Duration="0:0:0.1">
                                        <DiscreteBooleanKeyFrame KeyTime="100%"
                                                                 Value="False" />
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiDataTrigger.EnterActions>
                        <MultiDataTrigger.ExitActions>
                            <StopStoryboard BeginStoryboardName="HidePopupStoryboard" />
                        </MultiDataTrigger.ExitActions>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Popup.Style>
    </Popup>
</StackPanel>

您可以增加持续时间以使其短暂打开,但即使是1/10秒的延迟也足以防止闪烁。您甚至可以抛出双重动画,使其淡入/淡出,而不是完全打开/关闭。