Pular para o conteúdo principal

Multithreading WPF: Usando o BackgroundWorker para se comunicar com a UI.


Este post é para salvar aqueles que gastam horas desenvolvendo programa em WPF, com todos aqueles componentes para mostrar o que está acontecendo no processamento, mas que na hora que executa o programa a tela do programa congela e só realizar a modificação nos valores dos controles ao final do processamento. Passei por isso! E não foi só uma vez não. :)


Vamos lá...


Seu aplicativo WPF precisa executa tarefas que consomem grandes quantidades de tempo, mas que durante a execução dessa tarefa, que é em um processo separado, terá que permite manter comunicação com a interface do usuário, atualizando controle, enquanto a tarefa é executada.


O Framework possue o objeto BackgroundWorker. O BackgroundWorker é recomendado para executar tarefas que consomem bastante tempo, mas em processo dedicado e separado da Thread principal, deixando o UI interativo com o usuário.


Como implementar?


1. Crie uma variável de classe do tipo BackgroundWorker.


BackgroundWorker bw = new BackgroundWorker ();


Especifique que a operação em background irá permitir o cancelamento e comunicação do progresso.


bw.WorkerSupportsCancellation = true;

bw.WorkerReportsProgress = true;


3.Crie um manipulador de eventos para o evento DoWork.


O manipulador de eventos DoWork é onde você executa a operação que irá demorar. Quaisquer valores que são passados ​​para a operação de fundo são passados ​​via parametro DoWorkEventArgs do objeto que é passado para o manipulador de eventos.


Para informar o progresso da execução do código você precisará fazer uma chamada para o método ReportProgress e passar o percentual da conclusão de 0 a 100. Chamar o método ReportProgress gera  disparo do evento ProgressChanged, que trabalhará separadamente.


Para determinar se existe um pedido pendente para cancelar a operação em background, verifique a propriedade CancellationPending do objeto BackgroundWorker. Se a propriedade for verdadeiro, o método CancelAsync foi chamado. Defina o objeto BackgroundWorker de propriedade Cancel como true e interrompa a execução.


Para passar dados de volta para o processo de chamada, defina a propriedade resultado do objeto DoWorkEventArgs que é passado para o manipulador de eventos. Este valor pode ser acessado quando o evento RunWorkerCompleted é acionado no final da operação.


private void bw_DoWork(object sender, DoWorkEventArgs e)

{

    BackgroundWorker worker = sender as BackgroundWorker;


    for (int i = 1; (i <= 10); i++)

    {

        if ((worker.CancellationPending == true))

        {

            e.Cancel = true;

            break;

        }

        else

        {

            // Perform a time consuming operation and report progress.

            System.Threading.Thread.Sleep(500);

            worker.ReportProgress((i * 10));

        }

    }

}

4.Crie um método para o evento ProgressChanged do background.


No método do evento ProgressChanged, adicione código para indicar o progresso e atualizar a interface do usuário.

Para determinar qual a percentagem de a operação estar concluída, verifique a propriedade do objeto ProgressPercentage do paramentro e, e.ProgressChangedEventArgs que foi passado para o manipulador de eventos.



private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)

{

    this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");

}

5.Crie um método para o evento RunWorkerCompleted.


O evento RunWorkerCompleted é gerado quando a thread for concluída. Independente da operação de fundo ter sido concluído com sucesso,erro ou cancelado, ele atualizará a interface do usuário.


Para determinar se ocorreu um erro, verifique a propriedade Erro do objeto RunWorkerCompletedEventArgs que foi passado para o metodo do evento. Se ocorrer um erro, esta propriedade contém as informações de exceção.


Se a operação permite o cancelamento e você quiser verificar se a operação foi cancelada, verifique a propriedade Cancelled do objeto RunWorkerCompletedEventArgs que foi passado para o método. Se a propriedade for verdadeiro, o método CancelAsync é chamado.


private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

    if ((e.Cancelled == true))

    {

        this.tbProgress.Text = "Canceled!";

    }


    else if (!(e.Error == null))

    {

        this.tbProgress.Text = ("Error: " + e.Error.Message);

    }


    else

    {

        this.tbProgress.Text = "Done!";

    }

}


6. Adicone os métodos dos eventos de BackgroundWorker.


O exemplo a seguir mostra como adicionar os métodos dos eventos DoWork, ProgressChanged, e RunWorkerCompleted.


bw.DoWork +=

    new DoWorkEventHandler(bw_DoWork);

bw.ProgressChanged +=

    new ProgressChangedEventHandler(bw_ProgressChanged);

bw.RunWorkerCompleted +=

    new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

7.
Para iniciar a execução do código, o método RunWorkerAsync deve ser chamado.


private void buttonStart_Click(object sender, RoutedEventArgs e)

{

    if (bw.IsBusy != true)

    {

        bw.RunWorkerAsync();

    }

}


8. Para cancelar a operação, chame o método CancelAsync.


private void buttonCancel_Click(object sender, RoutedEventArgs e)

{

    if (bw.WorkerSupportsCancellation == true)

    {

        bw.CancelAsync();

    }

}


Exemplo 1 - WPF


O exemplo a seguir mostra como usar a classe BackgroundWorker.
No exemplo, a operação de fundo executa o método do sono e relatórios de progresso para a interface do usuário. O trabalho de fundo é configurado para permitir o cancelamento.


Classe:


using System.ComponentModel;

using System.Windows;

using System.Windows.Controls;


namespace BackgroundWorker_CS

{

    public partial class Page : UserControl

    {

        private BackgroundWorker bw = new BackgroundWorker();


        public Page()

        {

            InitializeComponent();


            bw.WorkerReportsProgress = true;

            bw.WorkerSupportsCancellation = true;

            bw.DoWork += new DoWorkEventHandler(bw_DoWork);

            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

        }

        private void buttonStart_Click(object sender, RoutedEventArgs e)

        {

            if (bw.IsBusy != true)

            {

                bw.RunWorkerAsync();

            }

        }

        private void buttonCancel_Click(object sender, RoutedEventArgs e)

        {

            if (bw.WorkerSupportsCancellation == true)

            {

                bw.CancelAsync();

            }

        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)

        {

            BackgroundWorker worker = sender as BackgroundWorker;


            for (int i = 1; (i <= 10); i++)

            {

                if ((worker.CancellationPending == true))

                {

                    e.Cancel = true;

                    break;

                }

                else

                {

                    // Perform a time consuming operation and report progress.

                    System.Threading.Thread.Sleep(500);

                    worker.ReportProgress((i * 10));

                }

            }

        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

        {

            if ((e.Cancelled == true))

            {

                this.tbProgress.Text = "Canceled!";

            }


            else if (!(e.Error == null))

            {

                this.tbProgress.Text = ("Error: " + e.Error.Message);

            }


            else

            {

                this.tbProgress.Text = "Done!";

            }

        }

        private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)

        {

            this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");

        }

    }

}


XAML:

 

Exemplo 2 - Delegate

BackgroundWorker worker = new BackgroundWorker();

worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    args.Result = CalculationMethod();
};

worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
    object result = args.Result;
};

worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
{     
    int percentage = args.ProgressPercentage;
};

worker.RunWorkerAsync();


Usando o Dispatcher para acessar os controles em outro processo

Você pode querer alterar a interface do usuário a partir de um processo de trabalho. Por exemplo, você pode querer habilitar ou desabilitar botões, ou mostrar uma ProgressBar que fornece informações sobre o andamento da execução, que é feito pelo método ReportProgress. O modelo de encadeamento WPF fornece a classe Dispatcher para chamadas de thread cruzado. Ao utilizar o Dispatcher, você pode atualizar de forma segura a interface do usuário de threads de trabalho que estão rodando em background.

Você pode obter uma referência para o objeto Dispacther para um elemento da interface do usuário a partir de sua propriedade Dispatcher.

System.Windows.Threading.Dispatcher aDisp = Button1.Dispatcher;

Dispatcher fornece dois métodos principais que você irá usar; Invoke e BeginInvoke. Ambos os métodos permite que você chame o método com segurança. O método BeginInvoke permite que você chame o método de forma assíncrona, e o método Invoke permite que você chame o método de forma síncrona.

Vamos dizer que eu tenho uma tarefa demorada e que eu quero executar esta tarefa demorada uma Thread, mas eu também quero mostrar um diálogo de progresso que mostra uma mensagem e a porcentagem da tarefa concluída. Eu também quero permitir que o usuário cancelar o processo a qualquer momento. Então, a primeira coisa que precisa fazer é criar minha janela de diálogo de progresso que tem um controle para mostrar o percentual concluído, uma barra de progresso para mostrar graficamente o progresso, e um botão Cancelar para que o usuário possa cancelar o processo.

O código ficaria assim:

//our bg worker
BackgroundWorker worker;
//our progress dialog window
ProgressDialog pd;
 
private void btnDispacther_Click(object sender, RoutedEventArgs e)
{
    int maxRecords = 1000;
 
    pd = new ProgressDialog();
 
    //hook into the cancel event
    pd.Cancel += CancelProcess;
 
    //get our dispatcher
    System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
 
    //create our background worker and support cancellation
    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
 
    worker.DoWork += delegate(object s, DoWorkEventArgs args)
    {
        for (int x = 1; x < maxRecords; x++)
        {
            if (worker.CancellationPending)
            {
                args.Cancel = true;
                return;
            }
 
            System.Threading.Thread.Sleep(10);
 
            //create a new delegate for updating our progress text
            UpdateProgressDelegate update = new UpdateProgressDelegate(UpdateProgressText);
 
            //invoke the dispatcher and pass the percentage and max record count
            pdDispatcher.BeginInvoke(update, Convert.ToInt32(((decimal)x / (decimal)maxRecords) * 100), maxRecords);
        }
    };
 
    worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
    {
        pd.Close();
    };
 
    //run the process then show the progress dialog
    worker.RunWorkerAsync();
    pd.ShowDialog();
}
 
//our delegate used for updating the UI
public delegate void UpdateProgressDelegate(int percentage, int recordCount);
 
//this is the method that the deleagte will execute
public void UpdateProgressText(int percentage, int recordCount)
{
    //set our progress dialog text and value
    pd.ProgressText = string.Format("{0}% of {1} Records", percentage.ToString(), recordCount);
    pd.ProgressValue = percentage;
}
 
void CancelProcess(object sender, EventArgs e)
{
    //cancel the process
    worker.CancelAsync();
}
 
Fonte: 
http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/
https://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
http://stackoverflow.com/questions/9732709/the-calling-thread-cannot-access-this-object-because-a-different-thread-owns-it 

 



Comentários

Postagens mais visitadas deste blog

EmguCV DLL Not Found Exception -Unable to find cvextern?

Eu também tive os mesmos problemas. Neste fórum emgucv , não sei por que cvextern.dlldo emgucv 3.4.3 não é possível carregar cvextern (porque não é possível encontrar cvextern), e você permitiu usar o arquivo cvextern.dlldo emgucv 3.4.2 (certifique-se de escolher x64 / x86), copie /bin/Debuge este arquivo é encontrado automaticamente /bin/Debug/x86ou /bin/Debug/x64quando você termina de construir o programa no Visual Studio.  O Erro:  The type initializer for 'Emgu.CV.CvInvoke' threw an exception. : Unable to load DLL 'cvextern': The specified module could not be found. (Exception from HRESULT: 0x8007007E)  Para resolver o problema copia a pasta x64 ou x86 para dentro da pasta bin da aplicação. Ou copie os arquivos para dentro da pasta bin da aplicação WCF.

Vagas .Net - Projeto CNSA

Prezados, Estamos selecionando candidados, em forma de cadastro de reserva, para participar do projeto: Cadastro Nacional de Sociedade de Advogados, na Assessoria de Tecnologia da Informação do Conselho Federal da OAB. Basicamente teremos 3 fases: * Avaliação Curricular; * Entrevista Pessoal; * Verificação prática; Outras informações: Vaga: Desenvolvedor .NET. Quantidade: 3. Tempo: 4 meses. Segue abaixo os requisitos que devem ser atendidos pelo candidato: Conhecimentos básicos * Conhecimento avançado em C#; * Conhecimento em ASP.NET 2.0; * ADO.NET 2.0; * SQL SERVER 2005 (DML, stored procedures, MER); * UML (Interpretação de casos de uso, diagrama de atividades, de classe, de sequência); * Experiência em projetos anteriores com ASP.NET e C#; Desejável * LINQ TO SQL; * Conhecimento em JavaScript e padrões Web (XHTML, CSS, etc); * ASP.NET 3.5; * C# 3.0 (LINQ, Propriedade implementadas automaticamente, Métodos de Extensão, Expressão Lamb...

Lista de IPs DNS Oi nos Estados

As vezes quando colocamos um IP fixo para uma máquina na rede, e esta rede tem acesso a internet através da rede da Oi, bem provável que você não conseguirá acessar a internet. Para resolver o problema defina o DNS primário e Secundário nas propriedades da placa de rede. - Protocolo TCP/IP Versão 4 (TCP/IPv4). Segue lista de DNS por estado. RS 201.10.1.2 201.10.120.3 SC 201.10.120.3 201.10.1.2 PR 201.10.120.2 201.10.128.3 GO 201.10.128.3 201.10.120.3 DF 201.10.128.2 201.10.120.2 MS 201.10.128.3 201.10.1.2 MT 201.10.128.2 201.10.120.3 RO/AC 201.10.128.2 201.10.1.2