Попытка отобразить сообщение «подождите» в WPF во время длительной операции

У меня есть please wait TextBlock в моем XAML, свойство Visibility которого связано со свойством IsBusy в моей модели представления (которая реализует INotifyPropertyChanged). Отдельная кнопка запускает долгоиграющий (2-3 секунды) процесс, вот так:

IsBusy = true;

try
{
    // Do some long-running process
}

finally
{
    IsBusy = false;
}

Но сообщение please wait никогда не появляется. Я предполагаю, что операция выполняется в потоке пользовательского интерфейса и, следовательно, не дает ей возможности обновиться? Это действительно работает, если я запускаю приведенный выше код в отдельном потоке, но я не хочу, чтобы пользователь что-либо делал во время выполнения операции — я рад, что пользовательский интерфейс freeze.

Как отобразить сообщение please wait? Или мне лучше запустить его в фоновом потоке и (каким-то образом) заблокировать пользовательский интерфейс на время? Я использую .Net 4, кстати.


person Andrew Stephens    schedule 30.01.2013    source источник
comment
Почему бы не запустить его в фоновом потоке и не привязать IsEnabled вашего Form/Window/UserControl/etc к свойству IsBusy? Затем, когда он становится занятым, все окно отключается, чтобы пользователь не мог его изменить, не блокируя ваше приложение?   -  person Rachel    schedule 30.01.2013
comment
@Rachel иногда самые простые решения являются лучшими - спасибо! Однако сейчас возникла другая проблема, но это вопрос для другого ТАК...   -  person Andrew Stephens    schedule 30.01.2013
comment
Хорошо, я расширил этот комментарий до ответа для вас :)   -  person Rachel    schedule 30.01.2013


Ответы (3)


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

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

Лучшим решением было бы привязать IsEnabled вашего Form/Window/UserControl/etc к свойству IsBusy. Затем, когда он становится занятым, вся форма отключается, чтобы пользователь не мог ее изменить, не блокируя при этом ваше приложение.

person Rachel    schedule 30.01.2013

Во-первых, свойство Visibility не равно bool, поэтому, если вы хотите привязать к нему переменную bool, вам нужно использовать IValueConverter, а во-вторых, да, вы правы, пока поток пользовательского интерфейса занят вашей длительной операцией, он не будет выполнять ничего другого, включая видимость. сдача.

Я бы предложил использовать WPF Toolkit BusyIndicator, ожидая установки собственной панели, у нее есть свойство IsBusy bool.

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

IsBusy = true;
Task.Factory.StartNew(() => 
{ 
  // Do work. 
})
.ContinueWith(t=>IsBusy=false, 
                        TaskScheduler.FromCurrentSynchronizationContext());

Также обратите внимание на проверку ошибок в методе ContinueWith, иначе вы получите исключение при удалении Task.

person Arsen Mkrtchyan    schedule 30.01.2013

Если вы запускаете свой код в отдельном потоке, вы по-прежнему можете получить доступ к пользовательскому интерфейсу через < strong>Метод Dispatcher.BeginInvoke.

if (!YOURCONTROL.Dispatcher.CheckAccess())
{
    YOURCONTROL.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(delegate()
        {
            //do something with the control/form/...
        }));
}
else
{
    //update your control
}
person Abbas    schedule 30.01.2013
comment
Это то, что я делал, но, как было сказано изначально, я не хотел работать в фоновом потоке. Оглядываясь назад, вероятно, лучше заблокировать пользовательский интерфейс контролируемым образом, а не просто позволить ему зависнуть. - person Andrew Stephens; 30.01.2013