Вызовы WPF не работают во время длительной обработки метода - PullRequest
1 голос
/ 13 апреля 2010

Следующий метод не применяет изменения wpf (фон = красный) до выхода из второго метода (DoWork):

private void change()
{
    Background = Brushes.Red;
    Dispatcher.BeginInvoke((Action) DoWork);
}

DoWork () запускается несколько секунд, и я не хочу помещать его в поток, так как этот код будет использоваться в нескольких местах и, вероятно, будет взаимодействовать с потоком Dispatcher через различные интервалы. Я пытался вызвать методы Invalidate ... (), но безрезультатно. BeginInvoke () был добавлен, чтобы увидеть, позволит ли задержка применить изменение фона до вызова логики. Как правило, логика будет частью этого метода. Кстати, большая часть логики выполняется в другом потоке и не должна блокировать поток Dispatcher?!

Может кто-нибудь помочь, пожалуйста? Спасибо

Ответы [ 3 ]

3 голосов
/ 13 апреля 2010

Поток "Диспетчер" является потоком пользовательского интерфейса.

Когда вы вызываете Dispatcher.BeginInvoke((Action) DoWork);, вы в основном блокируете поток пользовательского интерфейса до тех пор, пока не выйдет DoWork, так как он работает в потоке пользовательского интерфейса.

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

Вы действительно должны рассмотреть вопрос о перемещении DoWork в фоновый поток, либо напрямую через ThreadPool, BackgroundWorker, либо каким-либо другим способом. Это полностью решило бы эту проблему и предотвратило бы блокировку потока пользовательского интерфейса на «несколько секунд» (что произойдет, если вы запустите это с использованием потоков Dispatcher).

0 голосов
/ 22 декабря 2012

Метод BeginInvoke (..) не блокирует вызов, а просто помещает новое сообщение в очередь для обработки потоком диспетчера. В размещенном вами коде у вас нет гарантии, что изменение цвета фона будет обработано до запуска DoWorks.

Вы можете назначить более низкий приоритет вызову DoWork, чтобы убедиться, что фон изменяется первым:

        private void Button_Click(object sender, RoutedEventArgs e)
    {
        grid.Background = new SolidColorBrush(Colors.Red);
        Dispatcher.BeginInvoke((Action)DoWork, DispatcherPriority.ContextIdle);
    }

    private void DoWork()
    {
        Thread.Sleep(1000);
        btn.Content = "Done";
    }

Но это не лучшее решение, известное человечеству.

Лучше было бы перейти к DoWork в отдельном потоке. Приведенный выше пример с использованием потокового подхода будет выглядеть так:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        grid.Background = new SolidColorBrush(Colors.Red);

        Task t = new Task(DoWork);
        t.Start();
    }

    private void DoWork()
    {
        Thread.Sleep(1000);
        // Synchronize calls to the UI elements
        Application.Current.Dispatcher.BeginInvoke((Action)(() => { btn.Content = "Done"; }));

    }

Только не забудьте опубликовать все операции над элементами пользовательского интерфейса в потоке пользовательского интерфейса.

0 голосов
/ 21 мая 2010

Для связи между потоками у нас есть класс Dispatcher в WPF.

Dispatcher.Invoke(DispatcherPriority.Normal, (NoArgDelegate)delegate
            {
                ------
            });
...