WPF: cómo obligar a un comando a reevaluar 'CanExecute' a través de CommandBindings

130

Tengo un lugar Menudonde cada uno MenuItemen la jerarquía tiene su Commandpropiedad establecida en una RoutedCommandque he definido. El asociado CommandBindingproporciona una devolución de llamada para evaluar CanExecutequé controla el estado habilitado de cada uno MenuItem.

Esto casi funciona. Los elementos del menú aparecen inicialmente con los estados habilitados y deshabilitados correctos. Sin embargo, cuando los datos que CanExecuteutiliza mi devolución de llamada cambian, necesito el comando para volver a solicitar un resultado de mi devolución de llamada para que este nuevo estado se refleje en la interfaz de usuario.

No parece haber ninguna métodos públicos en RoutedCommando CommandBindingpara esto.

Tenga en cuenta que la devolución de llamada se usa nuevamente cuando hago clic o escribo en el control (supongo que se activa en la entrada porque el mouse-over no causa la actualización).

Drew Noakes
fuente

Respuestas:

172

No es el más bonito del libro, pero puede usar el CommandManager para invalidar todos los enlaces de comandos:

CommandManager.InvalidateRequerySuggested();

Ver más información sobre MSDN

Arcturus
fuente
1
Gracias, esto funcionó bien. Hay un ligero retraso en la interfaz de usuario, pero eso no me preocupa demasiado. Además, voté su respuesta de inmediato, luego retomé la votación para ver si funcionaba. Ahora que está funcionando, no puedo volver a aplicar la votación nuevamente. No estoy seguro de por qué SO tiene esa regla en su lugar.
Drew Noakes
55
Edité su respuesta para volver a aplicar mi voto. No cambié nada en la edición. Gracias de nuevo.
Drew Noakes
Tuve el mismo problema cuando estaba cambiando el contenido de un Texbox desde el código subyacente. Si lo edita a mano, funcionaría. En esta aplicación, tenían el texbox siendo editado por un control que aparecería, y cuando guardabas el popup, cambiaba la propiedad Texbox.Text. Esto resolvió el problema! Gracias @Arcturus
Dzyann
10
Solo
tome
84

Para cualquiera que se encuentre con esto más tarde; Si utiliza MVVM y Prism, la DelegateCommandimplementación de Prism ICommandproporciona un .RaiseCanExecuteChanged()método para hacerlo.

CodingWithSpike
fuente
12
Este patrón también se encuentra en otras bibliotecas MVVM, por ejemplo, MVVM Light.
Peter Lillevold
2
A diferencia de Prism, el código fuente de MVVM Light v5 indica que RaiseCanExecuteChanged() simplemente llama CommandManager.InvalidateRequerySuggested().
Peter
44
una nota al margen de MVVM Light en WPF, debe usar el espacio de nombres GalaSoft.MvvmLight.CommandWpf ya que GalaSoft.MvvmLight.Command causará problemas mvvmlight.net/installing/changes#v5_0_2
fuchs777
((RelayCommand)MyCommand).RaiseCanExecuteChanged();funcionó para mí, usando GalaSoft.MvvmLight.Command - PERO después de cambiar a CommandWPF, funcionó sin la necesidad de llamar a nada. Gracias @ fuchs777
Robin Bennett
1
¿Qué pasa si no estás usando una biblioteca de terceros?
Vidar
28

No pude usar CommandManager.InvalidateRequerySuggested();porque me estaba afectando el rendimiento.

He usado el comando Delegar de MVVM Helper , que se ve a continuación (lo he modificado un poco para nuestra solicitud). tienes que llamar command.RaiseCanExecuteChanged()desde VM

public event EventHandler CanExecuteChanged
{
    add
    {
        _internalCanExecuteChanged += value;
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        _internalCanExecuteChanged -= value;
        CommandManager.RequerySuggested -= value;
    }
}

/// <summary>
/// This method can be used to raise the CanExecuteChanged handler.
/// This will force WPF to re-query the status of this command directly.
/// </summary>
public void RaiseCanExecuteChanged()
{
    if (canExecute != null)
        OnCanExecuteChanged();
}

/// <summary>
/// This method is used to walk the delegate chain and well WPF that
/// our command execution status has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
    if (eCanExecuteChanged != null)
        eCanExecuteChanged(this, EventArgs.Empty);
}
Bek Raupov
fuente
3
Solo para su información, comenté CommandManager.RequerySuggested + = value; Estaba recibiendo una evaluación casi constante / en bucle de mi código CanExecute por alguna razón. De lo contrario, la solución funcionó como se esperaba. ¡Gracias!
robaudas
15

Si ha implementado su propia clase que implementa ICommand, puede perder muchas de las actualizaciones de estado automáticas que lo obligan a confiar en la actualización manual más de lo necesario. También puede romperse InvalidateRequerySuggested(). El problema es que una ICommandimplementación simple no puede vincular el nuevo comando al CommandManager.

La solución es usar lo siguiente:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

De esta forma, los suscriptores se unen a CommandManagersu clase en lugar de a su clase y pueden participar adecuadamente en los cambios de estado del comando.

Andrue Cope
fuente
2
Directo, hasta el punto, y permite a las personas tener control sobre sus implementaciones de ICommand.
Akoi Meexx
2

He implementado una solución para manejar la dependencia de la propiedad en los comandos, aquí el enlace https://stackoverflow.com/a/30394333/1716620

gracias a eso terminarás teniendo un comando como este:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
    //execute
    () => {
      Console.Write("EXECUTED");
    },
    //can execute
    () => {
      Console.Write("Checking Validity");
       return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
    },
    //properties to watch
    (p) => new { p.PropertyX, p.PropertyY }
 );
No importante
fuente
-3

Esto es lo que funcionó para mí: poner el CanExecute antes del comando en el XAML.

rmustakos
fuente