Dec 12, 2010

How Many Apartments for a UI Thread

With WPF, drawing and binding can be time consuming for large UI projects. If updating the UI takes a while, you need a second UI thread to display progress, but if you try to start a progress window from a worker thread, the framework will complain that the thread has too many apartments (I'm paraphrasing of course)!

The trick is to initialize the thread STA, start the thread message pump and use a dispatcher to send code to the thread for execution.

Use the following code to roll a progress indicator. Not shown is the ProgressDialog class which for me is a XAML window that exposes a progress bar (named bar) and a label (named label)
class ProgressIndicator
    {
        Thread t;
        ProgressDialog progdialog;
        bool running = false;
        public void Start()
        {        
            t = new Thread(ThreadFunc);
            t.SetApartmentState(ApartmentState.STA); // set the thread to STA
            t.Start();
            while (!running) ; // block until the thread is ready to accept updates
        }

        public void UpdateProgress(double Progress, String label)
        {
            progdialog.Dispatcher.BeginInvoke(
                (System.Windows.Forms.MethodInvoker)delegate() { progdialog.bar.Value = Progress; progdialog.label.Content = label; }
                                        );
        }

        public void Stop()
        {
            try
            {
                progdialog.Dispatcher.BeginInvoke(
                    (System.Windows.Forms.MethodInvoker)delegate() { progdialog.Close(); }
                                            );
                t.Abort();
                t.Join();
            }
            catch (Exception)
            {
            }
            finally
            {
                t = null;
            }
        }

        protected void ThreadFunc()
        {
            progdialog = new ProgressDialog();
            progdialog.Show();
            progdialog.Closed += (sender2, e2) =>
                progdialog.Dispatcher.InvokeShutdown();  // if main window closes...

            progdialog.bar.Maximum = 1;   // I always like dealing with 0 -> 1 progress
            running = true;
            System.Windows.Threading.Dispatcher.Run();  // start the message pump
            
        }
    }
Copy and paste the code and implement the ProgressDialog class with a progress bar named bar and a label named label and this should let you start and update a progress bar in a second UI thread.