Кнопка остается отключенной после включения ее из потока, пока я не сфокусируюсь - PullRequest
0 голосов
/ 07 февраля 2020

У меня есть кнопка, которая запускает фоновое задание. Он связан с командой, которая изменит свойство, чтобы отключить его, пока задача не будет завершена. Задача завершится и перейдет в свойство обратно в continueWith. Кнопка будет отключена до тех пор, пока я не нажму в любом месте окна.

Вот код:

<Button Command="{Binding StartCommand}"
        Content="Run"/>
public bool IsRunning
{
      get => _IsRunning;
      set
      {
            if (_IsRunning == value)
            {
                  return;
            }

            _IsRunning = value;
            NotifyPropertyChanged();
      }
}

private ICommand _StartCommand;
public ICommand StartCommand => _StartCommand ??
(_StartCommand = new RelayCommand(x =>
{
       IsRunning = true;

       var task = Task.Run(() => Thread.Sleep(1000));

       task.ContinueWith(t => { IsRunning = false; });

 }, x => !IsRunning));


public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Добавление Application.Current.Dispatcher.Invoke () не помогло, так как хорошо.

Я хотел бы понять, что идет не так, и иметь решение без кода.

Спасибо!

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

Вы можете попытаться использовать CommandManager.InvalidateRequerySuggested();, чтобы сделать недействительными привязки команд.

Другой параметр - установить Binding для IsEnabled свойство Button для инвертированного IsRunning значения и избавиться от CanExecute предикат в RelayCommand

private ICommand _StartCommand;

public ICommand StartCommand => _StartCommand ??
                    (_StartCommand = new DelegateCommand(x =>
                    {
                        IsRunning = true;
                        var task = Task.Run(() => Thread.Sleep(1000));
                        task.ContinueWith(t =>
                        {
                            IsRunning = false;
                        });
                    }));

private bool _isRunning;

public bool IsRunning
{
    get => _isRunning;
    set
    {
        _isRunning = value;
        OnPropertyChanged(nameof(IsRunning));
    }
}

декларация xaml

<local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
...
<Button Command="{Binding StartCommand}" Content="Run" 
        IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBooleanConverter}}"/>

InverseBooleanConverter декларация

[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is bool boolValue ? !boolValue : Binding.DoNothing;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }

    #endregion
}
0 голосов
/ 07 февраля 2020

Идея в том, что мы устанавливаем CanExecute нашей команды, чтобы она возвращала, может ли команда выполняться (пример здесь !IsRunning).

мы знаем, когда изменилась возможность выполнения команды правильно (когда мы буквально устанавливаем IsRunning на новое значение), поэтому, когда это происходит, мы должны повысить ICommand.CanExecuteChanged, , потому что это именно то, что только что произошло , способность выполнения команды только что изменилась.

и вот, часть привязки данных Button.Commands - это WPF, подключающий не только предоставляемый нами метод Execute, но и метод CanExecute, и WPF будет повторно оценивать CanExecute при поднятии CanExecuteChanged .

везде, сразу после установки IsRunning, мы хотели бы поднять _StartCommand.CanExecuteChanged, вызвав на него Invoke, что можно сделать только внутри самого RelayCommand из-за устаревшего правила в C# из The event can only appear on left hand side of += or -=, поэтому я бы предложил добавить protected void RaiseCanExecuteChanged в RelayCommand, чтобы сделать это: CanExecuteChanged?.Invoke(this, EventArgs.Empty).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...