TOC

This article has been localized into Vietnamese by the community.

Misc.:

Hủy bỏ BackgroundWorker

Như chúng ta đã thấy trong bài viết trước, đa luồng có thêm lợi thế là có thể hiển thị tiến trình và không khiến ứng dụng của bạn bị treo trong khi thực hiện thao tác tốn thời gian.

Một vấn đề khác bạn sẽ gặp phải nếu bạn thực hiện tất cả công việc trên luồng UI là thực tế là không có cách nào để người dùng hủy tác vụ đang chạy - và tại sao vậy? Bởi vì nếu luồng UI đang bận thực hiện tác vụ dài dòng của bạn, sẽ không có đầu vào nào được xử lý, nghĩa là cho dù người dùng của bạn nhấn nút Cancel hay phím Esc như thế nào, không có gì xảy ra.

May mắn cho chúng ta, BackgroundWorker được xây dựng để giúp bạn dễ dàng hỗ trợ tiến trình và hủy bỏ, và trong khi chúng tôi xem xét toàn bộ phần tiến trình trong chương trước, phần này sẽ nói về cách sử dụng hỗ trợ hủy. Hãy nhảy thẳng vào một ví dụ:

<Window x:Class="WpfTutorialSamples.Misc.BackgroundWorkerCancellationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="BackgroundWorkerCancellationSample" Height="120" Width="200">
    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBlock Name="lblStatus" HorizontalAlignment="Center" Margin="0,10" FontWeight="Bold">Not running...</TextBlock>
        <WrapPanel>
            <Button Name="btnStart" Width="60" Margin="10,0" Click="btnStart_Click">Start</Button>
            <Button Name="btnCancel" Width="60" Click="btnCancel_Click">Cancel</Button>
        </WrapPanel>
    </StackPanel>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace WpfTutorialSamples.Misc
{
	public partial class BackgroundWorkerCancellationSample : Window
	{
		private BackgroundWorker worker = null;

		public BackgroundWorkerCancellationSample()
		{
			InitializeComponent();
			worker = new BackgroundWorker();
			worker.WorkerSupportsCancellation = true;
			worker.WorkerReportsProgress = true;
			worker.DoWork += worker_DoWork;
			worker.ProgressChanged += worker_ProgressChanged;
			worker.RunWorkerCompleted += worker_RunWorkerCompleted;
		}

		private void btnStart_Click(object sender, RoutedEventArgs e)
		{
			worker.RunWorkerAsync();
		}

		private void btnCancel_Click(object sender, RoutedEventArgs e)
		{
			worker.CancelAsync();
		}

		void worker_DoWork(object sender, DoWorkEventArgs e)
		{
			for(int i = 0; i <= 100; i++)
			{
				if(worker.CancellationPending == true)
				{
					e.Cancel = true;
					return;
				}
				worker.ReportProgress(i);
				System.Threading.Thread.Sleep(250);
			}
			e.Result = 42;
		}

		void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			lblStatus.Text = "Working... (" + e.ProgressPercentage + "%)";
		}

		void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if(e.Cancelled)
			{
				lblStatus.Foreground = Brushes.Red;
				lblStatus.Text = "Cancelled by user...";
			}
			else
			{
				lblStatus.Foreground = Brushes.Green;
				lblStatus.Text = "Done... Calc result: " + e.Result;
			}
		}
	}
}

Vì vậy, XAML rất cơ bản - chỉ là một nhãn để hiển thị trạng thái hiện tại và sau đó là một vài nút để Start và Cancel.

Trong Code-behind, chúng tôi bắt đầu bằng cách tạo phiên bản BackgroundWorker. Đặc biệt chú ý đến WorkerSupportsCancellationWorkerReportsProgress thuộc tính mà chúng tôi thiết lập là true - mà không có một ngoại lệ sẽ được ném nếu chúng ta cố gắng sử dụng các tính năng này.

Nút hủy chỉ đơn giản gọi phương thức CancelAsync() - điều này sẽ báo hiệu cho trương trình rằng người dùng muốn hủy quá trình đang chạy bằng cách đặt thuộc tính CancellationPending thành true, nhưng đó là tất cả những gì bạn có thể làm từ luồng UI - phần còn lại sẽ phải được thực hiện từ bên trong sự kiện DoWork.

Trong sự kiện DoWork, chúng tôi đếm đến 100 với độ trễ 250 mili giây trên mỗi lần lặp. Điều này mang lại cho chúng tôi một nhiệm vụ dài và tốt đẹp, qua đó chúng tôi có thể báo cáo tiến trình và vẫn có thời gian để nhấn nút Cancel đó.

Lưu ý cách tôi kiểm tra thuộc tính CancellationPending trên mỗi lần lặp - nếutrương trình bị hủy, thuộc tính này sẽ true và tôi sẽ có cơ hội nhảy ra khỏi vòng lặp. Tôi đặt thuộc tính Cancel trên các đối số sự kiện, để báo hiệu rằng quy trình đã bị hủy - giá trị này được sử dụng trong sự kiện RunWorkerCompleted để xem liệu nhân viên thực sự đã hoàn thành hay nếu nó bị hủy.

Trong sự kiện RunWorkerCompleted, tôi chỉ cần kiểm tra xem trương trình có bị cancel hay không và sau đó cập nhật nhãn trạng thái cho phù hợp.

Tổng kết

Vì vậy, để tổng hợp, thêm hỗ trợ hủy bỏ cho BackgroundWorker của bạn được dễ dàng - chỉ cần đảm bảo rằng bạn thiết lập WorkerSupportsCancellation cho đúng và kiểm tra CancellationPending khi thực hiện công việc. Sau đó, khi bạn muốn hủy tác vụ, bạn chỉ cần gọi phương thức CancelAsync() trên worker và bạn đã hoàn tất.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!