В WPF, что эквивалентно Suspend / ResumeLayout () и BackgroundWorker () из Windows Forms - PullRequest
13 голосов
/ 17 сентября 2008

Если я нахожусь в функции в коде позади, и я хочу реализовать отображение «Загрузка ...» в строке состояния, то имеет смысл следующее, но, как мы знаем из WinForms, это NoNo:

StatusBarMessageText.Text = "Loading Configuration Settings...";            
LoadSettingsGridData();
StatusBarMessageText.Text = "Done";

То, что мы все теперь из класса 101 WinForms Chapter 1, это форма не будет отображать изменения для пользователя, пока не завершится вся функция ... то есть сообщение "Загрузка" никогда не будет отображаться пользователю. Необходим следующий код.

Form1.SuspendLayout();    
StatusBarMessageText.Text = "Loading Configuration Settings...";                
Form1.ResumeLayout();

LoadSettingsGridData();

Form1.SuspendLayout();    
StatusBarMessageText.Text = "Done";
Form1.ResumeLayout();

Какова лучшая практика для решения этой фундаментальной проблемы в WPF?

Ответы [ 3 ]

32 голосов
/ 01 августа 2010

Лучшее и простое:

using(var d = Dispatcher.DisableProcessing())
{
    /* your work... Use dispacher.begininvoke... */
}

Или

IDisposable d;

try
{
    d = Dispatcher.DisableProcessing();
    /* your work... Use dispacher.begininvoke... */
} finally {
    d.Dispose();
}
2 голосов
/ 18 сентября 2008

При чтении статьи Шона Вильдермута Темы WPF: создание более отзывчивых приложений с помощью Dispatcher .

Я натолкнулся на следующее, в котором говорится, что вы можете использовать Background Worker, как и в WindowsForms. Представь, что:

BackgroundWorker Теперь, когда у вас есть представление о том, как работает Диспетчер, вы можете быть удивлены, узнав, что в большинстве случаев вы его не найдете. В Windows Forms 2.0 Microsoft представила класс для обработки потоков, не связанных с пользовательским интерфейсом, чтобы упростить модель разработки для разработчиков пользовательского интерфейса. Этот класс называется BackgroundWorker. На рисунке 7 показано типичное использование класса BackgroundWorker.

Рисунок 7 Использование BackgroundWorker в WPF

BackgroundWorker _backgroundWorker = new BackgroundWorker();

...

// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += 
    _backgroundWorker_RunWorkerCompleted;

// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);

...

// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Do something
}

// Completed Method
void _backgroundWorker_RunWorkerCompleted(
    object sender, 
    RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        statusText.Text = "Cancelled";
    }
    else if (e.Error != null) 
    {
        statusText.Text = "Exception Thrown";
    }
    else 
    {
        statusText.Text = "Completed";
    }
}

Компонент BackgroundWorker хорошо работает с WPF, поскольку под крышками он использует класс AsyncOperationManager, который, в свою очередь, использует класс SynchronizationContext для работы с синхронизацией. В Windows Forms AsyncOperationManager отключает класс WindowsFormsSynchronizationContext, производный от класса SynchronizationContext. Аналогично, в ASP.NET он работает с другим производным SynchronizationContext, который называется AspNetSynchronizationContext. Эти производные от SynchronizationContext классы знают, как обрабатывать межпотоковую синхронизацию вызова метода.

В WPF эта модель расширена с помощью класса DispatcherSynchronizationContext. Используя BackgroundWorker, Dispatcher используется автоматически для вызова многопоточных вызовов методов. Хорошей новостью является то, что, поскольку вы, вероятно, уже знакомы с этим общим шаблоном, вы можете продолжать использовать BackgroundWorker в своих новых проектах WPF.

0 голосов
/ 17 сентября 2008

Самый простой способ заставить это работать - добавить LoadSettingsGridData в очередь диспетчера. Если вы установите достаточно низкое значение DispatcherPriority для операции, произойдут операции макета, и вам будет хорошо.

StatusBarMessageText.Text = "Loading Configuration Settings...";
this.Dispatcher.BeginInvoke(new Action(LoadSettingsGridData), DispatcherPriority.Render);
this.Dispatcher.BeginInvoke(new Action(() => StatusBarMessageText.Text = "Done"), DispatcherPriority.Render);
...