How to update a WPF Control from the TPL Task?

2019-07-09 23:37发布

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!

2条回答
The star\"
2楼-- · 2019-07-09 23:51

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()); 
查看更多
女痞
3楼-- · 2019-07-09 23:59

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(); 
}));
查看更多
登录 后发表回答