簡體   English   中英

允許用戶從按鈕立即取消長時間運行的WPF操作

[英]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傳遞給模擬的工作負載。 這樣,模擬工作也可以取消。
  • 為實際工作; 您必須根據自己的最佳判斷來測試信號何時何地看起來合理,這樣您就不必等待很長時間才能收到取消信號。 只需注意,讓調用者在ThreadTask正確結束時等待,這是執行此操作的最佳方法。 這就是Thread.Abort受到如此多批評並被勸阻的原因。
  • 另一種選擇是開火和忘記。 您可以在令牌源上調用Cancel() ,然后繼續進行而不必等待TaskThread完成。 您必須考慮到這一點進行設計,但這是實用的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM