Зачем BackgroundWorker в WPF нужен Thread.Sleep для обновления элементов управления пользовательского интерфейса? - PullRequest
3 голосов
/ 11 июня 2010
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    BackgroundWorker bgWorker;
    Action<int> myProgressReporter;

    public Window1()
    {
        InitializeComponent();
        bgWorker = new BackgroundWorker();
        bgWorker.DoWork += bgWorker_Task;
        bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;

        // hook event to method
        bgWorker.ProgressChanged += bgWorker_ReportProgress;

        // hook the delegate to the method
        myProgressReporter = updateProgress;

        bgWorker.WorkerReportsProgress = true;

    }

    private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        object result;
        result = e.Result;
        MessageBox.Show(result.ToString());
        progressBar1.Value = 0;
        button1.IsEnabled = true;
    }

    private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
    {
        System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
        disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
        //Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
    }

    private void updateProgress(int progressPercentage)
    {
        progressBar1.Value = progressPercentage;
    }

    private void bgWorker_Task(Object sender, DoWorkEventArgs e)
    {
        int total = 1000;
        for (int i = 1; i <= total; i++)
        {
            if (bgWorker.WorkerReportsProgress)
            {
                int p = (int)(((float)i / (float)total) * 100);
                bgWorker.ReportProgress(p);
            }
            Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception, 
        }

        e.Result = "Completed";
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {            
        if(!bgWorker.IsBusy)
            bgWorker.RunWorkerAsync("This is a background process");
        button1.IsEnabled = false;
    }
}
}

Ответы [ 2 ]

5 голосов
/ 11 июня 2010

Поскольку в вашем (искусственном) сценарии вы отправляете 1000 запросов на обновление в основной поток.
У него нет времени на выполнение цикла ожидания (необходимо выполнить обновление экрана).

Но (благодаря TerrorAustralis) вы должны начать с объединения ваших методов bgWorker_ReportProgress и myProgressReporter.Теперь вы синхронизируете дважды, что является возможной причиной переполнения стека.Отправка событий UpdateProgress является одной из основных функций Backgroundworker:

private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
  //System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
  //disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
  progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
3 голосов
/ 11 июня 2010

Возможности:
Dispatcher.BeginInvoke () - это асинхронная операция.Так как это так, вы можете попытаться ударить его еще раз, прежде чем он завершит свою работу.Чтобы проверить, является ли это проблемой, попробуйте Dispatcher.Invoke (), который является синхронным

. В качестве возможного обходного пути, если вы просто хотите обновить индикатор выполнения, событие backgroundWorker ProgressChanged может сделать это без использованияявный диспетчер.

...