Pergunta

Alguém sabe como posso forçar CanExecute de ser chamado em um comando personalizado (Josh Smith de RelayCommand )?

Normalmente, CanExecute é chamado sempre que a interação ocorre na interface do usuário. Se eu clicar em algo, meus comandos são atualizados.

Eu tenho uma situação onde a condição para CanExecute está ficando ligado / desligado por um temporizador nos bastidores. Porque este não é impulsionado pela interação do usuário, CanExecute não é chamado até que o usuário interage com a interface do usuário. O resultado final é que os meus Button restos ativado / desativado até que o usuário clica sobre ele. Após o clique, ele é atualizado corretamente. Às vezes, as aparece Button habilitado, mas quando o usuário clica ele muda para pessoas com deficiência em vez de disparar.

Como posso forçar uma atualização no código quando o temporizador altera a propriedade que afeta CanExecute? Tentei disparar PropertyChanged (INotifyPropertyChanged) na propriedade que afeta CanExecute, mas isso não ajuda.

Exemplo XAML:

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

Exemplo de código por trá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; }

Outras dicas

Eu estava ciente de CommandManager.InvalidateRequerySuggested () há muito tempo, e usou-o, mas não estava funcionando para mim às vezes. Eu finalmente descobri porque este era o caso! Mesmo que ele não jogue como algumas outras ações, você tem que chamá-lo no segmento principal.

Chamando-o em uma discussão de fundo aparecerá ao trabalho, mas às vezes deixam a interface do usuário desativado. Eu realmente espero que isso ajude alguém, e os salva as horas Eu apenas desperdiçados.

Uma solução para isso é vinculativo IsEnabled a uma propriedade:

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

e, em seguida, implementar essa propriedade em seu ViewModel. Isso também faz com que seja um pouco mais fácil para o UnitTesting ao trabalho com as propriedades em vez de comandos para ver se o comando pode ser executado em um determinado ponto do tempo.

Eu, pessoalmente, achar mais conveniente.

Provavelmente esta variante vai servi-lo:

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

Implementação:

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

Usando simples:

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

Obrigado rapazes para as pontas. Aqui está um pouco de código sobre como organizar essa chamada de um segmento BG para o segmento de interface do usuário:

private SynchronizationContext syncCtx; // member variable

No construtor:

syncCtx = SynchronizationContext.Current;

Na discussão de fundo, para acionar a repetição da consulta:

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

Espero que ajude.

- Michael

Para atualizar apenas um único GalaSoft.MvvmLight.CommandWpf.RelayCommand você poderia usar

mycommand.RaiseCanExecuteChanged();

e para mim eu criei um método de extensão:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top