Ожидание завершения ожидающих операций без блокировки потока пользовательского интерфейса - PullRequest
0 голосов
/ 21 января 2011

У меня есть приложение, подобное MVP, все дорогостоящие операции используют асинхронные вызовы и отображают GIF-подобный Ajax, который указывает пользователю, что что-то происходит, не блокируя основной поток.

Пример: форма ввода данных, пользовательнажимает кнопку Сохранить, выполняется асинхронная операция, и после ее завершения восстанавливает экран до редактируемой формы без блокировки потока пользовательского интерфейса (другими словами, не блокируя другие видимые окна в приложении).

Здесь все работает нормально, но с учетом следующего сценария:

Пользователь пытается закрыть форму и получает подтверждающее сообщение, которое спрашивает пользователя, уверен ли он, что он собирается закрыть, если он предпочитает сохранить перед закрытием.Когда пользователи нажимают «Сохранить», происходит та же логика, что и раньше, но я вынужден ждать завершения этого вызова в потоке пользовательского интерфейса (в случае возникновения ошибок в асинхронном вызове или чего-либо еще), и я не могунайти способ сделать это по-другому, не блокируя поток пользовательского интерфейса.

Есть предложения?Спасибо!

--- Редактировать ---- То, что я сейчас делаю, ждет всех моих WaitHandles в Presenter с этим циклом:

while (!WaitHandles.All(h => h.WaitOne(1)))
    Application.DoEvents();

Это выглядит немного грязно.. но, по крайней мере, он имитирует не блокирование потока.Это то, что я по какой-то причине не должен делать?

1 Ответ

0 голосов
/ 22 января 2011

Вот пример «метода скрытия». Конечно, это не MVP, это просто пример.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

class Form1 : Form
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    public Form1()
    {
        Text = "First Form";
        Button button;
        Controls.Add(button = new Button { Text = "Launch 2nd Form", AutoSize = true, Location = new Point(10, 10) });
        button.Click += (s, e) => new Form2 { StartPosition = FormStartPosition.Manual, Location = new Point(Right, Top) }.Show(this);
    }
}

class Form2 : Form
{
    public Form2()
    {
        Text = "Second Form";
        dirty = true;
    }

    private bool dirty;

    protected override void OnClosing(CancelEventArgs e)
    {
        DialogResult result;
        if (dirty && (result = new ConfirmSaveForm().ShowDialog(this)) != DialogResult.No)
        {
            if (Owner != null)
                Owner.Activate();
            Hide();
            e.Cancel = true;
            SaveAsync(result == DialogResult.Cancel);
        }
        base.OnClosing(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        Trace.WriteLine("Second Form Closed");
        base.OnClosed(e);
    }

    private void SaveAsync(bool fail)
    {
        SaveAsyncBegin();
        var sad = new Action<bool>(PerformAsyncSave);
        sad.BeginInvoke(fail, (ar) =>
        {
            try { sad.EndInvoke(ar); }
            catch (Exception ex) { Invoke(new Action<Exception>(SaveAsyncException), ex); return; }
            Invoke(new Action(SaveAsyncEnd));
        }, null);
    }

    private void SaveAsyncBegin()
    {
        // Update UI for save
    }

    private void PerformAsyncSave(bool fail)
    {
        Trace.WriteLine("Begin Saving");
        Thread.Sleep(1000); // Do some work
        if (fail)
        {
            Trace.WriteLine("Failing Save");
            throw new Exception("Save Failed");
        }
        dirty = false;
    }

    private void SaveAsyncEnd()
    {
        Trace.WriteLine("Save Succeeded");
        Close();
    }

    private void SaveAsyncException(Exception ex)
    {
        Trace.WriteLine("Save Failed");
        Show();
        MessageBox.Show(this, ex.Message, "Save Failed", MessageBoxButtons.OK, MessageBoxIcon.Stop);
    }
}

class ConfirmSaveForm : Form
{
    public ConfirmSaveForm()
    {
        Text = "Confirm Save";
        FormBorderStyle = FormBorderStyle.FixedDialog;
        ControlBox = false;
        ClientSize = new Size(480, 50);
        StartPosition = FormStartPosition.CenterParent;
        Controls.Add(new Button { Text = "Yes, Fail", DialogResult = DialogResult.Cancel, Size = new Size(150, 30), Location = new Point(10, 10) });
        Controls.Add(new Button { Text = "Yes, Succeed", DialogResult = DialogResult.Yes, Size = new Size(150, 30), Location = new Point(160, 10) });
        Controls.Add(new Button { Text = "No", DialogResult = DialogResult.No, Size = new Size(150, 30), Location = new Point(320, 10) });
        AcceptButton = Controls[0] as IButtonControl;
    }
}
...