Synchronize a long-running task to the UI-Thread in WPF/.NET

This post explains how to process data from a task in WPF in a way I’d consider “best-practice”. The main code could or should be in the view-model.

Code-behind (keep it short or better yet use a delegate command)

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Get some parameters if necessary
                [...]

                this.SomeViewModelData.StartSearch([parameters...]);
            }
            catch (Exception ex)
            {
                log.Warn("Error handling...", ex);
            }
        }

And the code in the view-model:

  • StartSearch() is a method in the view-model class and sets the properties of the view-model that are bound to the UI
  • this.IsWaiting is some bool property to indicate the waiting status in the UI
  • this.ResultData is not bound but contains data to be processed
  • ProcessResultData() processes the result and sets the output (for example it might fill an ObservableCollection)
        public void StartSearch([parameters...])
        {
            try
            {
                var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

                this.IsWaiting = true;

                Task.Factory.StartNew(() =>
                {
                    this.ResultData = DatabaseService.GetComplexData([parameters...]);
                }).ContinueWith((t) =>
                {
                    // Invoke in UI Thread via scheduler
                    this.ProcessResultData();

                    // Cancel waiting indicator
                    this.IsWaiting = false;
                }, uiScheduler // <-- This is the important part: Continue
                               // the task in the UI thread
                {
                    if (t.Exception != null)
                    {
                        log.Warn("Task exception handling...", t.Exception);
                    }

                    this.IsWaiting = false;
                }, TaskContinuationOptions.OnlyOnFaulted);
            }
            catch (Exception ex)
            {
                log.Warn("Do exception handling... ", ex);
                this.IsWaiting = false;
            }
        }

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.