NETMF Dispatcher.Invoke

A technique which is common and well documented in .NET and which allows for messaging between threads, appears to be very sparsely documented in .NETMF.

I have been able to find just a few posts where members request more information regarding the Dispatcher.Invoke method, yet as yet I have not been able to find anything in codeshare or documents or on the forum which supplements the very brief mention of this in the NETMF reference. I certainly may have overlooked information on this site, and if so, I will appreciate it if someone can point me to code examples which demonstrate proper use of the Dispatcher.Invoke method to achieve thread safe inter-thread messaging using this technique.

In order to demonstrate what I am attempting to achieve, I have included 2 copies of the same very small app, which consists of the Program class and a TestClass.

Please note that this code is was written purely in order to present what is being attempted.

In the app, the TestClass exposes an event named TestEvent and a public method named start_process(). The Program class contains an event handler for this event.

In the first app, the start_process() method is called when a button is clicked in the Program. This results in a call to the private process() method, which in turn raises the TestEvent event. When the event handler in the Program class executes, it assigns some text to the TextContext property of the text object. This all happens on a single thread.

In the second app, the start_process() method is called when a button is clicked in the Program. The start_process() method than starts a new thread if it has not already started. The process() method is called from the new thread, and then also raises the TestEvent event.

Because the TestEvent event is raised by the process() method which is executing on a different thread to that within which the event handler in the Program class executes, the event handler needs to pass text to the TextContext property of the text object(executing in the first thread) in a thread-safe manner.

From what I have read, code similar to the following is required in order to achieve this.
if (!text.Dispatcher.CheckAccess())
text.Dispatcher.Invoke(new TimeSpan(0, 0, 1), DispatcherOperationCallback method, new object[] { s });

I have not been able to find examples of or figure out the correct syntax and possible additional constructs required in order to get this to execute correctly.

Any pointers will be appreciated. Many thanks.

// Single Thread
// Program.cs
// ----------
using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using System.Threading;

namespace MFMultiThreading
{
    public class Program : Microsoft.SPOT.Application
    {
        private TestClass testClass;
        private Text text;
        Panel panel;
        private Window mainWindow;

        public static void Main()
        {
            Program myApplication = new Program();

            Window mainWindow = myApplication.CreateWindow();

            GPIOButtonInputProvider inputProvider = new GPIOButtonInputProvider(null);

            myApplication.Run(mainWindow);
        }

        public Window CreateWindow()
        {
            mainWindow = new Window();
            mainWindow.Height = SystemMetrics.ScreenHeight;
            mainWindow.Width = SystemMetrics.ScreenWidth;

            panel = new Panel();

            text = new Text();
            text.Font = Resources.GetFont(Resources.FontResources.small);
            text.TextContent = Resources.GetString(Resources.StringResources.String1);
            text.HorizontalAlignment = Microsoft.SPOT.Presentation.HorizontalAlignment.Center;
            text.VerticalAlignment = Microsoft.SPOT.Presentation.VerticalAlignment.Top;

            panel.Children.Add(text);

            mainWindow.Child = panel;

            mainWindow.AddHandler(Buttons.ButtonUpEvent, new RoutedEventHandler(OnButtonUp), false);

            mainWindow.Visibility = Visibility.Visible;

            Buttons.Focus(mainWindow);

            testClass = new TestClass(0);
            testClass.TestEvent += new TestEventHandler(TestClass_TestEvent);

            return mainWindow;
        }

        private void TestClass_TestEvent(string s)
        {
            text.TextContent = s;
            mainWindow.Invalidate();
        }

        private void OnButtonUp(object sender, RoutedEventArgs e)
        {
            ButtonEventArgs bea = (ButtonEventArgs)e;
            Debug.Print("Button pressed = " + bea.Button.ToString());
            testClass.start_process();
        }
    }
}

// TestClass.cs
// ------------
using System;
using Microsoft.SPOT;
using System.Threading;

namespace MFMultiThreading
{
    public delegate void TestEventHandler(string s);

    public class TestClass
    {
        private int sValue;

        public event TestEventHandler TestEvent;

        public TestClass(int startingValue)
        {
            sValue = startingValue;
        }

        private void RaiseTestEvent(string s)
        {
            TestEvent(s);
        }

        public void start_process()
        {
            //if (thread == null)
            //{
            //    thread = new Thread(new ThreadStart(process));
            //    thread.Start();
            //}
            //else
            process();
        }

        private void process()
        {
            sValue++;
            if (TestEvent != null)
            {
                this.RaiseTestEvent("TestEvent: sValue = " + this.sValue.ToString());
            }
        }
    }
}

// Multiple Threads
// Program.cs
// ----------
using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using System.Threading;

namespace MFMultiThreading
{
    public class Program : Microsoft.SPOT.Application
    {
        private TestClass testClass;
        private Text text;
        Panel panel;
        private Window mainWindow;

        public static void Main()
        {
            Program myApplication = new Program();

            Window mainWindow = myApplication.CreateWindow();

            GPIOButtonInputProvider inputProvider = new GPIOButtonInputProvider(null);

            myApplication.Run(mainWindow);
        }

        public Window CreateWindow()
        {
            mainWindow = new Window();
            mainWindow.Height = SystemMetrics.ScreenHeight;
            mainWindow.Width = SystemMetrics.ScreenWidth;

            panel = new Panel();

            text = new Text();
            text.Font = Resources.GetFont(Resources.FontResources.small);
            text.TextContent = Resources.GetString(Resources.StringResources.String1);
            text.HorizontalAlignment = Microsoft.SPOT.Presentation.HorizontalAlignment.Center;
            text.VerticalAlignment = Microsoft.SPOT.Presentation.VerticalAlignment.Top;

            panel.Children.Add(text);

            mainWindow.Child = panel;

            mainWindow.AddHandler(Buttons.ButtonUpEvent, new RoutedEventHandler(OnButtonUp), false);

            mainWindow.Visibility = Visibility.Visible;

            Buttons.Focus(mainWindow);

            testClass = new TestClass(0);
            testClass.TestEvent += new TestEventHandler(TestClass_TestEvent);

            return mainWindow;
        }

        private void TestClass_TestEvent(string s)
        {
            if (!text.Dispatcher.CheckAccess())
                text.Dispatcher.Invoke(new TimeSpan(0, 0, 1), DispatcherOperationCallback method, new object[] { s });
        }

        private void OnButtonUp(object sender, RoutedEventArgs e)
        {
            ButtonEventArgs bea = (ButtonEventArgs)e;
            Debug.Print("Button pressed = " + bea.Button.ToString());
            testClass.start_process();
        }
    }
}

// TestClass.cs
// ------------
using System;
using Microsoft.SPOT;
using System.Threading;

namespace MFMultiThreading
{
    public delegate void TestEventHandler(string s);

    public class TestClass
    {
        private int sValue;

        private Thread thread;

        public event TestEventHandler TestEvent;

        public TestClass(int startingValue)
        {
            sValue = startingValue;
        }

        private void RaiseTestEvent(string s)
        {
            TestEvent(s);
        }

        public void start_process()
        {
            if (thread == null)
            {
                thread = new Thread(new ThreadStart(process));
                thread.Start();
            }
            else
            process();
        }

        private void process()
        {
            sValue++;
            if (TestEvent != null)
            {
                this.RaiseTestEvent("TestEvent: sValue = " + this.sValue.ToString());
            }
        }

    }
}

@ andre.m - Thanks for your reponse.

I have not been able to find reference to it anywhere beyond your original post, and 2 others on the forum.

Is it sensible to use SyncOp in production code if no documentation can be found on it?

Are you able to point me to some place where SyncOp is fully documented please?

Thanks.

@ andre.m - Thank you for your reply.

Is anyone else able to assist with either full documentation on the SyncOp functionality, or an answer to the Dispatcher.Invoke question please?

Thank you.

Hi,
did you read Colin Miller’s blog?
http://blogs.msdn.com/b/netmfteam/archive/2008/03/04/using-the-dispatcher.aspx

@ RoSchmi - I have read this, and just about everything I could find on the subject re NETMF. I believe I will get this working. Thanks for the link.