[英]Allow user to immediately cancel long-running WPF operation from button
在WPF應用中,我們有一個按鈕,用戶可以單擊該按鈕來觸發要加載到VLC Media Player中的視頻列表:
<Button Content="{Binding RotatorButtonLabel}" Command="{Binding RotateVideosCommand}" />
在視圖模型MainWindowVm
,我們具有處理按鈕單擊的命令:
public ICommand RotateVideosCommand => new RelayCommand(RotateVideos);
private void RotateVideos()
{
IsRotatorActive = !IsRotatorActive;
RotatorButtonLabel = IsRotatorActive
? "Stop Rotator"
: "Rotate Videos";
_rotatorVm = new RotatorVm
{
ImageVms = ImagesView.Cast<ImageVm>().ToList(),
IsRotatorActive = IsRotatorActive
};
// This fires off a new thread to run the rotator, otherwise the UI freezes.
Task.Run(() => Messenger.Default.Send(rotatorVm, "LaunchRotator"));
}
注意,在上面的命令處理程序中,我們使用MVVM Light Toolkit的Messenger
來告訴背后的代碼啟動旋轉器。
現在在MainWindow.xaml.cs
,我們有以下代碼:
private CancellationTokenSource _cancellationTokenSource = null;
private CancellationToken _cancellationToken;
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<RotatorVm>(this, "LaunchRotator", LaunchRotator);
// Other logic...
}
然后上面的LaunchRotator
調用:
private void LaunchRotator(RotatorVm rotatorVm)
{
if (_cancellationToken.IsCancellationRequested)
{
_cancellationTokenSource.Dispose();
}
if (_cancellationTokenSource == null || _cancellationToken.IsCancellationRequested)
{
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
}
if (!rotatorVm.IsRotatorActive)
{
_cancellationTokenSource.Cancel();
return;
}
RotateVideos();
}
private void RotateVideos()
{
while (true)
{
if (_cancellationToken.IsCancellationRequested)
{
return;
}
// This is to simplify the code and simulate work.
Thread.Sleep(5000);
}
}
如果單擊“停止旋轉器”按鈕,則代碼可能需要幾秒鍾才能到達while
循環的下一個迭代並讀取IsCancellationRequested
。 在這種情況下,如何使它立即停止?
我看了這個示例 ,但是假設任務和活動都在一個類中。 在這里,我有一個視圖模型和一個后台代碼。 謝謝。
您不能(以實用的方式)也不應這樣做。
如果要停止在另一個線程中工作,那么正確的方法是向該線程發信號(就像您已經完成的那樣),並允許該線程自己停止。 由於您的示例工作負載是Thread.Sleep(5000)
,一旦被點擊,該線程將無法執行任何其他操作,直到睡眠時間結束。 換句話說,您可以正確地向線程發出信號,並且如果線程正在睡眠,它將一直存活直到睡眠完成,然后它將再次檢查該信號。
選項:
Task.Wait(5000, token)
並使用此Task.Wait(5000, token)
而不是Thread.Sleep(5000)
將Token
傳遞給模擬的工作負載。 這樣,模擬工作也可以取消。 Thread
或Task
正確結束時等待,這是執行此操作的最佳方法。 這就是Thread.Abort
受到如此多批評並被勸阻的原因。 Cancel()
,然后繼續進行而不必等待Task
或Thread
完成。 您必須考慮到這一點進行設計,但這是實用的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.