MVVM Light Toolkit Pitfalls on Windows Phone 7 and How to Avoid Them
I’ve been leaning heavily on Laurent Bugnion’s MVVM Light Toolkit framework in the course of learning Windows Phone 7[footnote:If you're interested in developing for Windows Phone 7, you can download all of the development tools for free from http://developer.windowsphone.com/] development for my work as a developer evangelist at Microsoft. It’s a fantastic framework and it does an excellent job of enabling SOC in Windows Phone 7 application development.
However, during some of my experimentation with the framework I’ve run into some initially puzzling development pitfalls that I wanted to share and demystify.
These pitfalls might also apply to vanilla Silverlight and WPF in general, but my experience has been with Windows Phone 7 development so I’ll qualify these pitfalls accordingly:
Pitfall #1 – DispatcherHelper is NOT initialized by default
The DispatcherHelper is useful for scenarios like this one from my Twitter search MVVM demo on WP7 where you have to marshal UI updates across threads:
//ListBox on MainPage.xaml is bound to this collection of Tweet objects public ObservableCollection Results { get; private set; } //Callback method when RestSharp returns an RSS feed worth of tweets relevant to our keyword public void GetResults(RestResponse results) { var returnResults = SearchResultConverterHelper.ConvertRssFeed(results.Data); DispatcherHelper.CheckBeginInvokeOnUI(() => { Results.Clear(); foreach (var item in returnResults) { Results.Add(item); } }); }
If you try to use this code as-is in your application you’ll receive a NullReferenceException stemming from the DispatcherHelper itself. This is because the default MVVM Light Toolkit project templates don’t call DispactherHelper.Initialize() anywhere; so the fix to this is pretty easy – just add this line of code inside your App.xaml.cs constructor:
public App() { // Global handler for uncaught exceptions. // Note that exceptions thrown by ApplicationBarItem.Click will not get caught here. UnhandledException += Application_UnhandledException; // Standard Silverlight initialization InitializeComponent(); //Readies the DispatcherHelper for usage throughout application DispatcherHelper.Initialize(); // Phone-specific initialization InitializePhoneApplication(); }
This will ensure that any calls you make to the DispatcherHelper
Pitfall #2 – EventToCommand namespaces are not included by default
EventToCommand is awesome. Take a moment to look at the documentation on it and bask in its elegant simplicity. Here’s an example below from my Twitter demo again:
<TextBox Height="71" Name="txtBoxSearch" Text="" Width="370" /> <Button Content="Go!" Height="71" Name="btnSearch" Width="Auto"> <Interactivity:Interaction.Triggers> <Interactivity:EventTrigger EventName="Click" x:Name="btnSearchClickedEventTrigger"> <Command:EventToCommand Command="{Binding ExecuteSearch}" CommandParameter="{Binding Text, ElementName=txtBoxSearch}"/> </Interactivity:EventTrigger> </Interactivity:Interaction.Triggers> </Button>
However, there is one minor pitfall which is that in order for this code to compile you need to make sure that both of these namespaces are included in your XAML file:
xmlns:Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP7" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
This isn’t really a pitfall so much as it is a snag that can slow you down when you’re trying to hammer out code.
Pitfall #3 – Messenger.Register/Send may not occur in the order you expect
Here are two pieces of code I have in a new Windows Phone 7 application I’m building – the first is in my MainViewModel class and the other is in the MainPage.xaml.cs code-behind:
MainViewModel.cs
//From the constructor of my MainViewModel class DetermineAccountStatus = new RelayCommand(() => { if (Accounts.Count == 0) Messenger.Default.Send(MessagingConstants.NoTwitterAccountsAuthorized, MessagingConstants.AccountsStatus); else Messenger.Default.Send(MessagingConstants.AtLeastOneTwitterAccountAuthorized, MessagingConstants.AccountsStatus); }); DetermineAccountStatus.Execute(new object());
MainPage.xaml.cs
public MainPage() { InitializeComponent(); //Receive messages from the view model notifying us if we need to show our "no accounts added" message or not Messenger.Default.Register(this, MessagingConstants.AccountsStatus, s => { if (s == MessagingConstants.NoTwitterAccountsAuthorized) this.txtBlockMissingAccounts.Visibility = System.Windows.Visibility.Visible; else this.txtBlockMissingAccounts.Visibility = System.Windows.Visibility.Collapsed; }); }
So what’s the problem here? The problem is that because the XAML parser instantiates the MainViewModel first because the ViewModelLocator is included in the App.xaml resource dictionary, which is hit first by the XAML parser before the MainPage.xaml file is!
<!--Application Resources--> <Application.Resources> <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" /> </Application.Resources>
Therefore the message is sent before the MainPage class ever has a chance to register to receive it.
This is a design mistake and it’s easy to make: don’t send any messages during object instantiation. Instead, let the View invoke the ViewModel after it’s loaded, like so:
public MainPage() { InitializeComponent(); //Receive messages from the view model notifying us if we need to show our "no accounts added" message or not Messenger.Default.Register(this, MessagingConstants.AccountsStatus, s => { if (s == MessagingConstants.NoTwitterAccountsAuthorized) this.txtBlockMissingAccounts.Visibility = System.Windows.Visibility.Visible; else this.txtBlockMissingAccounts.Visibility = System.Windows.Visibility.Collapsed; }); Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded); } void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e) { //Tell the ViewModel to call us back regarding whether or not we have any accounts to list in our listbox ViewModel.ViewModelLocator.MainStatic.DetermineAccountStatus.Execute(new object()); }
The MVVM Light Toolkit is a great framework and it’s a pleasure to work with once you start to understand some the nuances of Silverlight / Windows Phone 7 development. If you have any questions please feel free to leave a comment on this post!