-
First steps with Caliburn Micro in Windows Phone 8 – Using launchers and choosers
In the previous post we’ve seen how the messaging infrastructure works in Caliburn Micro. In this post we’ll see how to use launchers and choosers within a ViewModel: the two topics are strictly connected, since to accomplish this task Caliburn uses a special type of messages. In fact, when we want to interact with launchers and choosers, we exchange messages using the IEventAggregator class we learned to use in the previous post. The difference is that messages are “hidden” and already built in in the toolkit, so we won’t need to define them. Let’s start!
I assume that, since you’re reading this blog and since MVVM is an “advanced “ topic, you already know what launchers and choosers are. In case not, it’s a way that the operating system offers to interact with native applications: for example, displaying a position in the native Map app; or import a contact from the People hub; or send a SMS using the Messages app.
Launchers
Let’s see how to interact with launchers, that are the tasks that are used to demand an operation to a native application, without expecting anything in return: we’ll do this by using the MapsTask class, that is used to display a location in the native maps application. I assume that you already have an application that is created using the Caliburn toolkit, so that you have a View connected to a ViewModel.
In the View we add a button, that we will use to execute the launcher:
<StackPanel> <Button Content="Launch map" x:Name="LaunchMap" /> </StackPanel>
As usual, thanks to the Caliburn naming conventions, the click on the button will trigger the LaunchMap method in the ViewModel. Let’s take a look at the ViewModel:
public class MainPageViewModel: Screen { public MainPageViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; eventAggregator.Subscribe(this); } public void LaunchMap() { eventAggregator.RequestTask<MapsTask>(task => { task.SearchTerm = "Milan"; }); } }
As we’ve seen in the previous post, it’s enough to add in the constructor a parameter which type is IEventAggregator to have a reference to the real object in our ViewModel. The first step is to subscribe to receive messages, by using the Subscribe method, passing the value this as parameter (since we want to register the ViewModel itself).
In the LaunchMap method we use again the EventAggregator, by calling an extension method that is available exclusively for Windows Phone: **RequestTask
**, where **T** is the launcher we want to execute (selected by one of the objects that is available in the **Microsoft.Phone.Tasks** namespace). As parameter of the method we can pass a delegate (in this sample, we use a lambda expression): this is needed for all the launchers or choosers that need some properties to be set to work properly. In this sample, we set the **SearchTerm** property of the task, to specify the location we want to search on the map. If we’re going to use a launcher that doesn’t need any parameter (like the **MarketplaceDetailTask**), we can simply pass nothing to the method. If you try this code on the emulator or on the phone, you’ll see the Maps app opening and locating the Milan position as soon as you’ll tap on the button.
###
Choosers
Choosers work in a little bit different mode: the way we’re going to execute it is the same that we’ve seen for launchers but, in this case, we expect also a result in return, that we need to manage. As I’ve already told you, launchers and choosers infrastructure in Caliburn is based on messaging, so the way we’re going to manage results is the same we’ve seen in the previous post to manage incoming messages. Let’s see how it works using the PhoneNumberChooserTask, that can be used to import a contact from the People hub to the application.
First, let’s add a new button in the XAML:
<StackPanel> <Button Content="Open contact" x:Name="OpenContact" /> </StackPanel>
Now let’s take a look at the ViewModel:
public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>> { private readonly IEventAggregator eventAggregator; public MainPageViewModel(IEventAggregator eventAggregator, INavigationService navigationService) { this.eventAggregator = eventAggregator; eventAggregator.Subscribe(this); } public void OpenContact() { eventAggregator.RequestTask<PhoneNumberChooserTask>(); } public void Handle(TaskCompleted<PhoneNumberResult> message) { MessageBox.Show(message.Result.DisplayName); } }
The way a chooser works is the same as launchers: we call the **RequestTask
** method of the **EventAggregator**, passing as value of the T parameter the chooser’s type (**PhoneNumberChooserTask**). In this case, we don’t pass any parameter, since the chooser doesn’t require any property to be set to work properly. To manage, instead, the return value we use the same approach we’ve seen to receive a message: we inherit our ViewModel from the classe **IHandle **, where T is **TaskCompleted **. In this case, **T** is the class used by the operatying system to return a chooser’s result to the caller: in this sample, it’s a **PhoneNumberResult**, since it contains the phone number of the selected contact. Once we inherit the ViewModel from this class, we need to implement the Handle method, that receives as a parameter the result returned by the chooser. It’s up to us what to do: in this sample, we simply take the DisplayName property (which contains the name of the selected contact) and we show it using a MessageBox.
How to correctly manage the launchers and choosers lifecycle
There’s a problem in using the messaging architecture to manage launchers and choosers. When you subscribe a ViewModel to wait for incoming messages using the Subscribe method of the EventAggregator, the subscription is kept alive until the ViewModel instance itself is alive. In case of launchers and choosers this can cause some issues: if you rememer in the previous post, we were able to exchange messages between two different ViewModels (more precisely, it’s the reason why messages are used); in this case, instead, we’re using the messaging infrastructure to exchange messages inside a single ViewModel. What happens in the end is that if, for example, you move the OpenContact method to another ViewModel, but you keep the MainPageViewModel registered to listen for incoming **TaskCompleted
** message, you will notice that the MainPageViewModel will answers to requests that are triggered by other view models. This can cause some problems, especially if you need to use the same launcher or chooser in multiple views, so you have many view models registered to receive the same message. This is valid especially for the MainViewModel: since it’s connected to the first page of the application, its instance is always alive until the app is closed, unlike for the other pages of the application (if you’re in the second page of your application and you go back to the main page, the second page is dismissed and its ViewModel is disposed).
The best way to manage this situation is to use the navigation events we’ve learned to use in a previous post and to inherit our ViewModel from the Screen class: this way we can call the Subscribe method of the EventAggregator class when the user navigate towards the page (managed by the OnActivate method) and call the Unsubscribe method, instead, when the user leaves the page (managed by the OnDeactivate method). With this approach, when the user navigates to another page the previous page isn’t subscribed anymore to receive messages and, as a consequence, will ignore any interaction with launchers and choosers.
Here is a sample of the updated ViewModel
public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>> { private readonly IEventAggregator eventAggregator; public MainPageViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; } protected override void OnActivate() { eventAggregator.Subscribe(this); base.OnActivate(); } protected override void OnDeactivate(bool close) { eventAggregator.Unsubscribe(this); base.OnDeactivate(close); } public void OpenContact() { eventAggregator.RequestTask<PhoneNumberChooserTask>(); } public void Handle(TaskCompleted<PhoneNumberResult> message) { MessageBox.Show(message.Result.DisplayName); } }
As you can see, we don’t call anymore the Subscribe method of the EventAggregator class in the ViewModel constructor, but we’ve moved it in the OnActivate method.
Using the link below ou can downlod a sample project if you want to play with launchers and choosers on your own.
The Caliburn Micro posts series
in
-
First steps with Caliburn Micro in Windows Phone 8 – Messaging
When you develop an application using MVVM one of the most frequent scenarios is the communication between different view models: for example, you need to exchange data between two view models or you need that, as a consequence of an action in a page, something should happen in another page. In MVVM application the answer is messaging! All the MVVM toolkits offer a messenger class: you can think of it like a postman, that is able to dispatch and to process messages that are exchanged between view models.
UPDATE: since many of you have asked, I’ve updated the post to explain how to manage communications also between a View and a ViewModel
The message is represented by a simple class: it can have one or more properties, where you can store the data that you need to pass between the view models. Using messaging, usually, means to apply the following flow:
- A view model prepares a message, by creating a new instance of the class that represents it and then send it using the messenger.
- One or more view models can subscribe to receive that type of message: in that case, they are notified every time another view model sends that type of message and they can act, to process it and to do some operations. </ul> Let’s see how this is implemented in Caliburn Micro. The scenario we’re going to use for our sample is the following: we have two views, each one with its view model. The second page has a button to send a message, that contains a name: when the button is pressed, the message with the name is passed to the first view model, so that it can be displayed in the first page.
The first step is to setup the project: please follow the steps described in all the previous tutorials to create the bootstrapper, the two pages and the two view models and to connect all of them together.
Before taking care of the messaging stuff, let’s prepare the needed infrastructure to make the app working. The first thing is to add, in the first page, a TextBox and a Button: the first one will be used to show the information passed by the other page, the second one will be used to navigate to the second page. Open the main page (usually the MainPage.xaml file) and add the following XAML in the Grid labeled ContentPanel:
<StackPanel> <TextBox x:Name="Name" /> <Button Content="Go to page 2" x:Name="GoToPage2" /> </StackPanel>
Again, we are using the standard Caliburn naming conventions: the TextBox control will be bound with a property called Name in the ViewModel, while the button, when clicked, will execute the GoToPage2 method defined in the ViewModel.
And here’s our ViewModel:
public class MainPageViewModel: Screen { private string name; public string Name { get { return name; } set { name = value; NotifyOfPropertyChange(() => Name); } } public MainPageViewModel(INavigationService navigationService) { this.navigationService = navigationService; } public void GoToPage2() { navigationService.UriFor<SecondPageViewModel>().Navigate(); } }
There isn’t too much to say about it: it has a Name property, which type is string, and it holds a reference to the Caliburn’s navigation service, which is used in the GoToPage2 method to redirect the user to the second page of the application. Now let’s see the XAML inside the ContentPanel of the second page, that is very simple too:
<StackPanel> <Button Content="Send message" x:Name="SendMessage" /> </StackPanel>
The page contains a button, that is linked to the method SendMessage that is declared in the ViewModel. Let’s take a look at the ViewModel of the second page, that is much more interesting because we introduce the classes needed to exchange messages:
public class SecondPageViewModel: Screen { private readonly IEventAggregator eventAggregator; public SecondPageViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; } public void SendMessage() { eventAggregator.Publish(new SampleMessage("Matteo")); } }
The class that is used to manage all the messaging stuff in Caliburn is called EventAggregator and, as for the NavigationService, it’s built in in the framework: this means that it’s enough to add a parameter in the constructor of your ViewModel which type is IEventAggregator to automatically have a reference to the object. Sending a message is really simple: you call the Publish method passing, as a parameter, the object that represents the message you want to send. As I’ve already told you before, the message is a simple class, that can hold one or more properties. Here is an example of the SampleMessage class:
public class SampleMessage { public string Name { get; set; } public SampleMessage(string name) { Name = name; } }
Nothing special to say about: it’s a class with a property **called **Name. In the constructor, we allow the developer to set a value for this property simply by passing it as a parameter. The result is that, in the previous sample, we use the EventAggregator to publish a message that contains the value Matteo in the Name property.
Ok, the EventAggregator, that is our postman, has sent the message. How can the ViewModel of the first page receive it? We need to do some changes:
public class MainPageViewModel: Screen, IHandle<SampleMessage> { private readonly IEventAggregator eventAggregator; private readonly INavigationService navigationService; private string name; public string Name { get { return name; } set { name = value; NotifyOfPropertyChange(() => Name); } } public MainPageViewModel(IEventAggregator eventAggregator, INavigationService navigationService) { this.eventAggregator = eventAggregator; this.navigationService = navigationService; eventAggregator.Subscribe(this); } public void GoToPage2() { navigationService.UriFor<SecondPageViewModel>().Navigate(); } public void Handle(SampleMessage message) { Name = message.Name; } }
The first step is to inherit our ViewModel from the **IHandle
** class, that is part of the Caliburn toolkit and that it’s been created specifically for messaging purposes. When a ViewModel inherits from **IHandle **, we say that the ViewModel will be able to receive messages which type is T. In our sample, since the **SecondPageViewModel** class sends a message which type is **SampleMessage**, we make the **MainPageViewModel** class to inherit from **IHandle .** When we inherit from this interface, we are forced to implement in the ViewModel the Handle method, that contains as parameter the message that has been received. As you can see, dealing with messages is really simple: we simply need to include in the Handle method the code that we want to execute when the message is received. In this sample, we set the Name property of the ViewModel with the value of the Name property that comes with the message.
There’s another important thing to do to get messages working: we need to tell to the ViewModel that we want to subscribe to receive messages. For this reason, we need a reference to the IEventAggregator class also in the view model that will receive the message: we do it in the constructor, as usual, and, once we have it, we call the Subscribe method passing a reference to the ViewModel itself (we do this by passing the value this).
And we’re done! Now if we launch the application, we go to the second page, we press the Send message button and we go back to the first page, we’ll see that in the TextBox the string Matteo will be displayed.
###
###
Communications between the View and the ViewModel
In some cases you may need to exchange data between the view and the view model, for example to manage animations or events that can’t be subscribed using binding. The approach to do that is exactly the same we’ve just seen: the only difference is that we need a workaround to get access to the EventAggregator, since we can’t put a parameter which type is IEventAggregator in the constructor of the page: Caliburn Micro won’t be able to resolve it and will raise an exception. First we need to apply a change to the bootstrapper, to make the PhoneContainer object (that is the used to register and resolve classes in the application) a public property, so that it can be accessed also from other classes:
public class Bootstrapper : PhoneBootstrapper { public PhoneContainer container { get; set; } protected override void Configure() { container = new PhoneContainer(); container.RegisterPhoneServices(RootFrame); container.PerRequest<MainPageViewModel>(); AddCustomConventions(); } static void AddCustomConventions() { //ellided } protected override object GetInstance(Type service, string key) { return container.GetInstance(service, key); } protected override IEnumerable<object> GetAllInstances(Type service) { return container.GetAllInstances(service); } protected override void BuildUp(object instance) { container.BuildUp(instance); } }
Then we need, in the code behind of the page, to get a reference to the container:
private IEventAggregator eventAggregator;
// Constructor public MainPage() { InitializeComponent();
Bootstrapper bootstrapper = Application.Current.Resources["bootstrapper"] as Bootstrapper; IEventAggregator eventAggregator = bootstrapper.container.GetAllInstances(typeof (IEventAggregator)).FirstOrDefault() as IEventAggregator; this.eventAggregator = eventAggregator; eventAggregator.Subscribe(this); }</pre> First we get a reference to the bootstrapper: since it’s declared as a global resource, we can access to it with its key by using the **Application.Current.Resources** collection. Then, thanks to the modify we did before in the bootstrapper, we are able to access to the container and, using the **GetAllInstances** method, we get a reference to the **EventAggregator** class that has been automatically registered by Caliburn Micro at startup. We need to specify, as parameter of the method, which is the type of the class we want to get a reference, in this case **typeof(IEventAggregator).** Since there’s just one **EventAggregator** registered in the application, this collection will always contain just one element: we take it using the **FirstOrDefault()** LINQ operation. Now that we have a reference to the **EventAggregator** class, we can use the same approach we’ve seen before for view models. If we want to receive a message, we have to call the **Subscribe()** method and we need to inherit our page from the **IHandle<T>** class, where **T** is the message we need to manage. ****By implementing this interface we’ll have, also in the view, to implement the **Handle** method, that receives the message as parameter. Here is an example: <pre class="brush: csharp;">public partial class MainPage : PhoneApplicationPage, IHandle<SampleMessage> { private IEventAggregator eventAggregator; // Constructor public MainPage() { InitializeComponent(); Bootstrapper bootstrapper = Application.Current.Resources["bootstrapper"] as Bootstrapper; IEventAggregator eventAggregator = bootstrapper.container.GetAllInstances(typeof (IEventAggregator)).FirstOrDefault() as IEventAggregator; this.eventAggregator = eventAggregator; eventAggregator.Subscribe(this); } public void Handle(SampleMessage message) { MessageBox.Show(message.Name); }
}</pre>
In case, instead, we want to send a message we call the **Publish** method, passing the object that represents the message to send: <pre class="brush: csharp;">public partial class MainPage : PhoneApplicationPage { private IEventAggregator eventAggregator; // Constructor public MainPage() { InitializeComponent(); Bootstrapper bootstrapper = Application.Current.Resources["bootstrapper"] as Bootstrapper; IEventAggregator eventAggregator = bootstrapper.container.GetAllInstances(typeof (IEventAggregator)).FirstOrDefault() as IEventAggregator; this.eventAggregator = eventAggregator; eventAggregator.Subscribe(this); } private void OnSendOtherMessageClicked(object sender, RoutedEventArgs e) { eventAggregator.Publish(new SampleMessage("Matteo")); } }</pre> In this sample we’ve created an event handler to manage the **Click** event of a button: when the user taps on it the message is sent directly from the View. Obviously, this is just a sample, in a real scenario is not useful: it would be better to create a method in the ViewModel and connect it to the button using the Caliburn naming convention. But there are some events that can’t be connected to a command using binding: in this case, using messages is a good alternative. Keep visiting this blog because we’re not done yet <img class="wlEmoticon wlEmoticon-smile" style="border-top-style: none; border-bottom-style: none; border-right-style: none; border-left-style: none" alt="Smile" src="https://i1.wp.com/wp.qmatteoq.com/wp-content/uploads/2013/03/wlEmoticon-smile.png?w=640" data-recalc-dims="1" />More posts about Caliburn Micro and Windows Phone are coming! In the meantime, you can play with a sample project that implements what we’ve seen in this post. <div id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:53ac6b8d-fbe1-4d52-8335-66d78c27330f" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <p> <a href="http://wp.qmatteoq.com/wp-content/uploads/2013/07/CaliburnMicro_Messaging1.zip" target="_blank">Download the sample project</a> </p> </div> **The Caliburn Micro posts series** 1. <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-the-theory/" target="_blank">The theory</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-the-first-project/" target="_blank">The first project</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-actions/" target="_blank">Actions</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-collections-and-navigation/" target="_blank">Collections and navigation</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-tombstoning/" target="_blank">Tombstoning</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-advanced-navigation-and-deep-links/" target="_blank">Advanced navigation and deep links</a> * Messaging * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-using-launchers-and-choosers/" target="_blank">Using launchers and choosers</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-use-your-own-services-and-how-to-pass-data-between-different-pages/" target="_blank">Use your own services</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-the-application-bar/" target="_blank">The Application Bar</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-pivot/" target="_blank">Pivot</a> * [Lazy loading with pivot](http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-lazy-loading-with-pivot/) </ol>
in
- A view model prepares a message, by creating a new instance of the class that represents it and then send it using the messenger.
-
First steps with Caliburn Micro in Windows Phone 8 – Advanced navigation and deep links
In one of the previous posts about Caliburn Micro we’ve explored how navigation is managed in a Windows Phone application developed using MVVM. In this post we’re going to cover some advanced scenarios, like intercepting the navigation events and working with the deep links that are widely used by many Windows Phone APIs.
Deep links
Windows Phone has always supported a way to pass parameters from a page to another using query string parameters: Windows Phone 7.5 has introduced the deep link concept, that is used to provide the same mechanism also when the application is opened from the outside. This feature is widely used: when you have a secondary tile, the navigation that is triggered uses a deep link to identify which tile has been tapped and which is the context to display; when you receive a toast notification and you tap on it the app is opened and you can use a deep link to identify the context and display a specific information; the same happens also for the new Voice control feature: when a voice command is issued, your app receives it using a deep link.
Intercepting this information is trivial in a MVVM application, because usually the operation consists in two steps:
- Intercepting the OnNavigatedTo event, that is triggered when you navigate towards the current page.
- Using the NavigationContext class to get access to the query string parameters. </ul> The problem is that both of these operations, usually, can be done only in the code behind of a view: you don’t have access to these events and classes in the view model.
Luckily Caliburn Micro provides a useful naming convention to manage this scenario: it’s enough to declare a property in your view model with the same name of the query string parameter and Caliburn Micro will automatically inject the value retrieved from the URL, in a similar way we’ve seen for the standard navigation in a previous post.
Let’s try it with a sample application and let’s start with the standard scenario: a view (the MainPage.xaml) and a view model, bind together with the standard naming convetion. We’re going to add in the view a button, that will be used to create a secondary tile. Plus, we’ll add also a TextBox, that will be used to display the value returned by the query string.
Here is the XAML:
<StackPanel> <TextBox Text="{Binding Name}" /> <Button Content="Create secondary tile" x:Name="CreateTile" /> </StackPanel>
And here is the code of the view model:
public class MainPageViewModel: PropertyChangedBase { private string name; public string Name { get { return name; } set { name = value; NotifyOfPropertyChange(() => Name); } } public void CreateTile() { ShellTileData tile = new StandardTileData { Title = "Test", }; ShellTile.Create(new Uri("/Views/MainPage.xaml?Name=Matteo", UriKind.Relative), tile); }
}</pre>
The **Name** property is the one connected with the TextBox, while the **CreateTile** method defines the template for the secondary tile with just a title. In the end we effectly create the tile, by passing the template and the deep link that identifies the tile. You can notice that the name of the parameter is the same of the property we’ve defined in the ViewModel: **Name**. Now launch the application: when you tap on the button the application will be closed, to display the new secondary tile that has just been created. Tap on the secondary tile and… the magic happens again! You’ll see the name “Matteo” displayed in the TextBox. This happens because the parameter in the query string is automatically injected in the property of the view model, since they both share the same name. Cool, isn’t it? ### ### Intercepting navigation events Another common scenario when you develop a Windows Phone application is to intercept navigation events, so that you can be notified when the view is displayed. Usually the trick is to register the view models of our views with the **PerRequest** method in the bootstrapper: this way, every time a view is requested a new view model is created, so we can initialize it in the constructor. But, sometimes, this isn’t the best approach and it can’t be applied to every page: for example, the main page is always alive, since it’s always part of the stack of the application’s pages. For this reason, you can’t rely on the public constructor of the view model in case you want do something (for example, refreshing the data) every time the user navigates back to the main page. For the same reasons I’ve explained when I talked about deep links, it’s an hard task to accomplish in a MVVM application: events like **OnNavigatedTo, OnNavigatedFrom** or **Loaded** are available only in the code behind, because a Windows Phone page inherits from the **PhoneApplicationPage** class. For this reason, Caliburn Micro offers a class to use in our view models, that provides similar events in a view model: the class’ name is **Screen** and, to use it, you’ll need to let your view model inherit from it. This class puts together many pieces of Caliburn Micro, so that you don’t have to inherit your view model from too many interfaces. If we take a look at the Screen class, we’ll notice the following definition: <pre class="brush: csharp;">public class Screen : ViewAware, IScreen, IHaveDisplayName, IActivate, IDeactivate, IGuardClose, IClose, INotifyPropertyChangedEx, INotifyPropertyChanged, IChild {
}</pre>
As you can see, this class implements many interfaces, specifically **ViewAware, IActivate** and **IDeactivate** that exposes all the needed methos to interact with the view lifecycle. Plus, it implements also the **INotifyPropertyChanged** event: in case your view model inherits from the **Screen** class you don’t have to make it inherits also from the **PropertyChangedBase** class, the method **NotifyOfPropertyChange** is already supported. Once your view model is set up, you can override some method to interact with the lifecycle of your view: <pre class="brush: csharp;">public class MainPageViewModel: Screen { protected override void OnViewAttached(object view, object context) { base.OnViewAttached(view, context); Debug.WriteLine("OnViewAttached"); } protected override void OnInitialize() { base.OnInitialize(); Debug.WriteLine("OnInitialize"); } protected override void OnViewReady(object view) { base.OnViewReady(view); Debug.WriteLine("OnViewReady"); } protected override void OnActivate() { base.OnActivate(); Debug.WriteLine("OnActivate:"); } protected override void OnViewLoaded(object view) { base.OnViewLoaded(view); Debug.WriteLine("OnViewLoaded"); } protected override void OnDeactivate(bool close) { base.OnDeactivate(close); Debug.WriteLine("OnDeactivate"); } }</pre> Here is a description of the methods: * **OnViewAttached** is invoked when the view model is set as data context of the view. * **OnInitalize** is called when the view is initalized. * **OnViewReady** is called when the view is ready to be displayed * **OnViewLoaded** is called when the view is fully loaded and every control in the page is initalized * **OnActivate** is called every time the view is displayed * **OnDeactivate** is called every time the view is hided, because you have navigated away to another page or you have closed or suspended the app. </ul> The most important ones are **OnActivate** and **OnDeactivate**, that match with the **OnNavigatedTo** and **OnNavigatedFrom** events in the page: you can use them to do custom operation that can’t be managed with the helpers provided by Caliburn Micro. Specifically, the **OnActivate** event is very important because it’s raised when the page is fully loaded and you are able to interact with it: typically this event is used to load in your view model the data that should be displayed in the view. Think about the structure of a view model: usually you are used to load all the data in the view model’s constructor, since it’s invoked every time the view is displayed. In case you need to do asynchronous operations there’s a problem: the constructor of a class can’t be asynchronous. One approach is to defer the loading operation to another method (for example, LoadData) that is marked as **async** and that is called in the public constructor. <pre class="brush: xml;">public MainPageViewModel() { LoadData(); }
private async void LoadData() { MyData = await service.LoadData(); }</pre>
Which is the problem of this approach? That LoadData needs to be a **void** method: it can’t return a **Task**, because in the view model’s constructor we can’t use the **await** keyword. This makes the LoadData method a “fire and forget” method: we launch it and we don’t wait until it’s finished. Most of the time this approach works, especially if the loading operation isn’t too long: usually the user won’t immediately interact with the application, so there’s enough time to wait that the data is fully loaded. But, in case the user starts an action that does some operations on our data, that isn’t ready yet, we can have problems. The solution is moving the loading operation inside the **OnActivate** method, that can be marked as async: this way we can safely execute all the needed asynchronous operations and be sure that when the user will start to interact with the application the data will be ready. <pre class="brush: xml;">public MainPageViewModel() {
}
protected override async void OnActivate() { MyData = await service.LoadData(); }</pre>
In this case it’s correct that the **OnActivate()** method’s type is “fire and forget”, since the activation operations are automatically managed by the operating system. It’s the same that happens when we mark as async an event handler: since the event is automatically managed by the OS, there’s no need to wait that the operations are finished before releasing the handler. The **OnActivate** method is also very important when we need to manage deep links: it can happen, in fact, that when the view model is created and the constructor is invoked the query string parameters aren’t stored yet in our properties. This happens because the **OnNavigatedTo** event (that is used to read the parameters) is triggered after that the view model is created. In the deep link sample we’ve seen earlier we couldn’t notice the issue, thanks to the binding: after that the page is fully loaded, the value of the query string parameter was stored in the **Name** property; thanks to the binding and to the **NotifyOfPropertyChange** event, as soon as it happened, the TextBox in the view was automatically updated with the value. However, we could have noticed the issue if we would have tried to manipulate the value of the **Name** property inside the view model’s constructor: in this case we would have noticed that the value is still null, so any operation on the data would have caused an exception. The workaround is to execute these operations inside the **OnActivate** method: when it’s triggered the navigation is already completed, so we will find the query string parameter in our **Name** property as expected. You can download a sample project the implements what we’ve learned in this post with the link below. <div id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:471f8804-d972-4a8c-9b8d-c70f3e92cf73" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <p> <a href="http://wp.qmatteoq.com/wp-content/uploads/2013/07/CaliburnMicro_AdvancedNavigation.zip" target="_blank">Download the sample project</a> </p> </div> **The Caliburn Micro posts series** 1. <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-the-theory/" target="_blank">The theory</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-the-first-project/" target="_blank">The first project</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-actions/" target="_blank">Actions</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-collections-and-navigation/" target="_blank">Collections and navigation</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-tombstoning/" target="_blank">Tombstoning</a> * Advanced navigation and deep links * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-messaging/" target="_blank">Messaging</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-using-launchers-and-choosers/" target="_blank">Using launchers and choosers</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-use-your-own-services-and-how-to-pass-data-between-different-pages/" target="_blank">Use your own services</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-the-application-bar/" target="_blank">The Application Bar</a> * <a href="http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-pivot/" target="_blank">Pivot</a> * [Lazy loading with pivot](http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-lazy-loading-with-pivot/) </ol>
in
- Intercepting the OnNavigatedTo event, that is triggered when you navigate towards the current page.
subscribe via RSS