Cómo: Descargar un archivo en segundo plano
Descargar archivos es una tarea común y suele resultar de utilidad ejecutar esta operación que potencialmente invierte mucho tiempo en un subproceso independiente. Utilice el componente BackgroundWorker para lograr esta tarea con muy poco código.
Ejemplo
En el ejemplo de código siguiente se muestra cómo utilizar un componente BackgroundWorker para cargar un archivo XML de una dirección URL. Cuando el usuario hace clic en el botón Descargar, el controlador de eventos Click llama al método RunWorkerAsync de un componente BackgroundWorker para iniciar la operación de descarga. El botón se deshabilita por la duración de la descarga y, a continuación, se habilita cuando la descarga está completa. MessageBox muestra el contenido del archivo.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms
Imports System.Xml
Public Class Form1
Inherits Form
Private WithEvents downloadButton As Button
Private WithEvents progressBar1 As ProgressBar
Private WithEvents backgroundWorker1 As BackgroundWorker
Private document As XmlDocument = Nothing
Public Sub New()
InitializeComponent()
Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker()
End Sub
Private Sub downloadButton_Click( _
ByVal sender As Object, _
ByVal e As EventArgs) _
Handles downloadButton.Click
' Start the download operation in the background.
Me.backgroundWorker1.RunWorkerAsync()
' Disable the button for the duration of the download.
Me.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.
' 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 Me.backgroundWorker1.IsBusy
progressBar1.Increment(1)
' Keep UI messages moving, so the form remains
' responsive during the asynchronous operation.
Application.DoEvents()
End While
End Sub
Private Sub backgroundWorker1_DoWork( _
ByVal sender As Object, _
ByVal e As DoWorkEventArgs) _
Handles backgroundWorker1.DoWork
document = New XmlDocument()
' Replace this file name with a valid file name.
document.Load("http://www.tailspintoys.com/sample.xml")
' Uncomment the following line to
' simulate a noticeable latency.
'Thread.Sleep(5000);
End Sub
Private Sub backgroundWorker1_RunWorkerCompleted( _
ByVal sender As Object, _
ByVal e As RunWorkerCompletedEventArgs) _
Handles backgroundWorker1.RunWorkerCompleted
' Set progress bar to 100% in case it isn't already there.
progressBar1.Value = 100
If e.Error Is Nothing Then
MessageBox.Show(document.InnerXml, "Download Complete")
Else
MessageBox.Show("Failed to download file", "Download failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
' Enable the download button and reset the progress bar.
Me.downloadButton.Enabled = True
progressBar1.Value = 0
End Sub
#Region "Windows Form Designer generated code"
' <summary>
' Required designer variable.
' </summary>
Private components As System.ComponentModel.IContainer = Nothing
' <summary>
' Clean up any resources being used.
' </summary>
' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso (components IsNot Nothing) Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
' <summary>
' Required method for Designer support - do not modify
' the contents of this method with the code editor.
' </summary>
Private Sub InitializeComponent()
Me.downloadButton = New System.Windows.Forms.Button
Me.progressBar1 = New System.Windows.Forms.ProgressBar
Me.SuspendLayout()
'
'downloadButton
'
Me.downloadButton.Location = New System.Drawing.Point(12, 12)
Me.downloadButton.Name = "downloadButton"
Me.downloadButton.Size = New System.Drawing.Size(100, 23)
Me.downloadButton.TabIndex = 0
Me.downloadButton.Text = "Download file"
Me.downloadButton.UseVisualStyleBackColor = True
'
'progressBar1
'
Me.progressBar1.Location = New System.Drawing.Point(12, 50)
Me.progressBar1.Name = "progressBar1"
Me.progressBar1.Size = New System.Drawing.Size(100, 26)
Me.progressBar1.TabIndex = 1
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(136, 104)
Me.Controls.Add(Me.downloadButton)
Me.Controls.Add(Me.progressBar1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region
End Class
Public Class Program
' <summary>
' The main entry point for the application.
' </summary>
<STAThread()> _
Shared Sub Main()
Application.EnableVisualStyles()
Application.Run(New Form1())
End Sub
End Class
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());
}
}
Descargar el archivo
El archivo se descarga en el subproceso de trabajo del componente BackgroundWorker, que ejecuta el controlador de eventos DoWork. Este subproceso se inicia cuando el código llama al método RunWorkerAsync.
Private Sub backgroundWorker1_DoWork( _
ByVal sender As Object, _
ByVal e As DoWorkEventArgs) _
Handles backgroundWorker1.DoWork
document = New XmlDocument()
' Replace this file name with a valid file name.
document.Load("http://www.tailspintoys.com/sample.xml")
' Uncomment the following line to
' simulate a noticeable latency.
'Thread.Sleep(5000);
End Sub
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");
}
Esperar la finalización de un BackgroundWorker
El controlador de eventos downloadButton_Click muestra cómo esperar a que un componente BackgroundWorker finalice su tarea asincrónica.
Si solo desea que la aplicación responda a los eventos y no desea realizar ningún trabajo en el subproceso principal mientras espera a que el subproceso en segundo plano se complete, simplemente salga del controlador.
Si desea continuar trabajando en el subproceso principal, use la propiedad IsBusy para determinar si el subproceso BackgroundWorker todavía se está ejecutando. En el ejemplo, una barra de progreso se actualiza mientras se realiza la descarga. Asegúrese de llamar al método Application.DoEvents para que la UI siga respondiendo.
Private Sub downloadButton_Click( _
ByVal sender As Object, _
ByVal e As EventArgs) _
Handles downloadButton.Click
' Start the download operation in the background.
Me.backgroundWorker1.RunWorkerAsync()
' Disable the button for the duration of the download.
Me.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.
' 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 Me.backgroundWorker1.IsBusy
progressBar1.Increment(1)
' Keep UI messages moving, so the form remains
' responsive during the asynchronous operation.
Application.DoEvents()
End While
End Sub
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();
}
}
Mostrar el resultado
El método backgroundWorker1_RunWorkerCompleted controla el evento RunWorkerCompleted y se le llama cuando finaliza la operación en segundo plano. Este método comprueba primero la propiedad AsyncCompletedEventArgs.Error. Si AsyncCompletedEventArgs.Error es null, a continuación, este método muestra el contenido del archivo. Después, habilita el botón de descarga, que se deshabilitó cuando comenzó la descarga, y restablece la barra de progreso.
Private Sub backgroundWorker1_RunWorkerCompleted( _
ByVal sender As Object, _
ByVal e As RunWorkerCompletedEventArgs) _
Handles backgroundWorker1.RunWorkerCompleted
' Set progress bar to 100% in case it isn't already there.
progressBar1.Value = 100
If e.Error Is Nothing Then
MessageBox.Show(document.InnerXml, "Download Complete")
Else
MessageBox.Show("Failed to download file", "Download failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
' Enable the download button and reset the progress bar.
Me.downloadButton.Enabled = True
progressBar1.Value = 0
End Sub
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;
}
Compilar el código
Para este ejemplo se necesita:
- Referencias a los ensamblados System.Drawing, System.Windows.Forms y System.Xml.
Para obtener información acerca de la compilación de este ejemplo desde la línea de comandos de Visual Basic o Visual C#, vea Generar desde la línea de comandos (Visual Basic) o Compilar la línea de comandos con csc.exe. También puede compilar este ejemplo en Visual Studio pegando el código en un proyecto nuevo. Para obtener más información, vea Cómo: Compilar y ejecutar un ejemplo de código completo de formularios Windows Forms utilizando Visual Studio y Cómo: Compilar y ejecutar un ejemplo de código completo de formularios Windows Forms utilizando Visual Studio y Cómo: Compilar y ejecutar un ejemplo de código completo de formularios Windows Forms utilizando Visual Studio y Cómo: Compilar y ejecutar un ejemplo de código completo de Windows Forms en Visual Studio y Cómo: Compilar y ejecutar un ejemplo de código completo de Windows Forms en Visual Studio.
Programación eficaz
Compruebe siempre la propiedad AsyncCompletedEventArgs.Error en el controlador de eventos RunWorkerCompleted antes de intentar obtener acceso a la propiedad RunWorkerCompletedEventArgs.Result o a cualquier otro objeto que se vea afectado por el controlador de eventos DoWork.
Vea también
Tareas
Cómo: Ejecutar una operación en segundo plano
Cómo: Implementar un formulario que utiliza una operación en segundo plano