Gary Kindel
Gary Kindel

Reputation: 17699

How to efficently update WPF MainWindow from an async task

Is this an acceptable approach to update WPF MainWindow from an async task, using events thrown from a public static class?

In MainWindow.cs, I subscribe to UI.TaskCompleted event In UI.cs, I subscribe to AsyncTaskActions.TaskCompleted event.

Using this pattern, AsyncTaskActions.TaskCompleted is raised with a async task is completed. UI catch the event and raise UI.TaskCompleted. This way, the event is catch in MainWindow code where I can use Displacher.Invoke to refresh the displayed page. The end result is I get a page refresh when a task is completed and the task still runs asynchronously.

Application design summary:

MainWindow: main window which can have several different page classes in content area within the main window.

Common.cs public static class contains a number of common methods through the UI in the application.

AsyncTaskActions.cs - class with a number of async common methods (ie download file)

Code snippets:

    public partial class MainWindow 
        public MainWindow()
        {    
           UI.TaskCompleted += UI_TaskCompleted; 
        }

        void UI_TaskCompleted(EventArgs e)
        {
           Dispatcher.Invoke(new Action(this.PageRefresh));
        }

       public void PageRefresh()        
       {
           var page = ((ContentArea)).Content;
           if (page == null) return;

            switch (page.GetType().Name)
            {
                case "SearchPage":
                    ((SearchPage) page).SearchParts();
                    break;
                case "LegoPartPage":
                    ((LegoPartPage) page).LoadData();
                    break;
                case "LegoSetPage":
                    ((LegoSetPage) page).LoadData();
                    break;
                case "MainPage":
                    ((MainPage) page).LoadData();
                    break;
                case "MiniFigPage":
                    ((MiniFigPage) page).LoadData();
                    break;
            }
        }
   }

    public static class UI
    {
       public delegate void TaskComplete(EventArgs e);
       public static event TaskComplete TaskCompleted;

        public static async Task<int> DownloadPriceSummaryAsync(String itemNo, Int64 colorId)
        {
            var wAsyncTaskClasses = new AsyncTaskClasses();
            wAsyncTaskClasses.TaskCompleted += wAsyncTaskClasses_TaskCompleted;
            Task<HtmlDocument> task = wAsyncTaskClasses.DownloadPriceGuide(string.Format(Common.BrickLinkPrice, itemNo, colorId), itemNo, colorId);
            return await  wAsyncTaskClasses.ParsePriceSummaryAsync(task, itemNo, colorId);
        }
 }


public class AsyncTaskActions
{
    public delegate void TaskComplete(object sender, EventArgs e);
    public event TaskComplete TaskCompleted;

    public async Task<int> ParsePriceSummaryAsync(Task<HtmlDocument> task, string itemNo, Int64 colorId)
    {
        return await Task.Run(() => ParsePriceSummary(task, itemNo, colorId));
    }

    public int ParsePriceSummary(Task<HtmlDocument> task, string itemNo, Int64 colorId)
    {
         ... some code....

        if (null != TaskCompleted)
        {
            TaskCompleted(this, new EventArgs());
        }

        return recordCount;
    }

Upvotes: 0

Views: 1672

Answers (1)

Gusdor
Gusdor

Reputation: 14334

You are marshaling all events to the UI context with Dispatcher.BeginInvoke and that appears safe.

As usual, if it works, it works!


As an aside - there is not much about this design that I would personally consider acceptable. Is it reasonable to assume you are from an procedural or functional background? It might help to throw this at Code Review for more information and some OO tips.

Upvotes: 1

Related Questions