joedotnot
joedotnot

Reputation: 5133

Update the WPF UI while processing - best use of async await

Here is what i have tried - It works, as far as seeing the UI refreshed, but i don't think it's the best use of async / await. How can i improve on this?

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            DoSomeProcessingOnTheUIThread(reader);

            //only update UI every 100 loops
            if (rowCount > 0 && rowCount % 100 == 0)
            {
                //yay! release the UI thread
                await Task.Run(() =>
                {
                    Application.Current.Dispatcher.Invoke(
                        () =>
                        {
                            txtRowCount.Text = string.Format("{0}", rowCount);
                        });
                });
            }

        } //end-while
    }//end-using
}

What's a better approach?

UPDATE: I have avoided the Dispatcher.Invoke based on the answer of Clemens, by sending the processing to a background task, and updating progress on the UI directly. My code now looks like.

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            await DoSomeProcessing(reader);

            //only update UI every 100 loops
            if (rowCount % 100 == 0)
            {
                txtRowCount.Text = string.Format("{0}", rowCount);
            }

        } //end-while
    }//end-using
    MessageBox.Show("I am done!");
}

private Task DoSomeProcessing(XmlReader reader)
{
   Task t =Task.Run(() =>
   {
       //Do my processing here.
   });
   return t;
}

UPDATE#2: On reflection, why am i creating a new Task on every loop ? It would probably be better to just run the whole loop within one background task. And periodically raise a callback to show progress; See my other answer below.

Upvotes: 4

Views: 8246

Answers (2)

joedotnot
joedotnot

Reputation: 5133

My Final answer.

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{

    await Task.Run(() => DoSomeWorkOnBgThread( (cnt) => //HAHA! this syntax is so confusing
        {
            Application.Current.Dispatcher.Invoke(() =>
                {
                    txtRowCount.Text = cnt.ToString();
                }
            ); //end-invoke
        }
    ));

    MessageBox.Show("i am done!");
}


private void DoSomeWorkOnBgThread(Action<int> callbackFn)
{

    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            DoMyProcessingHere();

            //only update UI every 100 loops
            if (rowCount % 100 == 0)
            {
                callbackFn(rowCount); ///
            }

        } //end-while
    }//end-using
}

Upvotes: 0

Clemens
Clemens

Reputation: 128062

A Task that immediately calls Dispatcher.Invoke is pointless, because there is nothing that actually runs on a background thread, except the tiny piece of code that schedules the Dispatcher action.

Better set the Text property directly, and use XmlReader.ReadAsync:

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    using (var reader = XmlReader.Create("someXmlFile.xml"))
    {
        int rowCount = 0;

        while (await reader.ReadAsync())
        {
            rowCount++;

            await Task.Run(() =>
            {
                DoSomeWork(reader);
            });

            if (rowCount > 0 && rowCount % 100 == 0)
            {
                txtRowCount.Text = string.Format("{0}", rowCount);
            }
        }
    }
}

You may also consider making your DoSomeWork async and directly call it like this:

await DoSomeWork(reader);

Upvotes: 5

Related Questions