如何在后台执行进程时显示 MessageDialog 并在完成后关闭它

How to Show a MessageDialog while executing a process in background and close it after finished

我正在使用 Gtk# 创建一个简单的桌面应用程序,当用户单击一个按钮时,我想显示一个 "loading indicator" MessageDialog 并在后台进行一些处理,当该过程完成时关闭对话框并更新 UI.

中的一些控件

我对 Gtk# 和 Mono 很陌生,所以我的代码如下所示:

protected void OnBtnClicked(object sender, EventArgs e)
{
    try
    {
        Task.Factory.StartNew(() =>
        {
            var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Info, ButtonsType.None, "Processing...");
            dlg.Run();          

            //Some sync calls to remote services
            //...

            //The process finished so close the Dialog
            dlg.Destroy();

            //Here: Update the UI with remote service's response
            //txtResult.Buffer.Text = result.Message;
        });
    }
    catch (Exception ex)
    {
        var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, ex.Message);
        dlg.Title = "Error";
        dlg.Run();
        dlg.Destroy();
    }
}

这段代码显示了 MessageDialog,但它从未关闭。

单声道版本:4.4.2

IDE:Xamarin Studio 社区版 6.0.2

Gtk# 版本:2.12.38

在阅读 guide for responsive Mono applications and asking to Miguel de Icaza through Twitter 之后,我找到了这样做的方法。

注意事项:

1) 切勿从另一个线程创建或尝试修改 UI 元素。

2) 如果您需要从另一个线程修改 UI 控件,请在该线程内使用 Application.Invoke() 方法。

3) MessageDialog class 中的 运行() 方法等待用户交互关闭,即单击关闭按钮或调用 Close/Destroy 事件的东西。在这种情况下使用该方法是错误的,因为我将从我的代码中关闭 MessageDialog,因此显示对话框的正确方法是 Show()。

考虑到这一点,我的最终代码如下所示:

protected void OnBtnClicked(object sender, EventArgs e)
{
    try
    {
        var mdCalculate = new MessageDialog(this, DialogFlags.Modal, MessageType.Info, ButtonsType.None, "Processing...");
        mdCalculate.Title = "Calculate";
        mdCalculate.Show();

        Task.Factory.StartNew(() =>
        {
            //Some sync calls to remote services
            //...

            //returns the data I will show in the UI, lets say it's a string
            return someData;
        }).ContinueWith((prevTask) =>
        {
            Application.Invoke((send, evnt) =>
            {
                txtResult.Buffer.Text = prevTask.Result; //this is the string I returned before (someData)
                mdCalculate.Hide();
                mdCalculate.Destroy();
            });
        });
    }
    catch (Exception ex)
    {
        var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, ex.Message);
        dlg.Title = "Error";
        dlg.Run();
        dlg.Destroy();
    }
}

演示: