Как показать MessageDialog при выполнении процесса в фоновом режиме и закрыть его после завершения

Я создаю простое настольное приложение с использованием Gtk #. Когда пользователь нажимает кнопку, я хочу показать «индикатор загрузки» MessageDialog и выполнить некоторую обработку в фоновом режиме, когда процесс завершится, закройте диалоговое окно и обновите некоторые элементы управления из пользовательского интерфейса. .

Я новичок в 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 Community Edition 6.0.2

Gtk# версия: 2.12.38


person Anon Dev    schedule 13.08.2016    source источник


Ответы (1)


Прочитав руководство по отзывчивым приложениям Mono и попросив Мигель де Икаса через Twitter нашел способ сделать это.

Что нужно учитывать:

1) Никогда не создавайте и не пытайтесь изменять элементы пользовательского интерфейса из другого потока.

2) Если вам нужно изменить элементы управления пользовательского интерфейса из другого потока, используйте метод Application.Invoke() внутри этого потока.

3) Метод Run() из класса MessageDialog ожидает закрытия взаимодействия с пользователем, т.е. нажмите кнопку закрытия или что-то, что вызывает событие 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();
    }
}

Демо:

введите здесь описание изображения

person Anon Dev    schedule 15.08.2016