Simple Task.ContinueWith(), updating WinForms control on a different thread

using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TaskTest
{
    public partial class Form1 : Form
    {
        static string finished = "new";
        static string message = "new";
        static int result = 999;

        public Form1()
        {
            InitializeComponent();
            textBox3.Visible = false;

            var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            var backgroundScheduler = TaskScheduler.Default;

            //Run callMethod first.  When it completes, update the UI on the main thread.
            Task.Run(() => callMethod()).ContinueWith(delegate { UpdateUI(); }, uiScheduler);

            //Task.Run(() => callMethod()).ContinueWith(delegate { UpdateUI(); }, uiScheduler).ContinueWith(delegate { UpdateUI(); }, backgroundScheduler);
        }

        
        public static async Task callMethod()
        {
            result = await Method1(); 
            message = "done callMethod"; //This is populated AFTER Method1 completes
        }

        public static async Task Method1()
        {
            await Task.Run(() =>
            {
                Thread.Sleep(10000);
                finished = "finished long process"; //This is populated after Thread.Sleep
            });
            return 123; //The function returns AFTER the task completes.
        }

        private void UpdateUI()
        {
            textBox1.SafeInvoke(d => d.Text = finished);
            textBox2.SafeInvoke(d => d.Text = message);
            textBox3.SafeInvoke(d => d.Text = result.ToString());
            textBox3.SafeInvoke(d => d.Visible = true);

            Application.DoEvents();
            Thread.Sleep(250);
        }
    }

    //Updating Your Form from Another Thread without Creating Delegates for Every Type of Update
    //https://www.codeproject.com/articles/52752/updating-your-form-from-another-thread-without-cre?fid=1557676&df=90&mpp=25&sort=Position&view=Normal&spc=Relaxed&prof=True&fr=26
    public static class ExtensionMethod
    {

        public static TResult SafeInvoke(this T control, Func delegateCall) where T : ISynchronizeInvoke
        {
            if (control.InvokeRequired)
            {
                //delegateCall: A delegate to a method that takes parameters of the same number and type that are contained in the args parameter.
                //args: An array of objects to pass as arguments to the given method. This can be null if no arguments are needed.
                IAsyncResult result = control.BeginInvoke(delegateCall, new object[] { control });
                object endResult = control.EndInvoke(result); return (TResult)endResult;
            }
            else
                return delegateCall(control);
        }

        public static void SafeInvoke(this T control, Action call) where T : ISynchronizeInvoke
        {
            if (control.InvokeRequired) control.BeginInvoke(call, new object[] { control });
            else
                call(control);
        }
    }
}

Leave a Reply