等待挂起操作完成而不阻止UI线程

时间:2011-01-21 15:52:01

标签: winforms asynchronous c#-3.0 mvp

我有一个类似MVP的应用程序,所有昂贵的操作都使用Async调用并显示一个像gif这样的Ajax,指示用户发生了某些事情,而没有阻塞主线程。

实施例: 数据输入表单,用户单击“保存”,发生异步操作,完成后将屏幕恢复为可编辑的形式,而不会阻止UI线程(换句话说,不会阻止应用程序中的其他可见窗口)。

在这里一切正常,但考虑到以下情况:

用户尝试关闭表单,并收到一条确认消息,询问用户是否确定如果他在关闭前更喜欢保存将关闭。 当用户单击“保存”时,之前解释的逻辑相同,但是我被迫等待此调用在UI线程中完成(如果异步调用中有任何错误或者其他什么)我不能找到任何方式在不阻止UI线程的情况下以其他方式执行。

有什么建议吗?谢谢!

---编辑---- 我现在正在做的是在这个循环中等待Presenter中的所有WaitHandles:

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

感觉有点脏..但至少它模拟非阻塞线程。这是出于某种原因我不应该做的事情吗?

1 个答案:

答案 0 :(得分:0)

以下是“隐藏方法”的示例。当然,这不是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;
    }
}