He determinado que hay al menos dos errores en ContextMenu que hacen que sus llamadas CanExecute no sean confiables en diferentes circunstancias. Llama a CanExecute inmediatamente cuando se establece el comando. Las llamadas posteriores son impredecibles y ciertamente no confiables.
Pasé toda una noche tratando de rastrear las condiciones precisas bajo las cuales fallaría y buscando una solución alternativa. Finalmente me rendí y cambié a los manejadores de Click que disparaban los comandos deseados.
Decidí que uno de mis problemas era que cambiar el DataContext del ContextMenu puede hacer que se llame a CanExecute antes de que se vincule el nuevo Command o CommandParameter.
La mejor solución que conozco a este problema es utilizar sus propias propiedades adjuntas para el mando y CommandBinding en lugar de utilizar el incorporado en los:
Cuando su propiedad Command adjunta se establece, inscríbase en el Los eventos Click y DataContextChanged en el MenuItem, y también se suscriben a CommandManager.RequerySuggested.
Cuando el DataContext cambia, RequerySuggested entra, o cambia cualquiera de sus dos propiedades adjuntas, programe una operación de despachador usando Dispatcher.BeginInvoke que llamará a su CanExecute() y actualizará IsEnabled en el elemento de menú.
Cuando el evento Click se dispara, haga lo de CanExecute y, si pasa, llame a Execute().
uso es igual Comando regular y CommandParameter, pero utilizando las propiedades adjuntas en su lugar:
<Setter Property="my:ContexrMenuFixer.Command" Value="{Binding}" />
<Setter Property="my:ContextMenuFixer.CommandParameter" Value="{Binding Source=... }" />
Esta solución funciona y pasa por todos los problemas con los errores en el manejo de CanExecute ContextMenu.
Esperemos que algún día Microsoft solucione los problemas con ContextMenu y esta solución ya no será necesaria. Tengo un caso de reproducción aquí en alguna parte que tengo la intención de enviar a Connect. Tal vez debería subirme al balón y hacerlo realmente.
¿Qué es RequerySuggested, y por qué usarlo?
El mecanismo RequerySuggested es la forma en que RoutedCommand maneja eficientemente ICommand.CanExecuteChanged.En el mundo no enrutado, cada ICommand tiene su propia lista de suscriptores a CanExecuteChanged, pero para RoutedCommand, cualquier cliente que se suscriba a ICommand.CanExecuteChanged se suscribirá a CommandManager.RequerySuggested. Este modelo más simple significa que cada vez CanExecute de un RoutedCommand puede cambiar, todo lo que es necesario es llamar CommandManager.InvalidateRequerySuggested(), que va a hacer las mismas cosas que disparar ICommand.CanExecuteChanged pero hacerlo para todos los RoutedCommands al mismo tiempo y en un subproceso en segundo plano. Además, las invocaciones RequerySuggested se combinan juntas de modo que si se producen muchos cambios, CanExecute solo necesita ser llamado una vez.
Las razones que recomendé se suscribe a CommandManager.RequerySuggested en lugar de ICommand.CanExecuteChanged es: 1. Usted no necesita código para eliminar su suscripción de edad y añadir uno nuevo cada vez que el valor de su propiedad adjunta Comando cambia cambios y 2. CommandManager.RequerySuggested tiene una función de referencia débil incorporada que le permite configurar su controlador de eventos y seguir siendo un elemento no deseado. Hacer lo mismo con ICommand requiere que implemente su propio mecanismo de referencia débil.
La otra cara de esto es que si se suscribe a CommandManager.RequerySuggested en lugar de ICommand.CanExecuteChanged es que solo obtendrá actualizaciones para RoutedCommands. Yo uso RoutedCommands exclusivamente así que esto no es un problema para mí, pero debería haber mencionado que si usa ICommands regulares a veces debería considerar hacer el trabajo extra de suscribirse débilmente a ICommand.CanExecutedChanged. Tenga en cuenta que si hace esto, no necesita suscribirse a RequerySuggested también, ya que RoutedCommand.add_CanExecutedChanged ya lo hace por usted.
que tenían el mismo problema. Mi solución fue enlazar el comando después del parámetro de comando simplemente colocando el setter del parámetro de comando antes del setter del comando y de repente el parámetro enlazado fue pasado a la primera llamada de 'CanExecute'. – Cubinator73