Диалоговое окно WPF, пожалуйста, подождите

Я разрабатываю настольное приложение WPF на С# 4.0, которое должно обрабатывать множество длительных операций (загрузка данных из БД, расчет симуляций, оптимизация маршрутов и т. д.).

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

То, что у меня есть, пока работает, но есть некоторые проблемы: новый WaitXUI создается с использованием метода Create-factory. Метод Create ожидает текст заголовка и ссылку на главный элемент управления, который должен быть заблокирован. Метод Create устанавливает StartupLocation окна, текст заголовка и хост для блокировки:

WaitXUI wait = WaitXUI.Create("Simulation running...", this);
wait.ShowDialog(new Action(() =>
{
    // long running operation
}));

Затем с помощью перегруженного метода ShowDialog можно отобразить WaitXUI. Перегрузка ShowDialog ожидает действие, которое завершает длительную операцию.

В перегрузке ShowDialog я просто запускаю действие в его собственном потоке, а затем отключаю элемент управления хоста (устанавливаю Opacity на 0,5 и устанавливаю IsEnabled на false) и вызываю ShowDialog базового класса.

public bool? ShowDialog(Action action) 
    {
    bool? result = true;

    // start a new thread to start the submitted action
    Thread t = new Thread(new ThreadStart(delegate()
    {
        // start the submitted action
        try
        {
            Dispatcher.UnhandledException += Dispatcher_UnhandledException;
            Dispatcher.Invoke(DispatcherPriority.Normal, action);
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            // close the window
            Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
            this.DoClose();
        }
    }));
    t.Start();

    if (t.ThreadState != ThreadState.Stopped)
    {
        result = this.ShowDialog();
    }
    return result;
}

private new bool? ShowDialog() 
{
    DisableHost();
    this.Topmost = true;
    return base.ShowDialog();
}

private void DisableHost()
{
    if (host != null)
    {
        host.Dispatcher.Invoke(new Action(delegate()
        {
            this.Width = host.Width - 20;
            host.Cursor = Cursors.Wait;
            host.IsEnabled = false;
            host.Opacity = 0.5;
        }));
    }
}

Вот проблемы с этим:

  • Отключение элемента управления хоста приводит к потере информации о состоянии (SelectedItems...)
  • Иногда WaitXUI отображается всего несколько миллисекунд, когда поток завершается через несколько мс после отображения WaitXUI.
  • Иногда диалоговое окно вообще не появляется, хотя поток все еще работает.

Это основные проблемы, которые приходят мне на ум в данный момент. Как можно улучшить эту концепцию или какие другие методы можно использовать для решения этой проблемы?

Заранее спасибо!


person goflo    schedule 12.02.2014    source источник
comment
Даже не собираюсь публиковать как вопрос, так как это уродливо. Установите для каждого элемента управления значение IsEnabled = false. Или захватите любой ввод в окне и установите e.handled = true.   -  person paparazzo    schedule 12.02.2014
comment
Это точно дубликат.   -  person UIlrvnd    schedule 12.02.2014
comment
@Blam Но это не решает ни одной из моих проблем.   -  person goflo    schedule 13.02.2014
comment
@Akane Есть похожие вопросы, но все с другими (более низкими) требованиями! Указывать мой вопрос как дубликат и не публиковать решение бесполезно ;-)   -  person goflo    schedule 13.02.2014


Ответы (2)


Немного нестандартного мышления всегда помогает при разработке приложений WPF. Вы можете легко выполнить свои требования, используя только свойство Grid, Rectangle, bool (которое у вас уже может быть) и BooleanToVisibilityConverter, и вам не придется отключать какие-либо элементы управления.

Идея проста. Добавьте белый Rectangle перед содержимым вашего представления с его свойством Opacity, установленным между 0.5 и около 0.75. Данные связывают свое свойство Visibility со свойством bool в вашей модели представления или коде и подключают BooleanToVisibilityConverter:

<Grid>
    <Grid>
        <!--Put your main content here-->
    </Grid>
    <Rectangle Fill="White" Opacity="0.7" Visibility="{Binding IsWaiting, 
        Converter={StaticResource BooleanToVisibilityConverter}}" />
    <!--You could add a 'Please Wait' TextBlock here-->
</Grid>

Теперь, когда вы хотите отключить элементы управления, вы просто устанавливаете для свойства bool значение true, и Rectangle сделает пользовательский интерфейс блеклым:

IsWaiting = true;
person Sheridan    schedule 12.02.2014
comment
Спасибо за Ваш ответ. Эту же концепцию использует BusyIndicator (предложенный Юджином П). Было бы возможным решением. Единственное, что мне не нравится, это то, что мне приходится добавлять приведенный выше код в каждый пользовательский интерфейс, из которого вызывается WaitXUI. И есть много пользовательских интерфейсов (> 50), использующих диалоговое окно ожидания. Было бы неплохо, если бы это можно было сделать в одном месте (в WaitXUI)! :-/ - person goflo; 13.02.2014
comment
Вы, кажется, неправильно поняли... Если вы поместите это в MainWindow.xaml, а ваше свойство bool в модель основного представления, вам понадобится только одно. Поместите весь ваш обычный контент приложения там, где указано Put your main content here. - person Sheridan; 13.02.2014
comment
Хорошо, вы правы. Мне нужен этот кусок кода только в каждом окне, а не в каждом пользовательском интерфейсе. Я попробую это. Спасибо! - person goflo; 18.02.2014
comment
Без полного решения с концепцией BoolToVisibilityConverter или каких-либо намеков на это ответ не является полным. - person Adam Ri; 06.05.2019
comment
Это твой способ попросить разъяснений, @AdamRi? Если бы вы правильно прочитали мой ответ, вы бы увидели, что я упомянул стандарт BooleanToVisibilityConverter, который входит в состав .NET Framework. Поэтому вам было бы ясно, что ссылка на таинственный BoolToVisibilityConverter была не чем иным, как опечаткой и что вместо этого вы должны использовать вышеупомянутый BooleanToVisibilityConverter. Но спасибо за ваш комментарий. - person Sheridan; 07.05.2019

Не нужно создавать собственную реализацию, я думаю, что это избыточно.

взгляните на уже созданный компонент, такой как BusyIndicator, для аналогичных целей. что жизненно необходимо и эффективно. .

дополнительная информация из codeplex

person Eugene P.    schedule 12.02.2014
comment
Спасибо за Ваш ответ. BusyIndicator использует ту же концепцию, предложенную Шариданом. Это было бы возможным решением. Единственное, что мне не нравится, это то, что мне приходится добавлять код пользовательского интерфейса для BusyIndicator в каждый пользовательский интерфейс, из которого вызывается WaitXUI. И есть много пользовательских интерфейсов (> 50), использующих диалоговое окно ожидания. Было бы неплохо, если бы это можно было сделать в одном месте (в WaitXUI)! :-/ - person goflo; 13.02.2014
comment
Я не уверен, что у меня есть идея, которую вы пытаетесь достичь, но в любом случае, busyindicator является привязываемым, вы можете установить его в заполнитель корневого представления, а затем внедрить любое дочернее представление (страница/и т. д.). Затем вы можете легко использовать Binding для настройки управления. - person Eugene P.; 13.02.2014