取消并重新执行ReactiveCommand

时间:2017-03-08 08:26:52

标签: c# reactiveui

我正在努力使用ReactiveUI用例,我觉得这很简单,必须有开箱即用的#34;支持它。但我找不到它。

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

  • 用户输入搜索文本的搜索字符串TextBox
  • 结果显示的结果TextBox
  • 显示搜索正在进行的指标

搜索应该像这样:

  • 搜索字符串TextBox被限制,以便在500ms之后 不活动,启动搜索操作。
  • 每次启动新搜索时,都应取消正在进行的搜索操作。

基本上我试图扩展"令人信服的例子"在开始新命令之前取消当前正在执行的命令。

似乎很容易?是的,但我无法使用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());

上述代码适用于searchCmd.IsExecuting以外的所有方面。无论searchCmd.CanExecute的状态如何,我都会启动新搜索。这使得IsExecuting不可靠,因为它假设命令的串行操作。我无法使用InvokeCommand代替Execute,因为在搜索过程中不会启动新的搜索。

我目前有一个没有ReactiveCommand的工作解决方案。但我强烈认为应该使用ReactiveCommand以简单的方式支持这个简单的用例。我错过了什么?

1 个答案:

答案 0 :(得分:3)

AFAICT Rx7并没有真正处理这种重叠执行。所有消息最终都会通过,但不会使您的IsExecuting始终如一。 Rx6使用了飞行计数器,因此处理了重叠的执行,但Rx7将其简化为一切。最有可能的是性能和可靠性(但我只是在猜测)。因为任务不会立即取消第一个命令将在第二个命令启动后完成,这导致IsExecuting从true切换为false,从true切换为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);