You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF鼠标悬停MenuItem后按钮Hover状态失效,窗口焦点无法恢复

问题描述

我有一个包含自定义Menu和若干Button的WPF窗口,已实现鼠标悬停MenuItem时自动展开菜单。每个Button都配置了鼠标悬停(IsMouseOver)状态样式,但在通过悬停打开菜单后,Button不再显示该悬停状态。尝试在Popup关闭时手动将焦点恢复到Window,但未生效。

简化代码如下:

<Window>
    <Button Cursor="Hand" Width="20" Height="20">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="White" />
                <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="Border" Background="{TemplateBinding Background}" CornerRadius="30">
                            <Image Source="../Icons/close.png" Width="9" Height="9" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
                </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Gray" />
                </Trigger>
            </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

    <ScrollViewer>
        <Menu ItemsSource="{Binding Path=CommandMenuItems}">
            <Menu.ItemContainerStyle>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="Focusable" Value="False"/>
                    <Setter Property="Template">
                        <Setter.Value>
                        <ControlTemplate TargetType="{x:Type MenuItem}">
                                     <Border>
                                         <Grid>
                                             <ContentPresenter />
                                             <TextBlock />
                                             <Grid />
                                             <Popup IsOpen="{Binding IsSubmenuOpen}"/>
                                         </Grid>
                                     </Border>
                                <ControlTemplate.Triggers>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsMouseOver" Value="True" />
                                            <Condition Property="Role" Value="SubmenuHeader" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="IsSubmenuOpen" Value="True" />
                                    </MultiTrigger>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsMouseOver" Value="True" />
                                            <Condition Property="Role" Value="TopLevelHeader" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="IsSubmenuOpen" Value="True" />
                                    </MultiTrigger>
                                        
                                </ControlTemplate.Triggers>

                            </ControlTemplate>
                            </Setter.Value>
                    </Setter>
            </Menu.ItemContainerStyle>
    </ScrollViewer>
</Window>
问题分析

WPF的Popup控件在打开时会自动捕获鼠标输入,确保Popup优先响应鼠标操作。当通过悬停打开子菜单Popup后,鼠标捕获被Popup持有;即使Popup关闭,若未正确释放鼠标捕获,窗口内的其他控件(如Button)将无法正常检测到IsMouseOver状态——因为鼠标输入仍被之前的捕获操作占用。手动恢复Window焦点无法解决此问题,因为焦点与鼠标捕获是两个独立的WPF输入概念。

解决方案

需要确保Popup关闭时强制释放鼠标捕获,同时优化MenuItem的触发器逻辑,保证子菜单关闭时正确清理鼠标状态。具体修改如下:

  1. 给MenuItem模板中的Popup添加StaysOpen="False",确保鼠标离开Popup区域时自动关闭它;
  2. 添加触发器,当IsSubmenuOpen变为False时,调用Mouse.Capture(null)释放全局鼠标捕获;
  3. (可选)给Button添加Focusable="False",避免焦点状态干扰悬停样式的触发。

修改后的完整代码:

<Window>
    <Button Cursor="Hand" Width="20" Height="20" Focusable="False">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="White" />
                <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="Border" Background="{TemplateBinding Background}" CornerRadius="30">
                            <Image Source="../Icons/close.png" Width="9" Height="9" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
                </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Gray" />
                </Trigger>
            </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

    <ScrollViewer>
        <Menu ItemsSource="{Binding Path=CommandMenuItems}">
            <Menu.ItemContainerStyle>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="Focusable" Value="False"/>
                    <Setter Property="Template">
                        <Setter.Value>
                        <ControlTemplate TargetType="{x:Type MenuItem}">
                                     <Border x:Name="MenuItemBorder">
                                         <Grid>
                                             <ContentPresenter />
                                             <TextBlock />
                                             <Grid />
                                             <Popup IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" 
                                                    StaysOpen="False"/>
                                         </Grid>
                                     </Border>
                                <ControlTemplate.Triggers>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsMouseOver" Value="True" />
                                            <Condition Property="Role" Value="SubmenuHeader" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="IsSubmenuOpen" Value="True" />
                                    </MultiTrigger>
                                    <MultiTrigger>
                                        <MultiTrigger.Conditions>
                                            <Condition Property="IsMouseOver" Value="True" />
                                            <Condition Property="Role" Value="TopLevelHeader" />
                                        </MultiTrigger.Conditions>
                                        <Setter Property="IsSubmenuOpen" Value="True" />
                                    </MultiTrigger>
                                    <!-- 当子菜单关闭时释放鼠标捕获 -->
                                    <Trigger Property="IsSubmenuOpen" Value="False">
                                        <EventSetter Event="Loaded" Handler="MenuItem_Loaded"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>

                            </ControlTemplate>
                            </Setter.Value>
                    </Setter>
            </Menu.ItemContainerStyle>
    </ScrollViewer>
</Window>

同时,在窗口的后台代码中添加MenuItem_Loaded事件处理,确保释放鼠标捕获:

private void MenuItem_Loaded(object sender, RoutedEventArgs e)
{
    if (sender is MenuItem menuItem && !menuItem.IsSubmenuOpen)
    {
        Mouse.Capture(null);
    }
}

内容的提问来源于stack exchange,提问作者dransom90

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 04:57:33