【WinForm.NET开发】实现使用后台操作的窗体

发布时间:2024年01月21日

本文内容

  1. 创建使用后台操作的窗体
  2. 使用设计器创建 BackgroundWorker
  3. 添加异步事件处理程序
  4. 添加进度报告和取消支持
  5. Checkpoint

如果某项操作需要很长的时间才能完成,并且不希望用户界面 (UI) 停止响应或阻塞,则可以使用?BackgroundWorker?类在另一个线程上执行此操作。

本演练演示如何使用?BackgroundWorker?类“在后台”执行耗时的计算,同时用户界面保持响应。 演练时,将有一个异步计算 Fibonacci 数列的应用程序。 即使计算大型 Fibonacci 数列需要花费大量时间,但主 UI 线程不会被这种延时中断,并且在计算期间窗体仍会响应。

本演练涉及以下任务:

  • 创建基于 Windows 的应用程序

  • 在窗体中创建?BackgroundWorker

  • 添加异步事件处理程序

  • 添加进度报告和取消支持

1、创建使用后台操作的窗体

  1. 在 Visual Studio 中,创建一个名为?BackgroundWorkerExample?的基于 Windows 的应用程序项目(“文件”>“新建”>“项目”>“Visual C#”或“Visual Basic”>“经典桌面”>“Windows 窗体应用程序”)。

  2. 在“解决方案资源管理器”中,右键单击“Form1”,然后从快捷菜单中选择“重命名”。 将文件名更改为?FibonacciCalculator。 询问是否希望重命名对代码元素“”的所有引用时,单击“是”Form1按钮。

  3. 从“工具箱”将?NumericUpDown?控件拖到窗体上。 将?Minimum?属性设置为?1,将?Maximum?属性设置为?91

  4. 向窗体添加两个?Button?控件。

  5. 重命名第一个?Button?控件?startAsyncButton,并将?Text?属性设置为?Start Async。 重命名第二个?Button?控件?cancelAsyncButton,并将?Text?属性设置为?Cancel Async。 将它的?Enabled?属性设置为?false

  6. 为两个?Button?控件的?Click?事件创建一个事件处理程序。?

  7. 从“工具箱”将?Label?控件拖到窗体上,然后将其重命名为?resultLabel

  8. 从“工具箱”将?ProgressBar?控件拖到窗体上。

2、使用设计器创建 BackgroundWorker

可以使用“Windows 窗体设计器”为异步操作创建?BackgroundWorker

从“工具箱”的“组件”选项卡中,将?BackgroundWorker?拖到窗体上。

3、添加异步事件处理程序

现在已准备好为?BackgroundWorker?组件的异步事件添加事件处理程序。 这些事件处理程序将调用在后台运行的计算 Fibonacci 数列的耗时操作。

  1. 在“属性”窗口中的?BackgroundWorker?组件仍处于选中状态时,单击“事件”按钮。 双击?DoWork?和?RunWorkerCompleted?事件以创建事件处理程序。?

  2. 在窗体中新建一个名为?ComputeFibonacci?的新方法。 此方法完成实际的工作,并在后台运行。 这些代码演示了 Fibonacci 算法的递归实现,这种算法的效率非常低,对于较大的数值花费的时间按指数增长。 在这里使用是出于演示的目的,为了说明在应用程序中某项操作可能带来长时间的延迟。

    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if ((n < 0) || (n > 91))
        {
            throw new ArgumentException(
                "value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has canceled.
        // Note that a call to CancelAsync may have set
        // CancellationPending to true just after the
        // last invocation of this method exits, so this
        // code will not have the opportunity to set the
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
    
        if (worker.CancellationPending)
        {
            e.Cancel = true;
        }
        else
        {
            if (n < 2)
            {
                result = 1;
            }
            else
            {
                result = ComputeFibonacci(n - 1, worker, e) +
                         ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete =
                (int)((float)n / (float)numberToCompute * 100);
            if (percentComplete > highestPercentageReached)
            {
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    }
    
  3. 在?DoWork?事件处理程序中,添加对?ComputeFibonacci?方法的调用。 从?DoWorkEventArgs?的?Argument?属性中获取?ComputeFibonacci?的第一个参数。 稍后将?BackgroundWorker?和?DoWorkEventArgs?参数用于进度报告和取消支持。 将?ComputeFibonacci?的返回值分配给?DoWorkEventArgs?的?Result?属性。 此结果将可供?RunWorkerCompleted?事件处理程序使用。

    ?备注

    DoWork?事件处理程序不直接引用?backgroundWorker1?实例变量,因为这将会使此事件处理程序和某个特定的?BackgroundWorker?实例耦合。 相反,引发此事件的?BackgroundWorker?引用将从?sender?参数恢复。 当窗体承载多个?BackgroundWorker?时这非常重要。 在?DoWork?事件处理程序中不操作任何用户界面对象也非常重要。 而应该通过?BackgroundWorker?事件与用户界面进行通信。

    // This event handler is where the actual,
    // potentially time-consuming work is done.
    private void backgroundWorker1_DoWork(object sender,
        DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // Assign the result of the computation
        // to the Result property of the DoWorkEventArgs
        // object. This is will be available to the
        // RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci((int)e.Argument, worker, e);
    }
    
  4. 在?startAsyncButton?控件的?Click?事件处理程序中,添加启动异步操作的代码。

    private void startAsyncButton_Click(System.Object sender,
        System.EventArgs e)
    {
        // Reset the text in the result label.
        resultLabel.Text = String.Empty;
    
        // Disable the UpDown control until
        // the asynchronous operation is done.
        this.numericUpDown1.Enabled = false;
    
        // Disable the Start button until
        // the asynchronous operation is done.
        this.startAsyncButton.Enabled = false;
    
        // Enable the Cancel button while
        // the asynchronous operation runs.
        this.cancelAsyncButton.Enabled = true;
    
        // Get the value from the UpDown control.
        numberToCompute = (int)numericUpDown1.Value;
    
        // Reset the variable for percentage tracking.
        highestPercentageReached = 0;
    
        // Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute);
    }
    
  5. 在?RunWorkerCompleted?事件处理程序中,将计算结果分配给?resultLabel?控件。

    // This event handler deals with the results of the
    // background operation.
    private void backgroundWorker1_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // First, handle the case where an exception was thrown.
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // Next, handle the case where the user canceled
            // the operation.
            // Note that due to a race condition in
            // the DoWork event handler, the Cancelled
            // flag may not have been set, even though
            // CancelAsync was called.
            resultLabel.Text = "Canceled";
        }
        else
        {
            // Finally, handle the case where the operation
            // succeeded.
            resultLabel.Text = e.Result.ToString();
        }
    
        // Enable the UpDown control.
        this.numericUpDown1.Enabled = true;
    
        // Enable the Start button.
        startAsyncButton.Enabled = true;
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    

4、添加进度报告和取消支持

由于异步操作将会花费很长的时间,因此通常希望向用户报告进度并允许用户取消操作。?BackgroundWorker?类提供一个在后台操作进行时允许发送进度消息的事件。 它还提供允许辅助代码检测对?CancelAsync?的调用并中断自身的标记。

实现进度报告

  1. 在“属性”窗口中,选择?backgroundWorker1。 将?WorkerReportsProgress?和?WorkerSupportsCancellation?属性设置为?true

  2. 在?FibonacciCalculator?窗体中声明两个变量。 这将用于跟踪进度。

    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
  3. 为?ProgressChanged?事件添加事件处理程序。 在?ProgressChanged?事件处理程序中,使用?ProgressChangedEventArgs?参数的?ProgressPercentage?属性更新?ProgressBar

    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    

实现取消支持

  1. 在?cancelAsyncButton?控件的?Click?事件处理程序中,添加取消异步操作的代码。

    private void cancelAsyncButton_Click(System.Object sender,
        System.EventArgs e)
    {
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
  2. 下面的?ComputeFibonacci?方法中的代码片段可报告进程并支持取消。

    if (worker.CancellationPending)
    {
        e.Cancel = true;
    }
    
    // Report progress as a percentage of the total task.
    int percentComplete =
        (int)((float)n / (float)numberToCompute * 100);
    if (percentComplete > highestPercentageReached)
    {
        highestPercentageReached = percentComplete;
        worker.ReportProgress(percentComplete);
    }
    

5、Checkpoint

此时,可以编译并运行 Fibonacci 计算器应用程序。

按 F5 编译并运行应用程序。

在后台运行计算的同时,将会看到?ProgressBar?显示完成计算的进度。 也可以取消挂起的操作。

对于较小数值,计算应非常快,但对于较大数值,将看到明显的延时。 如果输入 30 或更大的值,应看到有几秒钟的延时,这取决于计算机的速度。 对于大于 40 的值,完成计算可能要花费数分钟或数小时。 在计算器计算较大的 Fibonacci 数列时,注意可以自由地移动窗体、最小化、最大化甚至关闭窗体。 这是因为主 UI 线程不会等待计算完成。

文章来源:https://blog.csdn.net/m0_51887793/article/details/135734341
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。