WPF Dispatcher.BeginInvoke и пользовательский интерфейс / фоновые потоки - PullRequest
18 голосов
/ 30 июля 2009

Я думаю, что мне нужны некоторые разъяснения относительно использования WPF Dispatcher.Invoke и Dispatcher.BeginInvoke .

Предположим, у меня есть какой-то давно работающий код 'работы', подобный такому, который вызывается нажатием кнопки в простом приложении WPF:

longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
    {
    Console.WriteLine("Starting Work Action");
    int i = int.MaxValue;
    while (i > 0)
        i--;
    Console.WriteLine("Ending Work Action");
    longWorkTextBox.Text = "Work Complete";
    };
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

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

Если предположить, каков наилучший способ настройки моего диспетчера для выполнения workAction в отдельном потоке от моего пользовательского интерфейса? Я знаю, что могу добавить BackgroundWorker my workAction , чтобы запретить блокировку пользовательского интерфейса следующим образом:

longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += delegate
    {
        Console.WriteLine("Starting Slow Work");
        int i = int.MaxValue;
        while (i > 0)
        i--;
        Console.WriteLine("Ending Work Action");
    };
    worker.RunWorkerCompleted += delegate
    {
        longWorkTextBox.Text = "Work Complete";
    };
    worker.RunWorkerAsync();
 };
 longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

Есть ли более элегантные способы сделать это, кроме использования BackgroundWorker ? Я всегда слышал, что BackgroundWorker изворотлив, поэтому мне любопытно узнать о некоторых альтернативах.

Ответы [ 5 ]

25 голосов
/ 30 июля 2009

Честно говоря, BackgroundWorker - самое элегантное решение для этого. Я не могу придумать более простой способ сделать это.

4 голосов
/ 30 мая 2012

Мне тоже не нравится BackgroundWorker. Простая альтернатива может быть что-то вроде:

using System;
using System.Threading;
using System.Windows;

namespace Sample
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
             base.OnSourceInitialized(e);
             longWorkTextBox.Text = "Ready For Work!";
       }

        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            new Thread(Work).Start();
        }

        void Work()
        {
            longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; }));
            Console.WriteLine("Starting Work Action");
            int i = int.MaxValue;
            while (i > 0)
                i--;
            Console.WriteLine("Ending Work Action");
            longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; }));
        }
    }
}

Легко, не так ли?

4 голосов
/ 31 июля 2009

Ответ Чарли на самом деле то, что вы ищете.

Однако, если это возможно, вы можете посмотреть, можете ли вы разделить свою работу так, чтобы отдельные единицы работы были небольшими и не влияли на пользовательский интерфейс так сильно. Это позволит вам просто использовать Диспетчер напрямую. Хороший пример этого можно найти на странице потоков WPF: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

2 голосов
/ 23 августа 2011

Как видно из названия, оно будет выполняться в фоновом режиме, поэтому вам не нужно создавать его экземпляр с помощью Dispatcher. Кроме того, если вы хотите, чтобы этот код запускался в WP7, BeginInvoke не получает параметр фона.

Моя рекомендация - создать BackgroundWorker как:

BackgroundWorker worker = new BackgroundWorker;

А затем создайте обработчики для событий:

worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork +=new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged);

И, наконец, вы звоните:

bkwkPlayingLoop.RunWorkerAsync();

Большой соблазн использовать Dispatcher внутри DoWork, но вместо этого вызывать worker.ReportProgress () и обрабатывать пользовательский интерфейс оттуда. В противном случае вы столкнетесь с некоторыми несоответствиями при запуске событий завершения.

0 голосов
/ 10 августа 2016

Задачи проще в использовании, чем фоновые рабочие, они выполняют больше задач, имеют меньше проблем и были в значительной степени созданы, поэтому фоновые рабочие больше не нужно больше использовать ...

...