2010-03-12 7 views
15

Estoy creando una aplicación y me gustaría implementar una ventana de progreso que aparece cuando se está llevando a cabo un largo proceso.¿Abra un segundo winform asincrónicamente pero aún se comporte como un hijo al formulario principal?

He creado un proyecto de formulario de Windows estándar para el cual he creado mi aplicación utilizando el formulario predeterminado. También creé un nuevo formulario para utilizar como ventana de progreso.

El problema surge cuando abro la ventana de progreso (en una función) usando:

ProgressWindow.ShowDialog(); 

Cuando se encuentra este comando, el foco está en la ventana de progreso y supongo que ahora es la ventana que está mainloop es siendo procesado para eventos. La desventaja es que bloquea la ejecución de mi operación prolongada en la forma principal.

Si abro la ventana de progreso usando:

ProgressWindow.Show(); 

A continuación, se abre la ventana correctamente y ahora no bloquea la ejecución de la forma principal, pero no actúa como una ventana secundaria (modal) debe , es decir, permite seleccionar el formulario principal, no está centrado en el elemento principal, etc.

¿Alguna idea de cómo puedo abrir una nueva ventana pero continuar procesando en el formulario principal?

Respuesta

9

Probablemente inicie su larga operación en una cadena de trabajo separada (por ejemplo, utilizando un trabajador de segundo plano). Luego, muestre su formulario usando ShowDialog() y al finalizar el hilo, cierre el diálogo que está mostrando.

Aquí hay un ejemplo: supongo que tiene dos formas (Form1 y Form2). En Form1 saqué BackgroundWorker de Toolbox. Luego conecté el evento RunWorkerComplete del BackgroundWorker a un controlador de eventos en mi formulario. Aquí está el código que se encarga de los eventos y muestra el cuadro de diálogo:

public partial class Form1 : Form 
{ 
    public Form1() { 
     InitializeComponent(); 
    } 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { 
     Thread.Sleep(5000); 
     e.Result = e.Argument; 
    } 

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
     var dlg = e.Result as Form2; 
     if (dlg != null) { 
      dlg.Close(); 
     } 
    } 

    private void button1_Click(object sender, EventArgs e) { 
     var dlg = new Form2(); 
     this.backgroundWorker1.RunWorkerAsync(dlg); 
     dlg.ShowDialog(); 
    } 
} 
+0

Ah sí, me se había olvidado por completo de los trabajadores de fondo! –

3

Otra opción:

Uso ProgressWindow.Show() & implementar el comportamiento modal-ventana de ti mismo. parentForm.Enabled = false, coloque el formulario usted mismo, etc.

+0

Hmm, pensé que era "lo mejor" (porque considero que ShowDialog() básicamente es un hack, debido a que está en la pila por mucho tiempo y llama a DoEvents(), pero no es "más fácil" (porque tienes que emular el comportamiento de diálogo modal usted mismo). Entonces, si crees que es más fácil, yo diría que síganlo. –

3

Implementé algo muy similar a esto para otro proyecto. Este formulario le permite un diálogo modal popup desde dentro de un subproceso de trabajo:

public partial class NotificationForm : Form 
{ 
    public static SynchronizationContext SyncContext { get; set; } 

    public string Message 
    { 
     get { return lblNotification.Text; } 
     set { lblNotification.Text = value; } 
    } 

    public bool CloseOnClick { get; set; } 

    public NotificationForm() 
    { 
     InitializeComponent(); 
    } 

    public static NotificationForm AsyncShowDialog(string message, bool closeOnClick) 
    { 
     if (SyncContext == null) 
      throw new ArgumentNullException("SyncContext", 
              "NotificationForm requires a SyncContext in order to execute AsyncShowDialog"); 

     NotificationForm form = null; 

     //Create the form synchronously on the SyncContext thread 
     SyncContext.Send(s => form = CreateForm(message, closeOnClick), null); 

     //Call ShowDialog on the SyncContext thread and return immediately to calling thread 
     SyncContext.Post(s => form.ShowDialog(), null); 
     return form; 
    } 

    public static void ShowDialog(string message) 
    { 
     //Perform a blocking ShowDialog call in the calling thread 
     var form = CreateForm(message, true); 
     form.ShowDialog(); 
    } 

    private static NotificationForm CreateForm(string message, bool closeOnClick) 
    { 
     NotificationForm form = new NotificationForm(); 
     form.Message = message; 
     form.CloseOnClick = closeOnClick; 
     return form; 
    } 

    public void AsyncClose() 
    { 
     SyncContext.Post(s => Close(), null); 
    } 

    private void NotificationForm_Load(object sender, EventArgs e) 
    { 
    } 

    private void lblNotification_Click(object sender, EventArgs e) 
    { 
     if (CloseOnClick) 
      Close(); 
    } 
} 

Para utilizar, tendrá que ajustar la SyncContext desde algún lugar de su hilo de interfaz gráfica de usuario:

NotificationForm.SyncContext = SynchronizationContext.Current; 
Cuestiones relacionadas