Tuesday, October 11, 2011

Multiple UI threads in WinForms

Most WinForms application has one main UI thread and some background worker threads. And it is very typical and clean way of doing multi-threading in WinForms. But, in some special situaions, you might want to create multiple UI threads so that each thread has each form independently. The following sample shows how to create multiple UI threads in WinForms.

First in Main(), it launches a form called 'MainForm' with Application.Run() method. Application.Run() creates message pump for a UI form, in this case, in main UI thread.

static class Program
{
   /// 
   /// The main entry point for the application.
   /// 
   [STAThread]
   static void Main()
   {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new MainForm());
   }
}

In Form_Load() of MainForm, we create 3 new Forms from 3 different threads.

public partial class MainForm : Form
{
   public MainForm()
   {
      InitializeComponent();
   }

   private void MainForm_Load(object sender, EventArgs e)
   {
      // Start new UI thread for Form1
      Thread thread1 = new Thread(ShowForm1);
      thread1.Start();

      // Start new UI thread for Form2
      Thread thread2 = new Thread(() => { Form2 f2 = new Form2(); Application.Run(f2); });
      thread2.Start();

      // Start new UI thread for Form3
      Thread thread3 = new Thread(ShowForm3);
      thread3.Start();
   }

   void ShowForm1()
   {
      // message pump with Application.Run()
      Application.Run(new Form1());
   }

   void ShowForm3()
   {
      Form3 f3 = new Form3();
      // Start message pump by using ShowDialog()
      f3.ShowDialog(); 
   }
}

First thread, thread1, runs ShowForm1() function which is simply calls Application.Run() against new Form1 instance. This thread creates new message pump for Form1 and works separately from MainForm.

Second thread, thread2, shows an example of using anonymous delegate to launch Form2. Basically it's the same as ShowForm1().

Third thread, thread3, calls ShowForm3() function where Form3 is launched by using ShowDialog(). If one uses Show() method instead of ShowDialog() here, thread will show the Form3 but then the form will be closed immediately since the thread will exit. ShowDialog begins new message pump for Form3 and will stay until user clicks Close button.


Each UI thread will work separately. Once all forms are closed, the application will be closed.

3 comments:

  1. OMG, I can't believe that starting a new UI Thread was that simple... after everything I have read among Google's results and StackOverflow's Q&As, I thought I would abandon...
    When finally, all I needed to call was Application.Run(myNewFormUIThreaded); !
    Thanks for sharing your nice&useful notes Alex!

    ReplyDelete
  2. UI threads should run as AppartmentState.STA to avoid random crashes when using some UI controls or windows COM interops, namely the Clipboard.

    This can be set before the thread is started:
    thread.SetApartmentState(AppartmentState.STA);

    ReplyDelete