Marco compacto/Threading: el cuadro de mensaje se muestra sobre otros controles después de elegir la opción

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

Pregunta

Estoy trabajando en una aplicación que captura e instala un montón de actualizaciones desde un servidor externo y necesito ayuda con los subprocesos.El usuario sigue este proceso:

  • Botón de clics
  • El método busca actualizaciones y se devuelve el recuento.
  • Si es mayor que 0, pregunte al usuario si desea instalar utilizando MessageBox.Show().
  • En caso afirmativo, se ejecuta a través de un bucle y llama a BeginInvoke() en el método run() de cada actualización para ejecutarlo en segundo plano.
  • Mi clase de actualización tiene algunos eventos que se utilizan para actualizar una barra de progreso, etc.

Las actualizaciones de la barra de progreso están bien, pero el cuadro de mensajes no se borra completamente de la pantalla porque el ciclo de actualización comienza justo después de que el usuario hace clic en Sí (ver captura de pantalla a continuación).

  • ¿Qué debo hacer para que el cuadro de mensaje desaparezca instantáneamente antes de que comience el ciclo de actualización?
  • ¿Debería utilizar Threads en lugar de BeginInvoke()?
  • ¿Debería realizar la verificación de actualización inicial en un hilo separado y llamar a MessageBox.Show() desde ese hilo?

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 pantalla

Windows Mobile Bug

¿Fue útil?

Solución

Su interfaz de usuario no se actualiza porque todo el trabajo se realiza en el hilo de la interfaz de usuario.Tu llamada a:

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

está diciendo invocar update.Action.Run() en el hilo que creó "esto" (su formulario), que es el hilo de la interfaz de usuario.

Application.DoEvents()

De hecho, le dará al hilo de la interfaz de usuario la oportunidad de volver a dibujar la pantalla, pero estaría tentado de crear un nuevo delegado y llamar a BeginInvoke al respecto.

Esto ejecutará la función update.Action.Run() en un subproceso separado asignado del grupo de subprocesos.Luego puede seguir verificando IAsyncResult hasta que se complete la actualización, consultando el progreso del objeto de actualización después de cada verificación (porque no puede hacer que el otro hilo actualice la barra de progreso/UI) y luego llamando a Application.DoEvents().

También se supone que debes llamar a EndInvoke() después, de lo contrario puedes terminar perdiendo recursos.

También me sentiría tentado a poner un botón de cancelar en el cuadro de diálogo de progreso y agregar un tiempo de espera; de lo contrario, si la actualización se atasca (o tarda demasiado), su aplicación se habrá bloqueado para siempre.

Otros consejos

¿Has probado a poner un

Application.DoEvents()

aquí

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

@ John Sibly

Puedes salirte con la tuya no llamar a EndInvoke cuando se trata de WinForms sin consecuencias negativas.

La única excepción documentada a la regla que yo sepa es en Windows Forms, donde oficialmente se le permite llamar a Control.BeginInvoke sin molestarse en llamar a Control.EndInvoke.

Sin embargo, en todos los demás casos, cuando se trata del patrón Begin/End Async, debe asumir que se filtrará, como usted indicó.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top