wpf BackgroundWorker - Что касается обновления пользовательского интерфейса - PullRequest
2 голосов
/ 30 марта 2011

Я использую диалог поиска файлов, чтобы позволить пользователю выбрать несколько изображений.Если выбрано много изображений, как ожидается, это займет немного времени.Ниже приведен пример того, что я делаю с выбранными изображениями.Я перебираю пути к изображениям и создаю экземпляр пользовательского элемента управления, пользовательский элемент управления имеет элемент управления изображением и несколько других элементов управления.Я создаю экземпляр этого элемента управления, а затем добавляю его в существующую stackPanel, созданную в xaml-файле связанного окна.Пример ниже работает отлично, но я пытаюсь лучше понять BackGroundWorker, я получаю основы того, как его настроить, с его событиями, и возвращаю значение, которое может обновить индикатор выполнения, но потому, что мой цикл требуетВремя вверх, указанное ниже, добавляет экземпляр usercontrol к существующей панели стека. Он не будет работать, находясь в другом потоке.Является ли BackGroundWorker чем-то, что работает для такого примера?Если так, то каков наилучший способ обновить пользовательский интерфейс (мою стековую панель), который находится вне потока?Я довольно новичок в wpf и никогда не использовал BackGroundWorker, кроме тестирования того, чтобы он просто обновлял прогресс со значением int, поэтому я надеюсь, что этот вопрос имеет смысл, если я далеко от цели, просто дайте мне знать.Спасибо за любые мысли.

Пример того, как я делаю это сейчас, который работает нормально.

protected void myMethod(string[] fileNames) {  
    MyUserControl uc;  

    foreach (String imagePath in fileNames) {  
        uc = new MyUserControl();  
        uc.setImage(imagePath);  
        stackPanel.Children.Add(uc);  
        progressBar.Value = ++counter;  
        progressBar.Refresh();  
    }  
}    

ниже этого класса у меня есть это, так что я могу иметь обновление progressBar:

public static class extensionRefresh {  
    private static Action EmptyDelegate = delegate() { };  

    public static void Refresh(this UIElement uiElement) {  
        uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);  
    }  
}

Ответы [ 2 ]

2 голосов
/ 30 марта 2011

Ознакомьтесь с этой статьей о Создание более отзывчивых приложений с помощью Dispatcher

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

. В WPF эта модель расширена с помощью класса DispatcherSynchronizationContext.Используя BackgroundWorker, Dispatcher используется автоматически для вызова многопоточных вызовов методов.Хорошая новость заключается в том, что, поскольку вы, вероятно, уже знакомы с этим общим шаблоном, вы можете продолжать использовать 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)
{
    // Doing UI stuff
    if (e.Cancelled)
    {
        statusText.Text = "Cancelled";
    }
    else if (e.Error != null) 
    {
        statusText.Text = "Exception Thrown";
    }
    else 
    {
        statusText.Text = "Completed";
    }
}
1 голос
/ 30 марта 2011

Использование только BackgroundWorker не решит вашу проблему, так как элементы, созданные в части DoWork, все еще будут происходить из потока, не являющегося пользовательским интерфейсом. Вы должны вызывать Freeze для любых объектов, которые вы собираетесь использовать в другом потоке. Однако только определенные объекты пользовательского интерфейса могут быть заблокированы. Возможно, вам придется загрузить изображения как BitmapImage s в фоновом потоке, а затем создать остальную часть вашего пользовательского элемента управления в потоке пользовательского интерфейса. Это все еще может достичь ваших целей, так как загрузка изображения, вероятно, самая тяжелая операция.

Просто не забудьте установить BitmapImage.CacheOption на OnLoad, чтобы он фактически загружал изображение при создании объекта, а не ожидал, пока его не нужно будет отобразить.

...