C# - Buscando e exibindo Registros em um DataGridView Usando BackgroundWorker
Hoje eu vou voltar a escrever sobre o controle BackGroundWorker.
O controle BackgroundWorker pode executar tarefas com processamento intensivo em uma thread separada de forma que a interface do usuário (UI) não congela durante o processamento.
O componente BackgroundWorker componente oferece a capacidade de executar operações demoradas assincronamente ("em segundo plano"), em uma thread diferente do segmento de interface do usuário principal do seu aplicativo.
Para usar um BackgroundWorker, você simplesmente diz a ele que método deseja que seja executado em segundo plano e, em seguida, chame o método RunWorkerAsync.
A thread chamada continua a ser executada normalmente enquanto o método worker é executado de forma assíncrona. Quando o método for concluído, o BackgroundWorker alerta a thread chamada, acionando o evento RunWorkerCompleted, que contém, opcionalmente, os resultados da operação.
Desde que essas tarefas estão sendo executados em segundo plano e podem levar algum tempo, é necessário mostrar algumas mensagens personalizadas e atualizar a interface do usuário enquanto o trabalho esta em execução.
Vamos criar um projeto Windows Forms usando o Visual C# 2010 Express Edition que irá acessar um banco de dados SQL Server e exibir os dados de uma tabela em um controle DataGridView. Vamos usar os eventos do controle BackGroundWorker para exibir mensagens ao usuário durante o processamento.
Estamos usando a tabela Funcionarios do banco de dados Teste.mdf que possui a seguinte estrutura:
Obs: Vamos definir a classe TabelaDados com os campos Id e nomeFuncionario para exibir somente os dois campos pertinentes da tabela.
Você pode usar qualquer banco de dados e tabela para preencher o controle DataGridView.
Crie então um novo projeto Windows Forms Application (menu File-> New Project) e informe o nome BackGroundWorkerExemplo;
A seguir vamos incluir a partir da ToolBox no formulário form1.cs os seguintes controles/componentes:
Disponha os controles conforme o leiaute abaixo:
Vejamos as propriedades e métodos do controle BackGroundWorker que iremos usar em nosso projeto:
Propriedades:
Métodos:
Eventos:
1- DoWork - realiza o trabalho de execução da thread em background sendo chamado quando o método RunWorkerAsync é acionado;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { //Logica para tarefa em background ser feita }
O DoWorkEventArgs e possui as propriedades e.Argument e e.Result:
2- ProgressChanged - reporta o progresso feito pela thread. Este evento é disparado a partir do evento DoWork usando o método ReportProgress();
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Reporta o progresso do BackgroundWorker }
O ProgressChangedEventArgs contém as propriedades e.ProgressPercentage e e.UserState:
3- RunWorkerCompleted - Este evento é executado quando o BackgroundWorker terminou o a tarefa. Também é disparado quando o BackgroundWorker falhou ao realizar a tarefa atribuida ou a tarefa foi cancelada;
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //Executado no termino de operação BackgroundTask/ Failure/ Cancellation. }
RunWorkerCompletedEventArgs e possui as seguinte propriedades:
- e.Cancelled: Indica que a operação BackgroundWorker foi cancelada;
- e.Error: Erro ocorrido no processo de execução background;
- e.Result: Indica o resultado na execução do processo em background;
Vamos declarar os seguintes namespaces no formulário:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Threading;
A seguir declare a string de conexão a uma variável o para obter o total de registros:
SqlCommand Sqlcmd;
string ConnString = @"Data
Source=.\SQLEXPRESS;AttachDbFilename=C:\dados\Teste.mdf;Integrated
Security=True;Connect Timeout=30;User Instance=True";
int _TotalRegistros;
No construtor do formulário
public frmDados() { InitializeComponent(); // Para ativar o relatório de progresso do background worker precisamos definir esta propriedade backgroundWorker1.WorkerReportsProgress = true; } |
Estamos ativando o componente backgroundWorker.
No evento Load do formulário configuramos alguns controles como o botão Cancelar, a barra de status e o controle DataGridView;
private void frmRetrive_Load(object sender, EventArgs e) { btnCancelar.Enabled = false; statusStrip1.Visible = false; toolStripStatusLabel1.Visible = false; dataGridView1.ColumnCount = 2; dataGridView1.Columns[0].Name = "No."; dataGridView1.Columns[0].Width = 150; dataGridView1.Columns[1].Width = 150; dataGridView1.RowHeadersWidth = 21; dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing; dataGridView1.ColumnHeadersHeight = 23; dataGridView1.Columns[1].Name = "Nome Funcionário"; } |
No evento Click do botão Executar usamos a propriedade IsBusy para indicar se o controle backgroundworker esta realizando uma operação assíncrona ou operação em background;
Limpamos o controle DataGridView e iniciamos a thread em background:
private void btnIniciar_Click(object sender, EventArgs e) { statusStrip1.Visible = true; toolStripStatusLabel1.Visible = true; toolStripProgressBar1.Maximum = GeTotalRegistros(); if (!backgroundWorker1.IsBusy) { TabelaDados TObj = new TabelaDados(); dataGridView1.Rows.Clear(); // Inicia a Thread em BackGround backgroundWorker1.RunWorkerAsync(TObj); btnIniciar.Enabled = false; btnCancelar.Enabled = true; } } |
No evento DoWork executamos a tarefa em segundo plano exibindo o seu processamento na barra de progresso:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { TabelaDados Obj = (TabelaDados)e.Argument; string SqlcmdString = "SELECT id,funci_nome FROM Funcionarios"; SqlDataReader reader; int i = 1; try { using (SqlConnection conn = new SqlConnection(ConnString)) { Sqlcmd = new SqlCommand(SqlcmdString, conn); conn.Open(); reader = Sqlcmd.ExecuteReader(); if (reader.HasRows) { while (reader.Read()) { Obj.id = reader["id"].ToString(); Obj.nomeFuncionario = reader["funci_nome"].ToString(); // força uma delay no processamento Thread.Sleep(2000); // Relatorio de progresso backgroundWorker1.ReportProgress(i,Obj); if (backgroundWorker1.CancellationPending) { // Define a flag e.Cancel de forma que o evento WorkerCompleted // saiba que o processo foi cancelado e.Cancel = true; backgroundWorker1.ReportProgress(0); return; } i = i + 1; } conn.Close(); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } } |
No evento RunWorkerCompleted verificamos se a tarefa foi concluída ou cancelada:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { toolStripStatusLabel1.Text = "Cancelado pelo usuáio..."; toolStripProgressBar1.Value = 0; } // Verifica se ocorreu algum erro no processo em background else if (e.Error != null) { toolStripStatusLabel1.Text = e.Error.Message; } else { // A tarefa em BackGround foi completada sem erros toolStripStatusLabel1.Text = " Todos os registros foram carregados..."; } } |
O evento ProgressChanged exibimos os dados no controle DataGridView se a tarefa não foi cancelada:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (!backgroundWorker1.CancellationPending) { //Obtem o status do usuário que enviado como parte do método ReportProgress() a partir do evento DoWork TabelaDados Obj = (TabelaDados)e.UserState; //Inclui os dados no dataGridView1 dataGridView1.Rows.Add(Obj.id.ToString(),Obj.nomeFuncionario.ToString()); toolStripProgressBar1.Value = e.ProgressPercentage; toolStripStatusLabel1.Text = "Processando Linha.. " + e.ProgressPercentage.ToString() + " de " +_TotalRegistros; } } |
No evento Click do botão Cancelar verificamos se o processo esta em execução e paramos a execução da thread:
private void btnCancelar_Click(object sender, EventArgs e) { //verifica se o processo background Process esta em execução if (backgroundWorker1.IsBusy) { // Para a execução da Thread Background backgroundWorker1.CancelAsync(); btnCancelar.Enabled = false; btnIniciar.Enabled = true; } } |
A rotina GetTotalRegistros obtém o total de registros da tabela acessada:
private int GeTotalRegistros() { SqlConnection con; SqlCommand cmd; try { using (con = new SqlConnection(ConnString)) { cmd = new SqlCommand("SELECT COUNT(*) FROM Funcionarios", con); con.Open(); _TotalRegistros = int.Parse(cmd.ExecuteScalar().ToString()); con.Close(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } return _TotalRegistros; } |
A classe usada para criar a tabela e exibir os dados no DataGridView:
public class TabelaDados { public string id; public string nomeFuncionario; } |
No evento FormClosing (este evento ocorre antes do formulário ser fechado) do formulário frmDados
Obs: Quando um formulário é fechado, ele é descartado, liberando todos os recursos associados ao formulário.Se você cancelar este evento, o formulário permanece aberto.Para cancelar o fechamento de um formulário, defina como true a propriedade Cancel do FormClosingEventArgs passado para o manipulador de eventos.
Nota: Lembrando também que os eventos Form.Closed e Form.Closing não são disparados quando o método Application.Exit é chamado para encerrar a aplicação.
private void frmDados_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason == CloseReason.UserClosing) { if (backgroundWorker1.IsBusy) { backgroundWorker1.CancelAsync(); btnCancelar.Enabled = false; btnIniciar.Enabled = true; } } } |
A propriedade CloseReason retorna vários valores conforme o motivo pelo qual o formulário esta sendo fechado como fazia o VB6 com o QueryUnload.
Vejamos então quais os valores da enumeração CloseReason :
Enumeração | Descrição do motivo do fechamento do formulário para cada valor |
ApplicationExitCall | O fechamento esta sendo feito via chamada a Application.Exit() |
MdiFormClosing | O formulário MDI do formulário esta sendo fechado |
None | causas desconhecidas |
FormOwnerClosing | O formulário proprietário esta sendo fechado |
TaskManagerClosing | O administrador de tarefas do Windows esta fechando o formulário |
UserClosing | O fechamento esta sendo feito via Close ou clicando no X do menu de controle |
WindowsShutDown | O Windows esta sendo encerrado |
Se o controle backgroundworker estiver realizando uma operação assíncrona ou operação em background cancelamos a operação.
A seguir vemos o projeto em execução:
Pegue o projeto completo aqui: BackGroundWorkerExemplo.zip
1Ts 4:13
Não queremos, porém, irmãos, que sejais ignorantes acerca dos que já dormem, para que não vos entristeçais como os outros que não têm esperança.1Ts 4:14
Porque, se cremos que Jesus morreu e ressurgiu, assim também aos que dormem, Deus, mediante Jesus, os tornará a trazer juntamente com ele.Referências: