Pregunta

¿Alguien sabe cómo puedo forzar CanExecute a ser llamado en un comando personalizado (Josh RelayCommand)?

Típicamente, CanExecute es llamada cuando se produce la interacción en la interfaz de usuario. Si hago clic en algo, mis órdenes son actualizados.

Tengo una situación en la que la condición para CanExecute se está encendido / apagado por un temporizador detrás de las escenas. Debido a que este no es impulsado por la interacción del usuario, CanExecute no se llama hasta que el usuario interactúa con la interfaz de usuario. El resultado final es que mi Button permanece activado / desactivado hasta que el usuario hace clic en él. Después del clic, se actualiza correctamente. A veces el Button aparece activado, pero cuando el usuario hace clic se cambia a discapacitados en vez de disparar.

¿Cómo puedo forzar una actualización de código cuando el temporizador cambia la propiedad que afecta CanExecute? He intentado disparar PropertyChanged (INotifyPropertyChanged) en la propiedad que afecta CanExecute, pero eso no ayuda.

Ejemplo XAML:

<Button Content="Button" Command="{Binding Cmd}"/>

código Ejemplo detrás:

private ICommand m_cmd;
public ICommand Cmd
{
    if (m_cmd == null)
        m_cmd = new RelayCommand(
            (param) => Process(),
            (param) => EnableButton);

    return m_cmd;
}

// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
¿Fue útil?

Otros consejos

Yo era consciente de CommandManager.InvalidateRequerySuggested atrás () desde hace mucho tiempo, y lo utilizó, pero no estaba trabajando para mí a veces. Finalmente me di cuenta de por qué este es el caso! A pesar de que no tira al igual que algunas otras acciones, se tiene que llamar en el hilo principal.

Llamada en un subproceso de fondo aparecerá a trabajar, pero a veces salir de la interfaz de usuario deshabilitado. Realmente espero que esto ayude a alguien, y las guarda las horas que acaba de perder.

Una solución para que sea vinculante IsEnabled a una propiedad:

<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>

y luego implementar esta propiedad en su modelo de vista. Esto también hace que sea un poco más fácil para la prueba unitaria para trabajar con las propiedades en lugar de comandos para ver si el comando se puede ejecutar en un momento determinado de tiempo.

Yo, personalmente, resulta más conveniente.

Es probable que esta variante se adapte a usted:

 public interface IRelayCommand : ICommand
{
    void UpdateCanExecuteState();
}

Implementación:

 public class RelayCommand : IRelayCommand
{
    public event EventHandler CanExecuteChanged;


    readonly Predicate<Object> _canExecute = null;
    readonly Action<Object> _executeAction = null;

   public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
    {
        _canExecute = canExecute;
        _executeAction = executeAction;
    }


    public bool CanExecute(object parameter)
    {
       if (_canExecute != null)
            return _canExecute(parameter);
        return true;
    }

    public void UpdateCanExecuteState()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, new EventArgs());
    }



    public void Execute(object parameter)
    {
        if (_executeAction != null)
            _executeAction(parameter);
        UpdateCanExecuteState();
    }
}

Uso sencillo:

public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);

 protected override bool CanEditCommandExecuted(object obj)
    {
        return SelectedItem != null ;
    }

    protected override void EditCommandExecuted(object obj)
    {
        // Do something
    }

   ...

    public TEntity SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;

            //Refresh can execute
            EditCommand.UpdateCanExecuteState();

            RaisePropertyChanged(() => SelectedItem);
        }
    }

XAML:

<Button Content="Edit" Command="{Binding EditCommand}"/>

Gracias a todos por los consejos. Aquí hay un poco de código en la forma de reunir esa llamada desde un hilo BG al hilo de interfaz de usuario:

private SynchronizationContext syncCtx; // member variable

En el constructor:

syncCtx = SynchronizationContext.Current;

En el subproceso de fondo, para activar la nueva consulta:

syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );

Espero que ayude.

- Michael

Para actualizar una única GalaSoft.MvvmLight.CommandWpf.RelayCommand podría utilizar

mycommand.RaiseCanExecuteChanged();

y para mí que he creado un método de extensión:

public static class ExtensionMethods
    {
        public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
        }

        public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top