Implementación de BackgroundWorker en C# para visualizar datos de base de datos en gráficos Chart

Uso de BackgroundWorker en aplicaciones WinForms

En el desarrollo de aplicaciones Windows Forms, la ejecución de operaciones prolongadas, como consultas a bases de datos o procesamiento intensivo, puede bloquear la interfaz de usuario (UI) y generar una experiencia negativa. Para evitar esto, se recomienda delegar estas tareas a un hilo separado. La clase BackgroundWorker de .NET proporciona una solución sencilla para ejecutar operaciones en segundo plano mientras se mantiene la UI responsiva.

BackgroundWorker facilita la comunicación entre el hilo de trabajo y el hilo de la UI mediante eventos, evitando la necesidad de usar delegados y Invoke manualmente, lo que simplifica el código en comparación con el uso directo de la clase Thread.

Métodos y propiedades esenciales

Métodos clave:

  • RunWorkerAsync(): Inicia la operación en segundo plano y desencadena el evento DoWork.
  • CancelAsync(): Solicita la cancelación de la operación; solo establece la propiedad CancellationPending en true, por lo que el código en DoWork debe verificar esta propiedad para detenerse.
  • ReportProgress(int porcentaje, object estado): Dispara el evento ProgressChanged para notificar el progreso a la UI.

Propiedades importantes:

  • CancellationPending: Indica si se ha solicitado la cancelación (lectura única).
  • WorkerSupportsCancellation: Debe establecerse en true para permitir la cancelación.
  • WorkerReportsProgress: Debe establecerse en true para usar ReportProgress.

Eventos principales:

  • DoWork: Se ejecuta en el hilo de fondo; aquí se coloca la lógica de la operación larga. No se deben manipular controles de UI directamente.
  • RunWorkerCompleted: Se dispara al finalizar, cancelar o si ocurre una excepción en DoWork. Es seguro actualizar la UI desde este evento.
  • ProgressChanged: Se activa al llamar a ReportProgress, útil para mostrar avances en la UI.

Consideraciones:

1. En el manejador de DoWork, evite interactuar con objetos de UI. Use ProgressChanged y RunWorkerCompleted para actualizaciones de interfaz.
2. BackgroundWorker no funciona a través de límites de AppDomain; no se debe usar para operaciones multihilo entre múltiples dominios.

Ejemplo práctico: Consulta a base de datos y visualización en Chart

A continuación, se muestra un ejemplo que utiliza BackgroundWorker para consultar datos de una base de datos y mostrarlos en controles Chart, junto con cálculos estadísticos como CPK.

Paso 1: Declarar el BackgroundWorker


// Declaración de una instancia de BackgroundWorker
BackgroundWorker asyncWorker = new BackgroundWorker();

Paso 2: Asignar eventos al cargar el formulario


private void Form1_Load(object sender, EventArgs e)
{
    // Vincular eventos al BackgroundWorker
    asyncWorker.DoWork += asyncWorker_DoWork;
    asyncWorker.RunWorkerCompleted += asyncWorker_Completed;
}

Paso 3: Implementar el evento DoWork

Este evento se ejecuta en un hilo secundario y contiene la lógica para la consulta a la base de datos. Los parámetros se pasan a través de la propiedad Argument de DoWorkEventArgs.


private void asyncWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Obtener parámetros desde e.Argument (ejemplo: rango de fechas)
    var parametros = (List<DateTime>)e.Argument;
    DataTable resultado = new DataTable();

    // Construir consulta SQL (ejemplo simplificado)
    string consulta = $"SELECT * FROM registros WHERE Fecha BETWEEN '{parametros[0]:yyyy-MM-dd}' AND '{parametros[1]:yyyy-MM-dd}' ORDER BY Id";

    // Simular ejecución de consulta (reemplazar con acceso real a base de datos)
    resultado = EjecutarConsulta(consulta);

    // Asignar resultado a e.Result para uso posterior
    e.Result = resultado;
}

private DataTable EjecutarConsulta(string sql)
{
    // Lógica para ejecutar la consulta (placeholder)
    DataTable dt = new DataTable();
    // ... código de acceso a base de datos ...
    return dt;
}

Paso 4: Manejar el evento RunWorkerCompleted

Aquí se procesan los resultados, se actualizan los controles Chart y se realizan cálculos adicionales. Es seguro interactuar con la UI en este evento.


private void asyncWorker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        MessageBox.Show("Error en la operación: " + e.Error.Message);
        return;
    }

    DataTable datos = (DataTable)e.Result;

    // Limpiar y actualizar gráficos Chart
    graficoTendencia.Series[0].Points.Clear();
    foreach (DataRow fila in datos.Rows)
    {
        graficoTendencia.Series[0].Points.AddXY(fila["Fecha"].ToString(), fila["Valor"].ToString());
    }

    // Calcular CPK (ejemplo simplificado)
    var valores = new List<float>();
    foreach (DataRow fila in datos.Rows)
    {
        float.TryParse(fila["Valor"].ToString(), out float valor);
        valores.Add(valor);
    }

    float limiteSuperior = 100.0f; // Ejemplo
    float limiteInferior = 50.0f;  // Ejemplo
    float cpk = CalcularCPK(valores.ToArray(), limiteSuperior, limiteInferior);
    etiquetaCPK.Text = cpk.ToString("F4");
}

private float CalcularCPK(float[] datos, float ls, float li)
{
    // Lógica para calcular CPK (placeholder)
    // Implementar cálculo estadístico real aquí
    return 0.0f;
}

Paso 5: Iniciar la operación asíncrona

Desde un evento de UI, como un clic de botón, se inicia el BackgroundWorker pasando los parámetros necesarios.


private void botonConsultar_Click(object sender, EventArgs e)
{
    DateTime inicio = dateTimePicker1.Value;
    DateTime fin = dateTimePicker2.Value;

    if (inicio > fin)
    {
        MessageBox.Show("La fecha de inicio debe ser anterior a la de fin.");
        return;
    }

    List<DateTime> rangoFechas = new List<DateTime> { inicio, fin };

    // Iniciar el BackgroundWorker si no está ocupado
    if (!asyncWorker.IsBusy)
    {
        asyncWorker.RunWorkerAsync(rangoFechas);
    }
}

Paso 6: Cancelar la operación (opcional)

Para detener la operación en segundo plano, se puede solicitar la cancelación al cerrar el fomrulario.


private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (asyncWorker.WorkerSupportsCancellation)
    {
        asyncWorker.CancelAsync();
    }
}

Inicialización del control Chart

Configurar los controles Chart para visualizar los datos. A continuación, un ejemplo de inicialización simplificada.


private void ConfigurarGraficos()
{
    // Configurar gráfico principal (ejemplo)
    graficoTendencia.Series.Clear();
    graficoTendencia.Series.Add("Datos");
    graficoTendencia.Series["Datos"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
    graficoTendencia.Series["Datos"].BorderWidth = 2;

    // Ajustar ejes
    graficoTendencia.ChartAreas[0].AxisX.Title = "Fecha";
    graficoTendencia.ChartAreas[0].AxisY.Title = "Valor";
    graficoTendencia.ChartAreas[0].AxisX.LabelStyle.Format = "yyyy-MM-dd";
}

Este enfoque garantiza que la UI permanezca responsiva durante operaciones intensivas, mejorando la experiencia del usuario en aplicacionse WinForms.

Etiquetas: C# BackgroundWorker WinForms Chart Database

Publicado el 6-16 02:20