Обновить команду WPF - PullRequest
       11

Обновить команду WPF

69 голосов
/ 23 апреля 2009

Кто-нибудь знает, как я могу заставить CanExecute вызываться по пользовательской команде (RelayCommand Джоша Смита)?

Как правило, CanExecute вызывается всякий раз, когда происходит взаимодействие в пользовательском интерфейсе. Если я что-то нажимаю, мои команды обновляются.

У меня есть ситуация, когда условие для CanExecute включается / выключается таймером за кулисами. Поскольку это не обусловлено взаимодействием с пользователем, CanExecute не вызывается, пока пользователь не взаимодействует с пользовательским интерфейсом. Конечным результатом является то, что мой Button остается включенным / отключенным, пока пользователь не нажмет на него. После клика он обновляется корректно. Иногда Button появляется включенным, но когда пользователь щелкает, он переключается на отключенный вместо стрельбы.

Как принудительно обновить код, когда таймер изменяет свойство, влияющее на CanExecute? Я попытался запустить PropertyChanged (INotifyPropertyChanged) для свойства, которое влияет на CanExecute, но это не помогло.

Пример XAML:

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

Пример кода позади:

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

Ответы [ 6 ]

101 голосов
/ 23 апреля 2009
28 голосов
/ 23 февраля 2010

Я давно знал о CommandManager.InvalidateRequerySuggested () и использовал его, но иногда он не работал для меня. Я наконец понял, почему это так! Даже если он не генерирует какие-либо другие действия, вы должны вызывать его в главном потоке.

Вызов в фоновом потоке будет работать, но иногда оставить интерфейс отключенным. Я действительно надеюсь, что это поможет кому-то и сэкономит те часы, которые я потратил впустую.

17 голосов
/ 04 мая 2012

Обходной путь для этого - привязка IsEnabled к свойству:

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

, а затем реализуйте это свойство в вашей ViewModel. Это также облегчает UnitTesting работу со свойствами, а не с командами, чтобы увидеть, может ли команда быть выполнена в определенный момент времени.

Мне лично это удобнее.

6 голосов
/ 05 декабря 2012

Возможно вам подойдет этот вариант:

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

Реализация:

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

Использование простого:

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}"/>
4 голосов
/ 03 марта 2011

Спасибо, ребята за советы. Вот немного кода о том, как перенаправить этот вызов из потока BG в поток пользовательского интерфейса:

private SynchronizationContext syncCtx; // member variable

В конструкторе:

syncCtx = SynchronizationContext.Current;

В фоновом потоке для запуска запроса:

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

Надеюсь, это поможет.

- Майкл

0 голосов
/ 27 июня 2018

Чтобы обновить только один GalaSoft.MvvmLight.CommandWpf.RelayCommand, вы можете использовать

mycommand.RaiseCanExecuteChanged();

и для меня я создал метод расширения:

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(); }));
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...