Открывать вторую winform асинхронно, но все же вести себя как ребенок в родительской форме?

Я создаю приложение, и я хотел бы реализовать окно прогресса, которое появляется, когда происходит длительный процесс.

Я создал стандартный проект формы Windows, к которому я создал свое приложение, используя форму по умолчанию. Я также создал новую форму для использования в качестве окна прогресса.

Проблема возникает, когда я открываю окно выполнения (в функции), используя:

ProgressWindow.ShowDialog();

Когда эта команда встречается, фокус находится в окне выполнения, и я предполагаю, что теперь это окно, которое mainloop обрабатывается для событий. Недостатком является то, что он блокирует выполнение моей длительной операции в основной форме.

Если я открою окно выполнения, используя:

ProgressWindow.Show();

Затем окно открывается правильно и теперь не блокирует выполнение основной формы, но оно не действует как дочернее (модальное) окно, то есть позволяет выбрать основную форму, не центрируется на родительском, и т.д.

Любые идеи, как я могу открыть новое окно, но продолжить обработку в основной форме?

Ответы

Ответ 1

Вероятно, вы начинаете свою длительную операцию в отдельном рабочем потоке (например, используя фонового работника). Затем покажите свою форму с помощью ShowDialog() и по завершении потока закройте диалоговое окно, которое вы показываете.

Вот пример - в этом я предполагаю, что у вас две формы (Form1 и Form2). На Form1 я вытащил BackgroundWorker из панели инструментов. Затем я связал событие RunWorkerComplete BackgroundWorker с обработчиком событий в моей форме. Вот код, который обрабатывает события и показывает диалог:

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

Ответ 2

Другая опция:

Используйте ProgressWindow.Show() и сами реализуйте поведение модального окна. parentForm.Enabled = false, расположите форму самостоятельно и т.д.

Ответ 3

Я реализовал что-то очень похожее на это для другого проекта. Эта форма позволяет вам отображать модальный диалог внутри рабочего потока:

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

Чтобы использовать, вам нужно установить SyncContext где-то в вашем потоке графического интерфейса:

NotificationForm.SyncContext = SynchronizationContext.Current;