Domanda

Qualcuno sa come posso forzare CanExecute ottenere chiamato su un comando personalizzato (Josh Smith di RelayCommand )?

Tipicamente, CanExecute è chiamato quando si verifica l'interazione sull'interfaccia utente. Se clicco qualcosa, i miei comandi vengono aggiornati.

Ho una situazione in cui la condizione per CanExecute è sempre acceso / spento da un timer dietro le quinte. Poiché questo non è guidato dall'interazione di utente, CanExecute non viene chiamato fino a quando l'utente interagisce con l'interfaccia utente. Il risultato finale è che il mio Button rimane abilitato / disabilitato fino a quando l'utente fa clic su di esso. Dopo il click, che viene aggiornato correttamente. A volte il Button appare abilitato, ma quando l'utente fa clic cambia per portatori di handicap, invece di cottura.

Come posso forzare un aggiornamento nel codice quando il timer modifica la proprietà che colpisce CanExecute? Ho provato a sparare PropertyChanged (INotifyPropertyChanged) sulla proprietà che colpisce CanExecute, ma che non ha aiutato.

Esempio XAML:

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

Esempio di codice dietro:

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; }
È stato utile?

Altri suggerimenti

ero consapevole di CommandManager.InvalidateRequerySuggested () molto tempo fa, e l'ho usato, ma non funzionava per me a volte. Ho finalmente capito perché questo era il caso! Anche se non si butta come alcune altre azioni, si deve chiamare sul thread principale.

La chiamata su un thread in background apparirà a lavorare, ma a volte lasciare l'interfaccia utente disabilitato. Spero davvero che questo aiuta qualcuno, e li salva le ore Ho appena sprecato.

Una soluzione per questo è vincolante IsEnabled a una proprietà:

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

e quindi implementare questa proprietà nel vostro ViewModel. Questo rende anche un po 'più facile per l'unit testing di lavorare con le proprietà anziché comandi per vedere se il comando può essere eseguito in un determinato punto del tempo.

Io, personalmente, lo trovo più comodo.

Probabilmente questa variante fa per voi:

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

Implementazione:

 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();
    }
}

Utilizzo semplice:

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}"/>

Grazie ragazzi per le punte. Ecco un po 'di codice su come schierare quella chiamata da un thread BG al thread UI:

private SynchronizationContext syncCtx; // member variable

Nel costruttore:

syncCtx = SynchronizationContext.Current;

Al thread in background, per far scattare l'requery:

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

La speranza che aiuta.

- Michael

Per aggiornare un solo GalaSoft.MvvmLight.CommandWpf.RelayCommand si potrebbe usare

mycommand.RaiseCanExecuteChanged();

e per me che ho creato un metodo di estensione:

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(); }));
        }
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top