Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open a second winform asynchronously but still behave as a child to the parent form?

Tags:

c#

winforms

I am creating an application and I would like to implement a progress window that appears when a lengthy process is taking place.

I've created a standard windows form project to which I've created my app using the default form. I've also created a new form to use as a progress window.

The problem arises when i open the progress window (in a function) using:

ProgressWindow.ShowDialog();

When this command is encountered, the focus is on the progress window and I assume it's now the window who's mainloop is being processed for events. The downside is it blocks the execution of my lengthy operation in the main form.

If I open the progress window using:

ProgressWindow.Show();

Then the window opens correctly and now doesn't block the execution of the main form but it doesn't act as a child (modal) window should, i.e. allows the main form to be selected, is not centered on the parent, etc..

Any ideas how I can open a new window but continue processing in the main form?

like image 321
Gary Willoughby Avatar asked Mar 12 '10 20:03

Gary Willoughby


People also ask

What is an MDI child Form?

MDI child forms are an essential element of Multiple-Document Interface (MDI) applications, as these forms are the center of user interaction. In the following procedure, you'll use Visual Studio to create an MDI child form that displays a RichTextBox control, similar to most word-processing applications.


3 Answers

You probably start your lengthy operation in a separate worker thread (e.g. using a background worker). Then show your form using ShowDialog() and on completion of the thread close the dialog you are showing.

Here is a sample - in this I assume that you have two forms (Form1 and Form2). On Form1 I pulled a BackgroundWorker from the Toolbox. Then I connected the RunWorkerComplete event of the BackgroundWorker to an event handler in my form. Here is the code that handles the events and shows the dialog:

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();
    }
}
like image 170
AxelEckenberger Avatar answered Oct 06 '22 00:10

AxelEckenberger


I implemented something very similar to this for another project. This form allows you to popup a modal dialog from within a worker thread:

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();
    }
}

To use, you'll need to set the SyncContext from somewhere in your GUI thread:

NotificationForm.SyncContext = SynchronizationContext.Current;
like image 22
Dan Bryant Avatar answered Oct 06 '22 01:10

Dan Bryant


Another option:

Use ProgressWindow.Show() & implement the modal-window behavior yourself. parentForm.Enabled = false, position the form yourself, etc.

like image 43
Conrad Albrecht Avatar answered Oct 06 '22 01:10

Conrad Albrecht