2011年5月27日金曜日

How to solve "Invalid cross-thread access" error in Silverlight

In Silverlight, an UnauthorizedAccessException with the message "Invalid cross-thread access" is thrown when jobs that should be done in the UI thread are tried to be done in other threads.

To make the UI thread do something, the instance of Dispatcher class of the UI thread (there is one Dispatcher instance per Thread) needs to be obtained and jobs for the UI thread should be passed to BeginInvoke method of the instance. For example, given a variable named 'dispatcherInstance' holds an instance of Dispatcher class, the following code will work.

    dipatcherInstance.BeginInvoke(() => DoMyJob());

There are some ways to access an instance of Dispatcher class. DependencyObject class has a property named 'Dispatcher' and it is the simplest way to use it. Within classes which are derived from DependencyObject class (because UIElement class is derived from DependencyObject class, as a result, all sub classes of UIElement are included), using the property, jobs can be passed to the dispatcher by the code like below.

    Dispatcher.BeginInvoke(() => DoMyJob());

To access the Dispatcher instance of the UI thread from where the Dispatcher property is not available, System.Windows.Deployment class can be used like the following.

    Deployment.Current.Dispatcher.BeginInvoke(() => DoMyJob());

Note that because an UnauthorizedAccessException may be raised when it is tried to access the Dispatcher instance of the UI thread via the property 'Application.Current.RootVisual' (because RootVisual belongs to the UI thread), it is better to use Deployment class.

------

"Invalid cross-thread access" 問題の解決の仕方 (Silverlight)

Silverlight において、UI スレッドで処理すべきことをそれ以外のスレッドからおこなおうとすると、Invalid cross-thread access というメッセージを伴う UnauthorizedAccessException が発生してしまう。

UI スレッドに処理をさせたい場合は、UI スレッド用の Dispatcher クラスのインスタンスを取得して(UI スレッドに限らず、それぞれのスレッドが、Dispatcher クラスのインスタンスを一つだけ持っている)、そのインスタンスの BeginInvoke メソッドに、UI スレッドで実行してほしい処理を渡せばよい。例えば、dispatcherInstance という変数に Dispatcher クラスのインスタンスが入っているとした場合、次のように記述する。

    dipatcherInstance.BeginInvoke(() => DoMyJob());

Dispatcher クラスのインスタンスにアクセスする方法は幾つかある。DependencyObject には、その名も Dispatcher というプロパティーがあるので、それを利用するのが一番簡単である。DependencyObject から派生するクラス(UIElement クラスは DependencyObject クラスから派生しているので、結果的に、これには UIElement から派生する全てのクラスが含まれる)内のコードでは、このプロパティーを利用して次のように記述することで、Dispatcher に処理を渡すことができる。

    Dispatcher.BeginInvoke(() => DoMyJob());

Dispatcher プロパティーにアクセスできない場所から UI スレッドの Dispatcher インスタンスを取得したい場合は、System.Windows.Deployment クラスを使用して、次のように記述する。

   Deployment.Current.Dispatcher.BeginInvoke(() => DoMyJob());

なお、Application.Current.RootVisual というプロパティー経由で UI スレッドの Dispatcher にアクセスしようとすると、UnauthorizedAccessException が発生しうるので(RootVisual は UI スレッドに属しているので)、Deployment クラスの方を使うのがよい。