It makes sense to execute long-running tasks on a background thread, in order to keep the UI responsive by not tying up the main thread. However, as is the case with other UI frameworks such as Windows Forms or WPF, you should not touch UI elements from worker threads. This has to do with how Windows apps process messages, which are always handled on the thread that created the visual element.
There are various mechanisms available for you to marshal a call from a worker thread back to the main thread. Windows Forms has the Control.InvokeRequired / Control.BeginInvoke API. WPF and Silverlight have Dispatcher.CheckAccess / Dispatcher.BeginInvoke. SynchronizationContext has a Post method that offers a higher level of abstraction, checking access and marshaling invocation at the same time. And the BackgroundWorker component lets you write events handlers that are executed on the UI thread when reporting progress or completion.
The problem all these approaches is that they require you to write code. It would be nice if there were some framework that took care of the grungy details of cross-thread marshaling so you can focus on the business logic. Well, it just so happens that my Simple MVVM Toolkit offers support for async execution right out of the box. For example, whenever you fire NotifyPropertyChanged from the setter in a view-model property, you should check to make sure that you’re on the UI thread and, if not, marshal the call over to the UI thread. That way, you won’t get an error when the binding handles the PropertyChanged event. The NotifyPropertyChanged method in ViewModelBase takes care of this for you by calling an InternalNotifyPropertyChanged method that guarantees the event will always be fired on the UI thread.
public static void InternalNotifyPropertyChanged(string propertyName, object sender, PropertyChangedEventHandler propertyChanged) { if (propertyChanged != null) { // Always fire the event on the UI thread if (Deployment.Current.Dispatcher.CheckAccess()) { propertyChanged(sender, new PropertyChangedEventArgs(propertyName)); } else { Deployment.Current.Dispatcher.BeginInvoke (() => propertyChanged(sender, new PropertyChangedEventArgs(propertyName))); } } }
Similarly, the issue of cross-thread calls rears its ugly head whenever you fire an event from the view-model that is handled by the view, for example, to alter the UI or display a ChildWindow. To shield you from those details, ViewModelBase has a set of Notify methods which support two-way communication between the view-model and view, but they call an InternalNotify method that marshals the call to the UI thread if needed.
protected void Notify (EventHandler<NotificationEventArgs> handler, NotificationEventArgs e) { if (handler != null) { InternalNotify(() => handler(this, e)); } } protected void Notify<TOutgoing> (EventHandler<NotificationEventArgs<TOutgoing>> handler, NotificationEventArgs<TOutgoing> e) { if (handler != null) { InternalNotify(() => handler(this, e)); } } protected void Notify<TOutgoing, TIncoming> (EventHandler<NotificationEventArgs<TOutgoing, TIncoming>> handler, NotificationEventArgs <TOutgoing, TIncoming> e) { if (handler != null) { InternalNotify(() => handler(this, e)); } } private void InternalNotify(Action method) { // Always fire the event on the UI thread if (Deployment.Current.Dispatcher.CheckAccess()) { method(); } else { Deployment.Current.Dispatcher.BeginInvoke(method); } }
An example of where you might want to use the Notify method is if you have the need to prompt the user for input in the course of executing a long-running task on a background thread. The tricky part is halting execution while waiting for a response, then resuming execution after receiving the response. Your friend here is a synchronization device like AutoResetEvent. The following is a section of code from a view-model that kicks off a worker thread to execute a for loop, prompting the user to halt or continue when reaching a certain threshold.
public class MainViewModel : ViewModelBase<MainViewModel> { // Events to notify the view or obtain data from the view public event EventHandler<NotificationEventArgs<bool, bool>> MaxReachedNotice; // Perform work asynchronously public void DoWork() { // Spin up a thread Thread worker = new Thread(InternalDoWork); worker.Name = "My Worker Thread"; worker.Start(); } private void InternalDoWork() { // Cancelled flag bool cancelled = false; // Max reached flag bool maxReached = false; // Wait handle var wait = new AutoResetEvent(false); // Flip IsBusy IsBusy = true; // Do the work for (int i = 0; i < Iterations; i++) { // Pause Thread.Sleep(500); // Calc value int val = (i + 1) * 10; // Notify view if we've reached the max if (val >= max && !maxReached) { var ea = new NotificationEventArgs<bool, bool> (null, true, cancel => { // Set cancelled and signal wait handle cancelled = cancel; wait.Set(); }); Notify<bool, bool>(MaxReachedNotice, ea); // Here we want to pause for the user wait.WaitOne(); // Set max reached maxReached = true; // Exit loop if cancelled if (cancelled) break; } // Set Result Result = val; } // Flip IsBusy IsBusy = false; } }
Notice that the Notify method takes event args that include a callback, which is specified using a lambda expression in which we set a cancelled flag and then signal a wait handle. Execution of the loop is paused until the wait handle is signaled. The code-behind for the view simply subscribes to the view-model’s OnMaxReachedNotice event and handles it by displaying the QueryUserDialog, invoking the Completed property of the events args, which tells the view-model whether or not to continue iteration.
public partial class MainPage : UserControl { // Reference view model MainViewModel viewModel; public MainPage() { // Get model from data context viewModel = (MainViewModel)LayoutRoot.DataContext; // Subscribe to notifications from the model viewModel.MaxReachedNotice += OnMaxReachedNotice; } void OnMaxReachedNotice(object sender, NotificationEventArgs<bool, bool> e) { QueryUserDialog dialog = new QueryUserDialog(); dialog.Closed += (s, ea) => { // Set the flag e.Completed(dialog.DialogResult == false); }; dialog.Show(); } }
Check out the sample app by downloading the Simple MVVM Toolkit and opening the SimpleMvvm-Async project.
whats the difference between using commands and event triggers and actions from the Blend SDK in MVVM pattern, which is best suitable and why.
@Giribabu: Commands are a cool feature of both WPF and Silverlight, which allow you bind the Command property of an element that derives from ButtonBase (such as a button, menu item or hyperlink button) to a property on the view-model which is of type ICommand. Typically, you would use a helper class (called DelegateCommand in my toolkit) to wire up the property to a method in the view-model. Commands will automatically enable and disable the button whenever the value of CanExecute changes.
I think commands are implemented better in WPF than in Silverlight. WPF has a command manager that will call RaiseCanExecuteChanged at the right times, but in Silverlight you need to call it manually whenever you want the CanExecute invoked. Aside from that, an inherent limitation of commands is that they only work on the Click event of a ButtonBase-derived element, which is pretty limiting. (You can get around this with something like an event-to-command behavior.) But the thing I like least about commands is that they require a lot of extra code in the view-model to support them, when all you really need is a method.
Enter event triggers from the Blend SDK. These allow you to write XAML that handles any event on any element, not just a button click. All you have to do is specify a method name for the CallMethodAction you use with the event trigger. No extra code needed in the view-model.
However, there is a limitation here, which is that the method cannot have any parameters. That means the method would need to use properties in the view-model. Most of the time, this works fine, because elements in the view are bound to view-model properties. However, there may be cases where it makes more sense to pass a parameter to the method in the view-model (for example, where you wish to navigate to a page and need to pass a relative uri). This is where it makes sense to use a command.
So I favor using event triggers / actions most of the time, and using commands when you need to pass a parameter. There is a generic version of DelegateCommand in my toolkit that accepts a strongly typed parameter. You can see it in action in the Navigation example that’s part of the download. You can also check out the online documentation for DelegateCommand. Cheers,
Tony
Hello!
I don’t know how in wpf, but in Silverlight 4 the method Dispatcher.CheckAccess() is invisible for Intellisense, because it has been marked with a [EditorBrowsable(EditorBrowsableState.Never)] attribute. Probably, Microsoft doesn’t want developers to use Dispatcher.CheckAccess(). Taking into account that all controls, including page, are created inside the UI thread, inside a page constructor the property Thread.CurrentThread.ManagedThreadId is the required UI thread identifier. We can use this fact it in the next way:
[csharp]
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);
});
}
}
}
[/csharp]
I’ve described it in details in my post in blog here http://dotnetfollower.com/wordpress/2011/07/silverlight-for-windows-phone-7-how-to-check-if-the-current-thread-is-ui-one/
Thank you!
.Net Follower (http://dotnetfollower.com)
As far as I’m concerned, there isn’t any reason you should not call Dispatcher.CheckAccess, even though it doesn’t show up in Intellisense. Just because the method is marked as EditorBrowsable(Never) does not mean that Microsoft does not want you to use it — it may simply be because of a bug in Visual Studio Intellisense. See the documentation for Dispatcher.CheckAccess: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx.