How to update a WPF Control from the TPL Task?

2019-07-09 23:12发布

问题:

How to update a WPF Control from the TPL Task?

Fine so I tried some scenarios to use Dispatcher but anyway it gives the error. I need help guys!

private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        //Application.Current.Dispatcher.Invoke((Action)MyInit); 
        backgroundDBTask = Task.Factory.StartNew(() =>
            {
                DoSomething1();
            }, TaskCreationOptions.LongRunning);
        backgroundDBTask.ContinueWith((t) =>
        {
            // ... UI update work here ...
        },             
        TaskScheduler.FromCurrentSynchronizationContext());             
    }

    void DoSomething1()
    {
        // MyInit();
        int number = 0;
        while (true)
        {
            if (state)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Begin second task... {0}", number++);
               // mostCommonWords = GetMostCommonWords(words) + string.Format("   Begin second task... {0}", number++);

                textBox2.Text = (number++).ToString(); // Gives the error

                Dispatcher.BeginInvoke(); // How it should be ?
            }
        }
    }

Thank you!

回答1:

You need to pass a delegate that does your work to BeginInvoke.
BeginInvoke will asynchronously run this delegate on the UI thread.

For example:

Dispatcher.BeginInvoke(new Action(delegate {
    textBox2.Text = number.ToString(); 
}));


回答2:

I know there is already an answer here and what Slaks gave you will fix it since it will use the Dispatcher thread thus will not throw an exception for accessing a control from a different thread.

But, I notice this.

 backgroundDBTask = Task.Factory.StartNew(() =>
        {
            DoSomething1();
        }, TaskCreationOptions.LongRunning);

With this

 backgroundDBTask.ContinueWith((t) =>
    {
        // ... UI update work here ...
    },             
    TaskScheduler.FromCurrentSynchronizationContext());   

You already have a comment where to update the UI and you also gave it the SynchronizationContext. Why are you still trying to update the UI inside of your DoSomething1?

Unless your purpose of your Task is to update the UI then there is no need to use the ContinueWith. Instead just pass the SynchronizationContext and it should work without having to explicitly call the Dispatcher.BeginInvoke.

backgroundDBTask = Task.Factory.StartNew(() =>
        {
            DoSomething1();
        },
    CancellationToken.None,
    TaskCreationOptions.None,
    TaskScheduler.FromCurrentSynchronizationContext());