C# ReactiveCommand.CreateAsync任务。如何使用按钮取消任务?
我有这个反应命令C# ReactiveCommand.CreateAsync任务。如何使用按钮取消任务?,c#,.net,system.reactive,reactive-programming,reactiveui,C#,.net,System.reactive,Reactive Programming,Reactiveui,我有这个反应命令 LoadFileCommand = ReactiveCommand.CreateAsyncTask((_, cancellationToken) => LoadFile(cancellationToken)); 我也同意这个命令 subscription = LoadFileCommand.Subscribe(file => OnFileLoaded(file); 现在我想从UI(在按钮中)发出另一个命令来取消任务 但是怎么做呢 我无法将cancellation
LoadFileCommand = ReactiveCommand.CreateAsyncTask((_, cancellationToken) => LoadFile(cancellationToken));
我也同意这个命令
subscription = LoadFileCommand.Subscribe(file => OnFileLoaded(file);
现在我想从UI(在按钮中)发出另一个命令来取消任务
但是怎么做呢
我无法将cancellationToken“注入”到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的“Load”按钮,但是代码会在创建viewmodel后立即执行任务,而不是在用户单击按钮时执行
顺便说一下,我想有另一个“取消”按钮,允许用户取消加载。订阅
LoadFileCommand
不会调用该命令。只有在调用命令上的某个execute方法后,才会调用该命令。在您的情况下,您需要调用LoadFileCommand.ExecuteAsync
。我相信,在您的情况下,这将返回一个IObservable
。处置对该可观察对象的订阅或以其他方式终止该可观察对象将导致该可观察对象请求取消在您的代理中传递给LoadFile
的取消令牌
我试图创建一个.NETFiddle来演示,但它一直说程序集没有被引用,尽管它显然是被引用的。无论如何,如果您想使用LinqPad或控制台应用程序,可以将以下代码复制到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"));
现在,如果将用于打开文件的按钮绑定到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));
var CancelOpenFileCommand=ReactiveCommand.Create();
取消OpenFileCommand
.订阅(x=>
安慰
WriteLine先生(
“{0}已调用CancelOpenFileCommand”,
DateTime.Now.TimeOfDay);
var LoadFile=new Func(异步取消令牌=>
{
对于(int i=0;i<5;i++)
{
安慰
WriteLine先生(
“{0}请求取消:{1}”,
DateTime.Now.TimeOfDay,
cancellationToken.IsCancellationRequested);
if(cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
等待任务。延迟(1000);
}
});
var LoadFileCommand=
反应命令
.CreateAsyncObservable(
名称=>
可观察
.fromsync(ct=>LoadFile(ct))
.TakeUntil(CancelOpenFileCommand));
LoadFileCommand.Execute(空);
可观察
.计时器(从秒开始的时间跨度(1))
.Subscribe(=>CancelOpenFileCommand.Execute(null));
以下是控制台输出:
19:04:57.6087252请求取消:False19:04:58.6157828请求取消:False
19:04:58.6197830调用的CancelOpenFileCommand
19:04:59.6268406请求取消:真
这样做subscription=LoadFileCommand.subscripte(文件=>OnFileLoaded(文件)是错误的吗?我认为订阅命令是任务完成后接收结果的方式。我是否遗漏了什么?@Super否,这没有错。您可以在
LoadFileCommand
observable上获得命令调用的结果。如果您想取消命令,我知道的唯一方法是捕获ob当您调用命令并终止该可观察对象时,servable返回。我不知道有什么方法可以使用该命令可观察对象执行此操作。这是有意义的,如果这是一个长时间运行的任务,该命令可以被多次额外调用,则取消单个命令调用的唯一方法是使用一些表示该命令调用的对象。我有点困惑:(我正在做的是将一个按钮绑定到LoadCommand.In MainViewModel(在ctor中)。如果我将行testCommand.ExecuteAsync(“第一个”)
即使未单击按钮,它也会引发任务。我希望用户通过单击一个按钮来引发任务,并允许用户使用另一个按钮取消任务。请查看我在原始消息中发布的编辑内容。@SuperJMN抱歉,我试图用我的代码演示如何取消任务调用命令后,将其绑定。我不是建议将代码合并到您的项目中以解决您的问题。您能告诉我将按钮绑定到OpenFileCommand
的位置吗?我使用XAML中的绑定绑定命令,就像我认为使用XAML绑定不会有任何结果一样。这将依赖于ICommand
接口来调用Execute
,该接口返回void。您需要调用ExecuteAsync
,并保留它返回的可观察值,以便可以取消它。我将尝试组合一些可能适用于您的情况的内容。糟糕!希望您能为我提供一些解决方法ound可以满足我的需要。几乎没有关于取消场景的在线文档。感谢您的努力!我想我有一个可能的解决方案给您。看看我更新的答案。
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));