Compact Framework/Threading - MessageBox é exibido sobre outros controles após a opção ser escolhida

StackOverflow https://stackoverflow.com/questions/10071

Pergunta

Estou trabalhando em um aplicativo que captura e instala várias atualizações de um servidor externo e preciso de ajuda com threading.O usuário segue este processo:

  • Botão de cliques
  • O método verifica se há atualizações, a contagem é retornada.
  • Se for maior que 0, pergunte ao usuário se deseja instalar usando MessageBox.Show().
  • Se sim, ele executa um loop e chama BeginInvoke() no método run() de cada atualização para executá-lo em segundo plano.
  • Minha classe de atualização possui alguns eventos que são usados ​​para atualizar uma barra de progresso, etc.

As atualizações da barra de progresso estão corretas, mas o MessageBox não é totalmente apagado da tela porque o loop de atualização começa logo após o usuário clicar em sim (veja a captura de tela abaixo).

  • O que devo fazer para que a caixa de mensagens desapareça instantaneamente antes do início do ciclo de atualização?
  • Devo usar Threads em vez de BeginInvoke()?
  • Devo fazer a verificação inicial da atualização em um thread separado e chamar MessageBox.Show() desse thread?

Código

// Button clicked event handler code...
DialogResult dlgRes = MessageBox.Show(
    string.Format("There are {0} updates available.\n\nInstall these now?", 
    um2.Updates.Count), "Updates Available", 
    MessageBoxButtons.YesNo, 
    MessageBoxIcon.Question, 
    MessageBoxDefaultButton.Button2
);

if (dlgRes == DialogResult.Yes)
{
    ProcessAllUpdates(um2); 
}

// Processes a bunch of items in a loop
private void ProcessAllUpdates(UpdateManager2 um2)
{
    for (int i = 0; i < um2.Updates.Count; i++)
    {
        Update2 update = um2.Updates[i];

        ProcessSingleUpdate(update);

        int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);

        UpdateOverallProgress(percentComplete);
    }
}

// Process a single update with IAsyncResult
private void ProcessSingleUpdate(Update2 update)
{
    update.Action.OnStart += Action_OnStart;
    update.Action.OnProgress += Action_OnProgress;
    update.Action.OnCompletion += Action_OnCompletion;

    //synchronous
    //update.Action.Run();

    // async
    IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
}

Captura de tela

Windows Mobile Bug

Foi útil?

Solução

Sua IU não está sendo atualizada porque todo o trabalho está acontecendo no thread da interface do usuário.Sua chamada para:

this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); }) 

está dizendo invocar update.Action.Run() no thread que criou "this" (seu formulário), que é o thread da interface do usuário.

Application.DoEvents()

de fato, dará ao thread da interface do usuário a chance de redesenhar a tela, mas ficaria tentado a criar um novo delegado e chamar BeginInvoke sobre isso.

Isso executará a função update.Action.Run() em um thread separado alocado no pool de threads.Você pode então continuar verificando o IAsyncResult até que a atualização seja concluída, consultando o objeto de atualização quanto ao seu progresso após cada verificação (porque você não pode fazer com que o outro thread atualize a barra de progresso/IU) e, em seguida, chamando Application.DoEvents().

Você também deve chamar EndInvoke() depois, caso contrário você pode acabar vazando recursos

Eu também ficaria tentado a colocar um botão cancelar na caixa de diálogo de progresso e adicionar um tempo limite; caso contrário, se a atualização travar (ou demorar muito), seu aplicativo ficará bloqueado para sempre.

Outras dicas

Você já tentou colocar um

Application.DoEvents()

aqui

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}

@John Sably

Você pode se safar não chamar EndInvoke ao lidar com WinForms sem quaisquer consequências negativas.

A única exceção documentada à regra que conheço está no Windows Forms, onde você está oficialmente autorizado a chamar Control.BeginInvoke sem se preocupar em chamar Control.EndInvoke.

No entanto, em todos os outros casos, ao lidar com o padrão Begin/End Async, você deve assumir que ele irá vazar, como afirmou.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top