Mobile Development: Easy to use background thread with GUI update

Print This Post Print This Post

Although there are well know ways to update the GUI from a background thread not running in the GUI thread, I looked for an easiest to use solution. After some experiments I got a background thread class that is very easy to use. No BeginInvoke or Control.Invoke needed any more to update a GUI element.

The final solution uses a MessageWindow inside a control based class with a worker thread. As the control and the MessageWindow is part of the GUI thread, there is no need to use Invokes.

Inside the thread I use SendMessage to transfer background thread informations to the control, which then fires an event you can subscribe in the GUI thread.

The test app attached shows three threads running independently and all update the GUI frequently without blocking the GUI.


Here is my try to visualize my idea and solution

Normally you have to use such a construction:

// this variable will hold some text set by the worker thread
public string Message = "";

// Create a worker thread and then add items to the ListBox from the
// UI thread
public void DoThreading()
{
    // Create the worker thread and start it
    ThreadStart starter = new ThreadStart(this.UpdateListBox);
    Thread t = new Thread(starter);
    t.Start();

    // Loop 4 times, adding a message to the ListBox each time
    for(int i = 0; i < 4; i++);
    {
        this.listBox1.Items.Add("Message from UI thread");
        this.listBox1.Update();
        // Process any queued events on the UI thread
        Application.DoEvents();
        // Suspend processing for 1 second
        Thread.Sleep(1000);
    }
    this.listBox1.Items.Add("Last message from UI thread");
    this.listBox1.Update();
}

public void UpdateListBox()
{
    for(int j = 0; j < 5; j++)
    {
        // Set the message to be added to the ListBox from the worker
        // thread
        this.Message = "Worker thread loop count = " + j.ToString();
        // Invoke the WorkerUpdate method in the ListBox’s thread
        // context
        this.listBox1.Invoke(new EventHandler(WorkerUpdate));
        Thread.Sleep(700);
    }
}
// The delegate that’s called from the worker thread
// to update the ListBox
public void WorkerUpdate(object sender, EventArgs e)
{
    this.listBox1.Items.Add(this.Message);
    this.listBox1.Update();
}

As you can see from the code you need to inform the worker thread about the delegate to use for GUI updates. An GUI update from the worker thread is done with the help of UpdateListBox() and that invokes WorkerUpdate and that function finally updates the GUI. So four places are tight together with function names and definitions.

There are also other  ways to do it. For example with declaring a delegate first and then Invoke from background thread (see here):

delegate void setText(string sText);
...
private void ThreadProcSafe()
		{
			this.SetText("This text was set safely.");
		}
...
		private void SetText(string text)
		{
			// InvokeRequired required compares the thread ID of the
			// calling thread to the thread ID of the creating thread.
			// If these threads are different, it returns true.
			if (this.textBox1.InvokeRequired)
			{
				SetTextCallback d = new SetTextCallback(SetText);
				this.Invoke(d, new object[] { text });
			}
			else
			{
				this.textBox1.Text = text;
			}
		}

But this solution also needs your background thread to know the delegate name (SetText(string s)).

I also used this patterns  often, but finally I thought, I need an easy to use class that is independent from the main gui thread code and easy to re-use.

With my bgThread classes (yes there are three predefined ones), you can simply use this construction:

    public partial class ThreadTest2 : Form
    {
        private bgThread _bgThread;
...
        //start and link the thread
        private void btn_bgThreadTest_Click(object sender, EventArgs e)
        {
            _bgThread = new bgThread("192.168.128.5");
            _bgThread.bgThreadEvent += new bgThread.bgThreadEventHandler(_bgThread_bgThreadEvent);
        }

        void _bgThread_bgThreadEvent(object sender, bgThread.BgThreadEventArgs bte)
        {
            listBox1.Items.Insert(0, "bgThread: " + bte.iStatus.ToString();
        }
...
        //in your exit function you MUST dispose existing threads
            if (_bgThread != null)
                _bgThread.Dispose();
            Application.Exit();
...

As you can see, you have only a bgThread object and after creating a new instance just add the eventhandler. From the eventhandler you can directly update the GUI. You don’t have to define  a delegate.

All three bgThread classes work fine. They differ in the amount and type of data you have to transfer from the background thread to the GUI and so the BgThreadEventArgs differ in there definition.

The first class, bgThread1 class, only uses the IntPtr’s usable with SendMessage to transfer data. So you can transfer a maximum of two integers or so. That is enough for the most usage scenarios, where you only need to know status codes from the background thread. The attached bgThread1 class only impements one DWORD (or int).

The second class, bgThread class, uses a global lock and can transfer the data you desire. You have to redefine only the bgThreadEventArgs class to include the data types you would like to transfer. This class uses PostMessage and NOT SendMessage. It may happen, that the data and the message will get out of sync as PostMessage works asynchron.

The third class, bgThread2 class, uses WM_COPYDATA to transfer strutured data from the background thread. It is more complicated to use and extend than bgThread1 but uses SendMessage and no lock objects. Your data is delivered with SendMessage and so there will be no problem with data and message syncronization.

All classes define a class based on Component.

    public class bgThread : Component
    {
        public delegate void bgThreadEventHandler(object sender, BgThreadEventArgs bte);
        public event bgThreadEventHandler bgThreadEvent;
...
        internal BgThreadEventArgs _BGeventArgs;
        private Thread myThread;
...
        internal class bgThreadWndProc : MessageWindow
        {
            public bgThreadWndProc(bgThread Parent)
            {
                this._bgThread = Parent;
                hwndControl = Hwnd;
            }
            protected override void WndProc(ref Message m)
            {
                int iMsg = m.Msg;
                System.Diagnostics.Debug.WriteLine("WndProc called...");
                switch (iMsg)
                {
                    case msgID:
                        {
                            this._bgThread.NotifyData(m.WParam);
                            break;
                        }
                    default:
                        {
                            base.WndProc(ref m);
                            break;
                        }
                }
            }
        }//MsgWnd

Within the bgThread class there is a nested class based on MessageWindow.

The bgThread class constructor:

        public bgThread()
        {
            bgWnd = new bgThreadWndProc(this);
            myThread = new Thread(myThreadStart);
            bRunThread = true;
            myThread.Start();
        }

The thread proc itself. It may contain more or less blocking functions  calls.

       private void myThreadStart()
        {
            try
            {
                do
                {
                    //The blocking function...
                    //sample: call ping and return number of pings answered
                    int iReply = myPing.Ping(System.Net.IPAddress.Parse(_sIP));
                    //create a msg and send it to the messagewindow, the messagewindow will then inform the event subscribers
                    //we only need the number of ping replies (0 or 1) for the ping
                    Microsoft.WindowsCE.Forms.Message msg = Message.Create(bgWnd.Hwnd, msgID, new IntPtr(iReply), IntPtr.Zero);
                    MessageWindow.SendMessage(ref msg); //async Message send
                    Thread.Sleep(1000); //if you have fast 'blocking' functions you should sleep
                } while (bRunThread);
            }
            catch (ThreadAbortException)
            {
                System.Diagnostics.Debug.WriteLine("Thread will abort");
                bRunThread = false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Exception in ThreadStart: " + ex.Message);
            }
            System.Diagnostics.Debug.WriteLine("ThreadProc ended");
        }

The code that fires the event is not called from the thread. Instead it is called from the MessageWindow. The thread itself sends its ‘data’ via SendMessage to the bgThread ‘Component’ (which is part of the GUI!):

        private void NotifyData(IntPtr i1)
        {
            BgThreadEventArgs _bgThreadEventArgs;
            //is there any subscriber
            if (this.bgThreadEvent == null)
            {
                return;
            }
            try
            {
                int i = i1.ToInt32();
                _bgThreadEventArgs = new BgThreadEventArgs(i);
                this.bgThreadEvent(this, _bgThreadEventArgs);
            }
            catch (MissingMethodException)
            {
            }
        }

The above code snippets are taken from bgThread1 class, the one that only ‘transfers’ an integer to the GUI.

Possible extensions

  • extend bgThread to Stop (pause) a bgThread
  • extend bgThread and let it invoke a eventhandler that does the blocking stuff. Similar to the BackgroundWorker class available in internet. So you can define your background function inside the main code.
DOWNLOAD:bgThread classes, a ping class and a demo application (VS2005, target WM6SDK) - (Hits: 896, size: 20.06 KB)
delegate void SetTextCallback(string text);

One Comment

  1. [...] part is outsourced into a thread, which is based on my background thread class bgThread2 (see http://www.hjgode.de/wp/2010/06/01/mobile-development-easy-to-use-background-thread-with-gui-update/). It was very easy to implement the stream reading within the thread, so the GUI is not blocked [...]

Leave a Reply