Pav
Pav

Reputation: 25

C# - How to send update value back to form class from a class where the work is done in a different thread

I'm working on a Splash/Loading screen for an app and I'd like to have a progress bar as well as a label on it which will notify the user of current process, e.g. connecting to the database, loading user settings, retrieving x data, retrieving y data.

I only did a tiny bit of BackGroundWorker stuff in VB before and nothing in C# so a bit confused where to start as it looks quite different in C#.

I'd like to keep the form class simple as to only calling specific methods from different classes on a different thread.

I mainly need to know how to update the GUI from the class that does the work as I think I could work out the threading itself from the below code:

    using System;
    using System.Threading;
    
    public class ThreadWork
    {
       public static void DoWork()
       {
          for(int i = 0; i<3;i++) {
             Console.WriteLine("Working thread...");
             Thread.Sleep(100);
          }
       }
    }

class ThreadTest
{
   public static void Main()
   {
      Thread thread1 = new Thread(ThreadWork.DoWork);
      thread1.Start();
      for (int i = 0; i<3; i++) {
         Console.WriteLine("In main.");
         Thread.Sleep(100);
      }
   }
}

Upvotes: 0

Views: 75

Answers (2)

Theodor Zoulias
Theodor Zoulias

Reputation: 43553

You could use the Task.Run method to offload work to a ThreadPool thread, and a Progress<string> object to report progress from the background thread to the UI. You can also use async/await in order to write your code in a straightforward way. Here is an example:

private async void Form_Load(object sender, EventArgs e)
{
    IProgress<string> progress = new Progress<string>(message =>
    {
        Label1.Text = message;
    });

    var result = await Task.Run(() =>
    {
        progress.Report("Connecting to database...");
        ConnectToDatabase();
        progress.Report("Loading user settings...");
        LoadUserSettings();
        progress.Report("Retrieving x data...");
        return RetrieveXData();
    });

    // At this point the background processing has completed
    Label1.Text = $"Done! ({result})";
}

If you have heterogeneous data to report at different intervals, you can use multiple Progress<T> objects (for example a Progress<string>, a Progress<int> etc). You can also use a complex type for reporting, for example Progress<(string, int)>.

Upvotes: 2

Usama Aziz
Usama Aziz

Reputation: 279

Although Mitch's answer will work. You can also use TaskSchedulers to handle background work and then respond back to UI:

public static void Main()
        {
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            Task.Factory.StartNew(() => DoBackgroundWork(uiScheduler));
        }

        private static void DoBackgroundWork(TaskScheduler uiScheduler)
        {
            // Do background stuff here...

            Task.Factory.StartNew(() =>
            {
                // Respond back to UI here
                Console.WriteLine("Doing work");
                // Or if you have to change GUI, let's say a label value;
                label1.Text = "Text changed by background Task";

            }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
            
            // Some other background work
        }

Upvotes: 1

Related Questions