取消并重新执行ReactiveCommand

Fre*_*all 3 c# reactiveui

我正在努力解决一个 ReactiveUI 用例,我觉得它非常简单,必须有“开箱即用”的支持。但我找不到它。

该场景是一个基本的搜索界面,具有以下功能:

  • 用户在其中输入搜索文本的搜索字符串文本框
  • 显示结果的结果文本框
  • 显示搜索正在进行的指示器

搜索应该像这样工作:

  • 搜索字符串 TextBox 受到限制,因此在 500 毫秒不活动后,将启动搜索操作。
  • 每次启动新的搜索时,任何正在进行的搜索操作都应取消。

基本上,我试图扩展“令人信服的示例”,以在启动新命令之前取消当前正在执行的命令。

看起来很容易吗?是的,但是我无法使用 ReactiveCommand 来正确处理它。这就是我所拥有的:

var searchTrigger = this.WhenAnyValue(vm => vm.SearchString)
    .Throttle(TimeSpan.FromMilliseconds(500))
    .Publish().RefCount();
var searchCmd = ReactiveCommand.CreateFromObservable(
    () => Observable
        .StartAsync(ct => CancellableSearch(SearchString, ct))
        .TakeUntil(searchTrigger));
searchCmd.ToPropertyEx(this, vm => vm.Result);
searchCmd.IsExecuting.ToPropertyEx(this, vm => vm.IsSearching);
searchTrigger.Subscribe(_ => searchCmd.Execute(Unit.Default).Subscribe());
Run Code Online (Sandbox Code Playgroud)

上面的代码适用于除searchCmd.IsExecuting. 无论 的状态如何,我都会开始新的搜索searchCmd.CanExecute。这使得IsExecuting不可靠,因为它假设命令的串行操作。而且我不能使用InvokeCommand代替,Execute因为当搜索正在进行时,新的搜索将不会启动。

我目前有一个没有ReactiveCommand. 但我有一种强烈的感觉,这个简单的用例应该使用ReactiveCommand. 我缺少什么?

Sha*_*lle 7

AFAICT Rx7 并不能真正处理这种重叠执行。所有消息最终都会通过,但不会使 IsExecuting 始终为 true。Rx6 使用飞行计数器,因此可以处理重叠执行,但 Rx7 完全简化了它。最有可能是为了性能和可靠性(但我只是猜测)。因为任务不会立即取消,所以第一个命令将在第二个命令启动后完成,这会导致 IsExecuting 从 true 到 false 切换到 true 到 false。但是,当消息赶上时,从假到真再到假的中间转换会立即发生。我知道您说过您有一个非反应性命令正在工作,但我认为这是一个可以通过等待第一个命令完成或完成取消来与反应性命令一起使用的版本。等到任务实际取消的一个好处是,您可以放心,您不会把两只手放在饼干罐里:-)这对于您的情况可能并不重要,但在某些情况下可能会很好。

//Fires an event right away so search is cancelled faster
var searchEntered = this.WhenAnyValue(vm => vm.SearchString)
    .Where(x => !String.IsNullOrWhiteSpace(x))
    .Publish()
    .RefCount();



ReactiveCommand<string, string> searchCmd = ReactiveCommand.CreateFromObservable<string, string>(
    (searchString) => Observable.StartAsync(ct => CancellableSearch(SearchString, ct))
                    .TakeUntil(searchEntered));

//if triggered wait for IsExecuting to transition back to false before firing command again
var searchTrigger = 
    searchEntered
        .Throttle(TimeSpan.FromMilliseconds(500))
        .Select(searchString => searchCmd.IsExecuting.Where(e => !e).Take(1).Select(_ => searchString))
        .Publish()
        .RefCount();

_IsSearching =
    searchCmd.IsExecuting
    .ToProperty(this, vm => vm.IsSearching);


searchTrigger
    .Switch()
    .InvokeCommand(searchCmd);
Run Code Online (Sandbox Code Playgroud)