声明 ReactiveCommand 后如何更新 CanExecute 值

Posted

技术标签:

【中文标题】声明 ReactiveCommand 后如何更新 CanExecute 值【英文标题】:How do you update the CanExecute value after the ReactiveCommand has been declared 【发布时间】:2020-02-17 03:33:18 【问题描述】:

我正在使用 ReactiveUIAvaloniaUI,并且有一个带有多个 ReactiveCommands 的 ViewModel,即扫描、加载和运行。

Observable<string> 更新时调用扫描(当我从扫描仪收到条形码时)。

从扫描命令中触发加载。

从 UI 上的按钮触发运行。

下面的简化代码:

var canRun = Events.ToObservableChangeSet().AutoRefresh().ToCollection().Select(x => x.Any());
Run = ReactiveCommand.CreateFromTask<bool>(EventSuite.RunAsync, canRun);

var canLoad = Run.IsExecuting.Select(x => x == false);
var Load = ReactiveCommand.CreateFromTask<string, Unit>(async (barcode) =>
    
        //await - go off and load Events.
    , canLoad);

var canReceiveScan = Load.IsExecuting.Select(x => x == false)
        .Merge(Run.IsExecuting.Select(x => x == false));
var Scan = ReactiveCommand.CreateFromTask<string, Unit>(async (barcode) => 
    
        //do some validation stuff
        await Load.Execute(barcode)
    , canReceiveScan);

Barcode
   .SubscribeOn(RxApp.TaskpoolScheduler)
   .ObserveOn(RxApp.MainThreadScheduler)
   .InvokeCommand(Scan);

每个命令只有在没有其他命令正在运行(包括它自己)时才能执行。但是我不能在声明之前引用命令的IsExecuting 属性。所以我一直在尝试像这样合并“CanExecute”可观察变量:

canRun = canRun
   .Merge(Run.IsExecuting.Select(x => x == false))
   .Merge(Load.IsExecuting.Select(x => x == false))
   .Merge(Scan.IsExecuting.Select(x => x == false))
   .ObserveOn(RxApp.MainThreadScheduler);

// same for canLoad and canScan

我遇到的问题是,当另一个命令正在执行时,ReactiveCommand 将继续执行。

有没有更好/正确的方法来实现这个?

【问题讨论】:

【参考方案1】:

但我不能在声明之前引用命令的 IsExecuting 属性。

一种选择是使用Subject&lt;T&gt;,将其作为canExecute: 参数传递给命令,然后在Subject&lt;T&gt; 上使用OnNext 发出新值。

另一种选择是使用WhenAnyObservable

this.WhenAnyObservable(x => x.Run.IsExecuting)
    // Here we get IObservable<bool>,
    // representing the current execution 
    // state of the command.
    .Select(executing => !executing)

然后,您可以将Merge 运算符应用于WhenAnyObservable 生成的observables。要跳过初始空值(如果有),请使用 Where 运算符或 .Skip(1)

【讨论】:

【参考方案2】:

举一个Artyom的答案中描述的Subject&lt;T&gt;选项的例子,这里是受Kent Boogaart的book p的启发。 82:

var canRun = new BehaviorSubject<bool>(true);

Run = ReactiveCommand.Create...(..., canExecute: canRun);
Load = ReactiveCommand.Create...(..., canExecute: canRun);
Scan = ReactiveCommand.Create...(..., canExecute: canRun);

Observable.Merge(Run.IsExecuting, Load.IsExecuting, Scan.IsExecuting)
    .Select(executing => !executing).Subscribe(canRun);

【讨论】:

以上是关于声明 ReactiveCommand 后如何更新 CanExecute 值的主要内容,如果未能解决你的问题,请参考以下文章

更新架构声明后,Mongoose 不会更新新字段

宝石更新后:测试失败,“资产未声明为在生产中预编译”

如何强制Azure Active Directory身份验证服务重新发出带有更新声明的id_token?

svn更新未声明标识符后的S3 TransferManager

通过 AppStore 更新后如何测试对钥匙串属性的访问?

如何在db更新后获取SQLAlchemy ORM对象的先前状态?