ReactiveCommand.CreateAsync任务。如何使用按钮取消任务?

时间:2016-02-25 16:19:33

标签: c# .net system.reactive reactive-programming reactiveui

我有这个ReactiveCommand;

LoadFileCommand = ReactiveCommand.CreateAsyncTask((_, cancellationToken) => LoadFile(cancellationToken));

我也订阅了命令

subscription = LoadFileCommand.Subscribe(file => OnFileLoaded(file);

现在我想创建另一个从UI(在按钮中)使用的命令来取消任务。

但是如何?

我无法将cancelToken“注入”LoadFileCommand。我真的输了!

编辑:

目前,在我的MainViewModel.cs下(在构造函数中)我有这个:

OpenFileCommand = ReactiveCommand.CreateAsyncTask(async (o, ct) => await LoadFile(ct));

var whenButtonClick =
    Observable
        .Timer(TimeSpan.FromSeconds(10));
whenButtonClick.Subscribe(_ => Console.WriteLine());

OpenFileCommand
    .ExecuteAsync()
    .TakeUntil(whenButtonClick)
    .Subscribe(OnDocumentLoaded);

我的视图中有一个绑定到LoadFileCommand的“加载”按钮,但代码在创建视图模型后立即执行任务,而不是在用户单击按钮时执行。

顺便说一句,我想要另一个“取消”按钮,允许用户取消加载。

1 个答案:

答案 0 :(得分:3)

订阅LoadFileCommand不会调用该命令。在调用命令的其中一个执行方法之前,不会调用该命令。在您的情况下,您想要致电LoadFileCommand.ExecuteAsync。我相信,这会在你的情况下返回IObservable<File>。处置对该observable的订阅或以其他方式终止observable将导致observable请求取消传递给您的委托中LoadFile的取消令牌。

我尝试创建一个.NET小提琴here来演示,但它一直说组件没有被引用,即使它显然是。无论如何,如果您想要使用它,可以将相同的代码复制到LinqPad或控制台应用程序中:

var testCommand = ReactiveCommand.CreateAsyncTask(async (name, ct) =>
{
    // Do some long running work and periodically check if the
    // token has been cancelled.
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(
            "{0} cancellation requested: {1}", 
            name, 
            ct.IsCancellationRequested);

        if (ct.IsCancellationRequested)
        {
            ct.ThrowIfCancellationRequested();
        }
        await Task.Delay(1000);
    }
});

var whenButtonClick =
    Observable
    .Timer(TimeSpan.FromSeconds(2));

// Execute a command that is cancelled when a button click happens.
// Note the TakeUntil(whenButtonClick)
testCommand
.ExecuteAsync("first")
.TakeUntil(whenButtonClick)
.Subscribe(
    onNext: _ => Console.WriteLine("first next"),
    onCompleted: () => Console.WriteLine("first completed"));

// Execute a command that runs to completion.
testCommand
.ExecuteAsync("second")
.Subscribe(
    onNext: _ => Console.WriteLine("second next"),
    onCompleted: () => Console.WriteLine("second completed"));

这是上面代码的输出。您可以看到取消令牌确实请求取消:

  

首先取消请求:错误   第二次取消要求:假   第二次取消要求:假   首先取消请求:假   先完成了   第一次取消要求:正确   第二次取消要求:假   第二次取消要求:假   第二次取消要求:假   第二个下一个   第二次完成

修改 - 可能的解决方案

所以我认为我的某些东西可以在您的场景中运行,同时仍允许您使用Xaml绑定。我正在将取消逻辑推送到命令工厂方法,而不是尝试获取单个调用并取消它们。

CancelOpenFileCommand = ReactiveCommand.Create();

LoadFileCommand = 
    ReactiveCommand
    .CreateAsyncObservable(_ =>
        Observable
        .FromAsync(cancellationToken => LoadFile(cancellationToken))
        .TakeUntil(CancelOpenFileCommand));

现在,如果您将要用于打开文件的按钮绑定到LoadFileCommand,并且要用于取消CancelOpenFileCommand命令的按钮,那么一切都应该正常工作。

以下是使用我在上面描述的相同模式的示例。我用一个虚拟任务替换了LoadFile,该任务只包含循环五次,循环内部我将取消令牌的状态写入控制台,然后延迟一秒钟。因此任务应该花费五秒钟才能完成。但是我没有让它完成,而是在一秒钟之后调用CancelOpenFileCommand。这表明在调用CancelOpenFileCommand并且命令提前终止时取消了取消令牌。

var CancelOpenFileCommand = ReactiveCommand.Create();

CancelOpenFileCommand
.Subscribe(x => 
    Console
    .WriteLine(
        "{0} CancelOpenFileCommand Invoked", 
        DateTime.Now.TimeOfDay));

var LoadFile = new Func<CancellationToken, Task>(async cancellationToken =>
    {
        for (int i = 0; i < 5; i++)
        {
            Console
            .WriteLine(
                "{0} Cancellation requested: {1}", 
                DateTime.Now.TimeOfDay, 
                cancellationToken.IsCancellationRequested);             

            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }
            await Task.Delay(1000);
        }
    });

var LoadFileCommand = 
    ReactiveCommand
    .CreateAsyncObservable(
        name =>
            Observable
            .FromAsync(ct => LoadFile(ct))
            .TakeUntil(CancelOpenFileCommand));

LoadFileCommand.Execute(null);

Observable
.Timer(TimeSpan.FromSeconds(1))
.Subscribe(_ => CancelOpenFileCommand.Execute(null));

这是控制台输出:

  

19:04:57.6087252要求取消:假   19:04:58.6157828要求取消:假   19:04:58.6197830 CancelOpenFileCommand Invoked
  19:04:59.6268406要求取消:正确