ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

解决WPF中ContextMenu绑定RoutedCommand时第一次无法执行的问题 Original yycoding Friday, April 15, 2022 China Standard

2022-07-26 14:38:15  阅读:156  来源: 互联网

标签:绑定 15 ContextMenu Button Friday 命令 菜单 窗体 Time


WPF中RoutedCommand是一个非常实用的功能,它能够将某一命令绑定到多个控件上,比如同一个命令可以绑定到Button,Menu,ContextMenu上,这样不但可以避免重复,还可以统一行为。

    但初次使用RoutedCommand时我就遇到了一个很奇怪的Bug,就是将右键弹出菜单ContextMenu的某一个菜单MenuItem和窗体上的某个Button同时绑定到了某一个命令RoutedUICommand上。当程序初次运行时,ContextMenu里面的绑定了命令的菜单是灰色的,不可用,即使设置IsEnable=true,也不行。而Button却是正常的,但在点击Button执行一次命令后,ContextMenu里面的菜单就变得可用了。

    在一顿搜索之后发现了这篇文章How to Solve Execution Problems of RoutedCommands in a WPF ContextMenu,完美的解决了这一问题。

问题


     其实这个问题在上面这个链接里说的非常清楚,我这里还是以NTP时间同步这个应用的代码来说明一下情况。在app.xaml的Resouce中我定义了一个RoutedUICommand,Key为"SyncNow",表示用来同步时间:

<Application  x:Class="NTPClock.App"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      Startup="AppOnStartup"
      StartupUri="MainWindow.xaml" >
    <Application.Resources>
        <ResourceDictionary>
            <RoutedUICommand x:Key="SyncNow" />
        </ResourceDictionary>
    </Application.Resources>
</Application>

    紧接着在MainWindow中定义了这个命令的执行委托:

<Window.CommandBindings>
    <CommandBinding Command="{StaticResource SyncNow}"  Executed="CommandBinding_Executed"/>
</Window.CommandBindings>

    紧接着定义了一个ContextMenu里面添加了一个MenuItem,绑定了这个命令。

<Grid.ContextMenu>
    <ContextMenu Name="gridContextMenu">
        <MenuItem Header="{DynamicResource S.TimeSetting.SyncNow}"  Command="{StaticResource SyncNow}"  />
    </ContextMenu>
</Grid.ContextMenu>

     在窗体的Button上也绑定了这个方法:

<Button VerticalAlignment="Center" Name="btnSync"  Command="{StaticResource SyncNow}"  />

原因


    原因在于ContextMenu是一个独立的窗体,它有自己的视觉树和逻辑树。CommandManager会在当前聚焦的范围(focus scope)内查找绑定,如果当前的聚焦范围内没有命令绑定,它会在其父聚焦范围内查找。当应用程序启动时,主窗体的聚焦范围还没有被设定。调用FocusManager.GetFocusElement(this)可以看到他会返回null。

解决方法


     最简单的解决方法是,在程序启动后,在构造函数里调用Focus方法,手动设置聚焦范围。

public MainWindow()
{
    InitializeComponent();
    this.Closed += delegate { Application.Current.Shutdown(); };
    Focus();
}

    或者在xaml里面 定义:

<controls:MetroWindow x:Class="NTPClock.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:NTPClock"
        mc:Ignorable="d"
        Icon="NTPClock.ico"
        ShowIconOnTitleBar="True"
        Title="{DynamicResource S.Title}" 
        FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}"
        Height="450"  Width="550" ResizeMode="CanMinimize" Loaded="MainWindow_OnLoaded">
</controls:MetroWindow>

     这样,CommandManager就能在其父窗体控件的聚焦范围内找到对应的命令绑定。

     还有一种方法是设置CommandTarget对象:

<MenuItem Header="{DynamicResource S.TimeSetting.SyncNow}"  Command="{StaticResource SyncNow}" CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ContextMenu}}}" >

待解决的问题


    在我的程序中,在使用handycontrol添加了一个托盘菜单,托盘菜单里也有一个同步菜单,这个菜单目前还无法绑定到SyncNow命令上,参考开源项目GifRecorder,可能解决的方法是把这些命令放到ViewModel里,然后设置DataContext,就能解决吧。目前还是直接使用Click事件来处理的,不够优雅。

总结


    由于ContextMenu跟窗体都有自己独立的视觉树和逻辑树,所以在将ContextMenu与RoutedCommand绑定时,第一次运行时,由于主窗体的聚焦范围(focus scope)没有被设定,导致CommandManager找不到对应的绑定命令,从而使得ContextMenu里面的MenuItem不可用,这是一个非常常见的问题。解决方法是在主窗体的构造函数里调用Focus方法,或者手动设置MenuItem的CommandTarget属性。

 转载:https://www.yycoding.xyz/post/2022/4/15/solve-execution-problems-of-contextmenu-binding-routedcommands-in-wpf

标签:绑定,15,ContextMenu,Button,Friday,命令,菜单,窗体,Time
来源: https://www.cnblogs.com/cdaniu/p/16520860.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有