【问题标题】:Using MouseEnter/MouseLeave events with ContextMenus使用带有 ContextMenus 的 MouseEnter/MouseLeave 事件
【发布时间】:2019-06-25 21:55:02
【问题描述】:

我在Button 上有一个ContextMenu,我想在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 的角度来看,这是一个非常简单的效果。

【问题讨论】:

  • 在 mouseEnter 上,您应该检查 ContextMenu 是否打开。如果它已经打开,则不应再次打开它。

标签: c# wpf events contextmenu mouseenter


【解决方案1】:

ContextMenu被调用时,它进入一个循环,Button松开鼠标然后关闭ContextMenu,现在鼠标再次回到Button,你得到了你的循环。

你可以去:

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>

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

【讨论】:

  • 谢谢,我最终切换到了Popup。使用起来更容易。
【解决方案2】:

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

我的最终解决方案是使用一个非常基本的故事板添加进入/退出操作,我想我会分享它以防其他人看到 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 秒的延迟也足以防止我闪烁。你甚至可以加入一个双重动画,让它淡出/淡入,而不是直接打开/关闭。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多