Вопрос

Кто-нибудь знает, как я могу заставить 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; }
Это было полезно?

Другие советы

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

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

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

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

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

Лично я нахожу это более удобным.

Вероятно, этот вариант вам подойдет:

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

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

private SynchronizationContext syncCtx; // member variable

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

syncCtx = SynchronizationContext.Current;

В фоновом потоке, чтобы запустить запрос:

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

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

-- Майкл

Чтобы обновить только один файл 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(); }));
        }
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top