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

A batalha por uma SafeKid (Unicamp)

Figura 1 - Cadeira Safekid (Unicamp) Nas consultas usando a ferramenta de busca do Google obtive os seguintes link para a tal cadeira. 1- http://revistacrescer.globo.com/Crescer/0,19125,EFC1010380-2213-2,00.html 2- http://www.ibiubi.com.br/produtos/cadeira-cinto-de-seguran%c3%a7a-safekid-projetado-pela-unicamp+beb%c3%aas-e-crian%c3%a7as+cadeiras/IUID4138740/ 3- http://compras.hsw.uol.com.br/assento-infantil-guia.htm 4- http://produto.mercadolivre.com.br/MLB-123086053-cadeiracinto-de-seguranca-safekid-projetado-pela-unicamp-_JM a própria! só que venda finalizada. Foi então que resolvi procurar no Bing (Microsoft) www.bing.com. Também não encontrei! Rs Encontrei algumas parecidas a preços imbatíveis, mas fora do país. 1- http://www.magazineluiza.com.br/produto/index_Produto.asp?Produto=1766074&linha=BB&Setor=CADA&modelo=08 2- http://www.precomania.com/search_attrib.php/page_id=1990/st=page/page=1 Mas foi no Yahoo buscas que eu encontrei a dita cuja. Quero dizer, um possível ...

Driver para Receptor de TV Digital da Multilaser RE003

Email recebido da central de atendimento da Multilaser para resolver problema de funcionamento do receptor digital em Windows 7. --------------------------------------------------------------------------------- Prezado(a) Cliente, Primeiramente pedimos desculpas por quaisquer transtornos causados. Segue anexo manual passo a passo para a instalação do RE003. Conseguimos desenvolver um novo software para o aparelho, pois o enviado no cd não estava conseguindo localizar canais. Peço a gentileza de seguir o passo a passo e me informar se conseguiu utilizar o produto. Os drivers que irá precisar estão disponíveis no link http://tinyurl.com/j3w3svn Os drivers que irá precisar estão disponíveis no link  http://tinyurl.com/j3w3svn Os drivers que irá precisar estão disponíveis no link  http://tinyurl.com/j3w3svn Instale os drivers e o software Presto! Serial: EVALUATION

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.