Silverlight for Windows Phone 7: How to check if the current thread is UI one
If in your wp7 application you are planning to use background threads for some long operations, you likely need from time to time to update controls to reflect state and/or result of operations. Any update has to be done in the UI thread, in which all controls are created. Being in another thread we can use object Dispatcher to invoke a method inside the UI thread:
Dispatcher.BeginInvoke(delegate() { /* update controls */ });
Before invoking a method, it’s a good practice to check whether the current calling thread is different from UI thread. And if the current thread is the UI thread, we can update controls directly, not using Dispatcher. To examine the current thread, there is a method CheckAccess() of Dispatcher object. It returns true if the calling thread is the UI thread, otherwise, false. Taking this fact into account, a typical way to update control is:
public void UpdateSomeTextBox(string text) { if (Dispatcher.CheckAccess()) // if it's UI thread, just set the new text directly someTextBox.Text = text; else // otherwise, invoke UpdateSomeTextBox in UI thread Dispatcher.BeginInvoke(delegate() { UpdateSomeTextBox(text); }); }
Interestingly enough that the method Dispatcher.CheckAccess() is invisible for Intellisense, because, for some unknown reason, it has been marked with a [EditorBrowsable(EditorBrowsableState.Never)] attribute. It looks like Microsoft doesn’t want developers to use CheckAccess. But does an alternative exist? Let’s try to find it. First of all, let’s take a look at this method through the Reflector:
[EditorBrowsable(EditorBrowsableState.Never)] public bool CheckAccess() { return (NativeHost.Current.UIThreadID == Thread.CurrentThread.ManagedThreadId); }
It just compares an identifier of UI thread with an identifier of the current thread. NativeHost is an internal class, therefore we can’t get NativeHost.Current.UIThreadID outside of a dll where property declared. How else can we get the identifier of UI thread? As we know all controls, including page, are created inside UI thread. It means that inside a page constructor the property Thread.CurrentThread.ManagedThreadId is the required UI thread identifier. So, we need take this identifier and preserve to use later. The following example demonstrates how it could look like:
using System; using Microsoft.Phone.Controls; namespace WindowsPhoneApplication1 { public partial class MainPage : PhoneApplicationPage { private int _uiThreadID = -1; private bool IsUIThread { get { return _uiThreadID == System.Threading.Thread.CurrentThread.ManagedThreadId; } } // Constructor public MainPage() { // preserve UI thread id _uiThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId; InitializeComponent(); } public void UpdateSomeTextBox(string text) { if (IsUIThread) // if it's UI thread, just set the new text directly someTextBox.Text = text; else // otherwise, invoke UpdateSomeTextBox in UI thread Dispatcher.BeginInvoke(delegate() { UpdateSomeTextBox(text); }); } } }
Thanks!