Show / Hide Table of Contents

Практическое руководство. Фоновая загрузка файла

Загрузка файла — это обычная задача, и было бы разумным запускать эту потенциально длительную операцию в отдельном потоке. С помощью компонента BackgroundWorker и небольшого фрагмента кода эта задача легко решается.

Пример

В примере кода ниже демонстрируется использование компонента BackgroundWorker для загрузки XML-файла с URL-адреса. Когда пользователь щелкает загрузить кнопки Click вызовов обработчика событий RunWorkerAsync метод BackgroundWorker компонента для запуска операции загрузки. Кнопка отключается во время загрузки, а по его окончании снова становится активной. В поле MessageBox выводится содержимое файла.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Xml;

public class Form1 : Form
{
    private BackgroundWorker backgroundWorker1;
    private Button downloadButton;
    private ProgressBar progressBar1;
    private XmlDocument document = null;

    public Form1()
    {
        InitializeComponent();

        // Instantiate BackgroundWorker and attach handlers to its
        // DowWork and RunWorkerCompleted events.
        backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
        backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
        backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
    }

    private void downloadButton_Click(object sender, EventArgs e)
    {
        // Start the download operation in the background.
        this.backgroundWorker1.RunWorkerAsync();

        // Disable the button for the duration of the download.
        this.downloadButton.Enabled = false;

        // Once you have started the background thread you 
        // can exit the handler and the application will 
        // wait until the RunWorkerCompleted event is raised.

        // Or if you want to do something else in the main thread,
        // such as update a progress bar, you can do so in a loop 
        // while checking IsBusy to see if the background task is
        // still running.

        while (this.backgroundWorker1.IsBusy)
        {
            progressBar1.Increment(1);
            // Keep UI messages moving, so the form remains 
            // responsive during the asynchronous operation.
            Application.DoEvents();
        }
    }

    private void backgroundWorker1_DoWork(
        object sender,
        DoWorkEventArgs e)
    {
        document = new XmlDocument();

        // Uncomment the following line to
        // simulate a noticeable latency.
        //Thread.Sleep(5000);

        // Replace this file name with a valid file name.
        document.Load(@"http://www.tailspintoys.com/sample.xml");
    }

    private void backgroundWorker1_RunWorkerCompleted(
        object sender,
        RunWorkerCompletedEventArgs e)
    {
        // Set progress bar to 100% in case it's not already there.
        progressBar1.Value = 100;

        if (e.Error == null)
        {
            MessageBox.Show(document.InnerXml, "Download Complete");
        }
        else
        {
            MessageBox.Show(
                "Failed to download file",
                "Download failed",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
        }

        // Enable the download button and reset the progress bar.
        this.downloadButton.Enabled = true;
        progressBar1.Value = 0;
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    /// <summary>
    /// Required method for Designer support
    /// </summary>
    private void InitializeComponent()
    {
        this.downloadButton = new System.Windows.Forms.Button();
        this.progressBar1 = new System.Windows.Forms.ProgressBar();
        this.SuspendLayout();
        // 
        // downloadButton
        // 
        this.downloadButton.Location = new System.Drawing.Point(12, 12);
        this.downloadButton.Name = "downloadButton";
        this.downloadButton.Size = new System.Drawing.Size(100, 23);
        this.downloadButton.TabIndex = 0;
        this.downloadButton.Text = "Download file";
        this.downloadButton.UseVisualStyleBackColor = true;
        this.downloadButton.Click += new System.EventHandler(this.downloadButton_Click);
        // 
        // progressBar1
        // 
        this.progressBar1.Location = new System.Drawing.Point(12, 50);
        this.progressBar1.Name = "progressBar1";
        this.progressBar1.Size = new System.Drawing.Size(100, 26);
        this.progressBar1.TabIndex = 1;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(133, 104);
        this.Controls.Add(this.progressBar1);
        this.Controls.Add(this.downloadButton);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

    }

    #endregion
}

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new Form1());
    }
}

Загрузка файла

Файл скачивается в рабочем потоке компонента BackgroundWorker, который запускает обработчик событий DoWork. Этот поток запускается, когда код вызывает метод RunWorkerAsync.

private void backgroundWorker1_DoWork(
    object sender,
    DoWorkEventArgs e)
{
    document = new XmlDocument();

    // Uncomment the following line to
    // simulate a noticeable latency.
    //Thread.Sleep(5000);

    // Replace this file name with a valid file name.
    document.Load(@"http://www.tailspintoys.com/sample.xml");
}

Ожидание завершения работы BackgroundWorker

Обработчик событий downloadButton_Click демонстрирует ожидание завершения выполнения асинхронной задачи компонентом BackgroundWorker.

Если нужно, чтобы приложение лишь реагировало на события и не выполняло никакой работы в основном потоке в ожидании, пока не завершится выполнение фонового потока, просто завершите работу обработчика.

Если нужно продолжить выполнение работы в основном потоке, воспользуйтесь свойством IsBusy, чтобы определить, по-прежнему ли выполняется поток BackgroundWorker. В примере индикатор выполнения обновляется в процессе загрузки. Для сохранения отклика пользовательского интерфейса обязательно вызовите метод Application.DoEvents.

private void downloadButton_Click(object sender, EventArgs e)
{
    // Start the download operation in the background.
    this.backgroundWorker1.RunWorkerAsync();

    // Disable the button for the duration of the download.
    this.downloadButton.Enabled = false;

    // Once you have started the background thread you 
    // can exit the handler and the application will 
    // wait until the RunWorkerCompleted event is raised.

    // Or if you want to do something else in the main thread,
    // such as update a progress bar, you can do so in a loop 
    // while checking IsBusy to see if the background task is
    // still running.

    while (this.backgroundWorker1.IsBusy)
    {
        progressBar1.Increment(1);
        // Keep UI messages moving, so the form remains 
        // responsive during the asynchronous operation.
        Application.DoEvents();
    }
}

Вывод результата

Метод backgroundWorker1_RunWorkerCompleted обрабатывает событие RunWorkerCompleted и вызывается по завершении фоновой операции. Сначала этот метод проверяет свойство AsyncCompletedEventArgs.Error. Если свойство AsyncCompletedEventArgs.Error имеет значение null, то метод выводит содержимое файла. Затем он активирует кнопку загрузки, которая была отключена, когда началась загрузка, и сбрасывает индикатор выполнения.

private void backgroundWorker1_RunWorkerCompleted(
    object sender,
    RunWorkerCompletedEventArgs e)
{
    // Set progress bar to 100% in case it's not already there.
    progressBar1.Value = 100;

    if (e.Error == null)
    {
        MessageBox.Show(document.InnerXml, "Download Complete");
    }
    else
    {
        MessageBox.Show(
            "Failed to download file",
            "Download failed",
            MessageBoxButtons.OK,
            MessageBoxIcon.Error);
    }

    // Enable the download button and reset the progress bar.
    this.downloadButton.Enabled = true;
    progressBar1.Value = 0;
}

Компиляция кода

Для этого примера требуются:

  • ссылки на сборки System.Drawing, System.Windows.Forms и System.Xml.

Сведения о выполнении сборки этого примера из командной строки для Visual Basic или Visual C#, см. в разделе построение из командной строки или командной строки создания с помощью csc.exe. Можно также сборке этого примера в Visual Studio путем вставки кода в новый проект.

Отказоустойчивость

Перед попыткой обращения к свойству RunWorkerCompletedEventArgs.Result или любому другому объекту, который может быть изменен обработчиком событий DoWork, всегда проверяйте свойство AsyncCompletedEventArgs.Error в обработчике событий RunWorkerCompleted.

См. также

  • BackgroundWorker
  • Практическое руководство. Фоновое выполнение операции
  • Практическое руководство. Реализация формы, в которой выполняется фоновая операция
Back to top Неофициальная документация по .NET на русском языке. Лицензия: CC-BY 4.0. Основано на документации по .NET с Microsoft Docs
Generated by DocFX