2016-02-25 42 views
2

我有這個ReactiveCommand;ReactiveCommand.CreateAsync任務。我如何使用按鈕取消任務?

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); 

我在查看了 「Load」 按鈕綁定到LoadFileCommand,但代碼在創建視圖模型後立即執行任務,而不是在用戶單擊按鈕時執行任務。

順便說一句,我希望能有另一個「取消」按鈕,允許用戶取消裝載。

+0

你能告訴我你在哪裏綁定您的BU tton到'OpenFileCommand'? –

+0

我使用XAML中的綁定綁定到命令,如

+0

我不認爲你會得到與XAML綁定的任何地方。這將依賴於'ICommand'接口來調用'Execute',它返回void。你需要調用ExecuteAsync並掛在它返回的observable上,以便你可以取消它。我會盡量把一些可能適合你的情況扔在一起。 –

回答

2

訂閱的LoadFileCommand不會調用命令。直到您調用命令中的某個執行方法之後,纔會調用該命令。在你的情況下,你想打電話給LoadFileCommand.ExecuteAsync。我相信這將返回一個IObservable<File>你的情況。認購以可觀察或以其他方式終止觀察到的處置將導致觀察到的,請求傳遞給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")); 

這是從上面的代碼的輸出。你可以看到,取消標記確實請求取消:

請求的第一消除:假
請求的第二消除:假
請求的第二消除:假
請求的第一消除:假
第一完成
請求的第一個取消:True
第二次取消請求:錯誤
第二次取消請求:錯誤
第二次取消請求d:假
接下去的第二個
第二完成

編輯 - 可能的解決方案

所以我覺得我有東西會在你的情況下工作,同時還允許您使用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調用
19:04:59.6268406取消請求:真

+0

這樣算下來訂閱= LoadFileCommand.Subscribe(文件=> OnFileLoaded(文件)是錯了嗎?我還以爲訂閱命令是接到任務的結果時,完成後的樣子。我缺少的東西? – SuperJMN

+1

@Super沒有,如果你想取消一個命令,我知道的唯一方法是捕獲當你調用該命令並終止該observable時返回的observable。我不知道有什麼辦法可以通過observable命令來做到這一點,哪種方式是有道理的,如果它是一個長時間運行的任務,那麼命令可能會被調用任意數量的附加時間,這是您可以取消單個命令調用的唯一方法會有一些代表該命令調用的對象 –

+0

我有點困惑:(我所做的是將一個Button綁定到LoadCommand中)在MainViewModel中(在c TOR)。如果我把'testCommand .ExecuteAsync(「第一個」)這一行放到任務中,即使沒有點擊按鈕,它也會引發任務。我希望用戶通過點擊按鈕來提高它,並允許使用另一個按鈕取消。請查看我在原始郵件中發佈的修改。 – SuperJMN