it-swarm.com.ru

WPF Dispatcher.BeginInvoke и пользовательский интерфейс/фоновые потоки

Я думаю, что мне нужны некоторые разъяснения относительно использования 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 к своему 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 причудливый, поэтому мне любопытно узнать о некоторых альтернативах.

18
Matthew Ruston

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

25
Charlie

Мне тоже не нравится 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
italianogrosso

Ответ Чарли - это то, что ты ищешь, правда.

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

4
Anderson Imes

Как видно из его названия, он будет выполняться в фоновом режиме, поэтому вам не нужно создавать его экземпляр с помощью 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 () и обрабатывать пользовательский интерфейс оттуда. В противном случае вы столкнетесь с некоторыми несоответствиями при запуске событий завершения.

2
Julio Bailon

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

0
MattE