Как использовать RX для управления доступностью команд в сложных сценариях?

0

Вопрос

Настройка

Давайте предположим следующее. У нас есть следующий теоретический класс viewmodel для приложения WPF:

public MyViewModel
{

    public MyViewModel()
    {
        // Condition under which this command may be executed is:
        // this.ActiveDocument.Highlighting.Type == Highlighting.Xml && 
        //    !this.ActiveDocument.IsReadOnly && 
        //    (this.License.Kind == LicenseKind.Full || this.License.TrialDay < 30)
        MyCommand = new Command(obj => DoSomething());
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

В дополнение:

  • Текущий класс реализует правильно INotifyPropertyChanged
  • Все классы в цепочках доступа участников реализуются должным образом INotifyPropertyChanged (например, модель представления документа, доступная из ActiveDocument собственность)
  • ActiveDocument может быть null. ActiveDocument.Highlighting также может быть нулевым.

Проблема

Я бы хотел, чтобы команда была включена только при выполнении условия в комментарии.

Опция без RX

Я написал свою собственную библиотеку для обработки таких ситуаций. Решением было бы либо:

public MyViewModel
{
    private readonly Condition commandAvailableCondition;

    public MyViewModel()
    {
        commandAvailableCondition = new LambdaCondition(this, 
            vm => m.ActiveDocument.Highlighting.Type == Highlighting.Xml && 
                !vm.ActiveDocument.IsReadOnly && 
                (vm.License.Kind == LicenseKind.Full || vm.License.TrialDay < 30),
            false);

        MyCommand = new AppCommand(obj => DoSomething(), commandAvailableCondition);
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

Или - если вы хотите, чтобы код был немного более читабельным, чтобы частичные условия можно было использовать повторно - вот так:

public MyViewModel
{
    private readonly Condition commandAvailableCondition;

    public MyViewModel()
    {
        var highlightingIsXml = new LambdaCondition(this, 
            vm => vm.ActiveDocument.Highlighting.Type == Highlighting.Xml, 
            false);
        var documentIsReadonly = new LambdaCondition(this,
            vm => vm.ActiveDocument.IsReadOnly, 
            false);
        var appIsLicensed = new LambdaCondition(this,
            vm => vm.License.Kind == LicenseKind.Full || this.License.TrialDay < 30,
            false);

        commandAvailableCondition = highlightingIsXml & !documentIsReadonly & appIsLicensed;

        MyCommand = new AppCommand(obj => DoSomething(), commandAvailableCondition);
    }

    public ICommand MyCommand { get; } 
    // (all other required properties)
}

Что моя библиотека (или, точнее, LambdaCondition класс) делает это:

  • Он отслеживает все экземпляры, реализующие INotifyPropertyChanged и обрабатывать изменения (например. когда ActiveDocument изменения или ActiveDocument.Highlighting изменения или ActiveDocument.Highlighting.Type изменения и т.д.)
  • Он отслеживает возможные nulls в пути, и в этом случае он вернет значение по умолчанию (в данном случае, false)
  • Он автоматически сообщит команде об изменениях (но только об изменениях) доступности, чтобы при необходимости можно было обновить пользовательский интерфейс.

Вопрос

Как можно было бы реализовать сценарий, описанный выше, используя System.Reactive в C#? Можно ли это сделать легко, соблюдая все требования к INotifyPropertyChanged, нули по пути и значение по умолчанию? Вы можете делать любые разумные предположения, когда это необходимо.

c# mvvm system.reactive wpf
2021-11-23 15:15:48
1

Лучший ответ

0

Структура ReactiveUI имеет ReactiveCommand класс, который использует IObservable<T> чтобы обновить статус команды (т. е. поднять CanExecuteChanged событие в ICommand).

Пожалуйста, обратитесь к документам для примера того, как использовать его для управления исполняемостью:

var canExecute = this.WhenAnyValue(
    x => x.UserName, x => x.Password,
    (userName, password) => 
        !string.IsNullOrEmpty(userName) && 
        !string.IsNullOrEmpty(password));

var command = ReactiveCommand.CreateFromTask(LogOnAsync, canExecute);
2021-11-24 14:52:33

Следует ли это за разработчиками INotifyPropertyChange в цепочке доступа к свойствам? Работает ли он также правильно, если какое-либо из свойств равно нулю? Не могли бы вы, пожалуйста, показать, как будет выглядеть мой конкретный пример при реализации в RX?
Spook

WhenAnyValue действительно выдает новое значение, когда любой из Username и Password свойства характеризует PropertyChanged событие. Каков именно ваш конкретный пример? Что ты пробовал?
mm8

Вы прочитали весь мой вопрос целиком? Я представил точное условие, за которым предполагается следить: vm => m.ActiveDocument.Highlighting.Type == Highlighting.Xml && !vm.ActiveDocument.IsReadOnly && (vm.License.Kind == LicenseKind.Full || vm.License.TrialDay < 30), Что, если, например. ActiveDocument равно нулю? Справится ли RX с этим должным образом? Я ожидаю, что в этом случае условие будет иметь значение по умолчанию (или, по крайней мере, значение false по умолчанию).
Spook

Если ActiveDocument, вы получите NullReferenceException, Это не имеет никакого отношения к RX.
mm8

В моей библиотеке я этого не сделаю. Вот, среди прочего, почему меня интересует, хорошо ли RX подходит для этой задачи.
Spook

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................