Interested in what's coming up? Take a look at the Roadmap!

Kick-Start

To get started quickly, add references to the Liphofra assemblies in your Windows Phone project. Then, in the application class constructor, initialize the bootstrapper, like:

// initialize Liphofra
Bootstrapper.Initialize(RootFrame);

The "Bootstrapper" type is located in the namespace "Liphofra.Mvvm". Important: because it relies on the root phone application frame this initialization call obviously must be done after the "InitializePhoneApplication" call of the default Windows Phone template, so "RootFrame" is initalized properly.

What the bootstrapper does:

  • Uses the default IServiceLocator implementation (IoC container) to:
  • Register the default IEventAggregator implementation and
  • Register the default IViewModelFactory implementation and
  • Register the default INavigationService implementation

After that, you can use all the features of Liphofra using the provided default implementations. You can override any of these (including the service locator itself) by doing the registrations manually.

Phone Application Pages

To prepare your pages for special Liphofra features, do the following:

1.) After creating a default phone application page from the Visual Studio templates, change the base type in the code-behind class to "Liphofra.Mvvm.Views.PhoneApplicationPage"

2.) Change the type in Xaml also. To this end, you need to add a namespace definition for the corresponding Liphofra namespace:

<liphofra:PhoneApplicationPage 
    xmlns:liphofra="clr-namespace:Liphofra.Mvvm.Views;assembly=Liphofra.Mvvm" />
<!-- ... -->
</liphofra:PhoneApplicationPage>

As a convenience, the page offers the following protected properties:

  • A property named "ViewModel" of type "INavigationViewModel": access to the connected view model instance.
  • A property named "ServiceLocator" of type "IServiceLocator": access to the IoC container. If you use the default implementation of the view model locator, this will be the the built-in "ServiceLocator".

In addition to the normal overridable protected methods of a "PhoneApplicationPage", the Liphofra implementation also differentiates a bit more between scenarios like "the user returned to the page from tombstoning" or "the user returned to the page, but it wasn't tombstoned". This spares you writing the same boilerplate code again and again. In particular, the following overridables are available:

  • Notifications about navigations to the corresponding page:
    • The "OnInitialize" overridable: called once when the view model was created during a forward navigation and should initialize itself
    • The "OnRestoreState" overridable: called when a new instance of the view model was created during a back navigation (after tombstoning), or when a refresh is required in a fast app resume situation.
  • Notifications about navigations from the corresponding page:
    • The "OnNavigatingBack" overridable: called when the user navigates back from the page, provides the possibility to cancel navigation using the event args
    • The "OnNavigatingAway" overridable: called when the user navigates forward, away from the page. Cannot be cancelled.
    • The "OnNavigatedAway" overridable: called when the user has navigated forward, away from the page. Should be used to e.g. persist state.
    • The "OnNavigatedBack" overridable: called when the user has navigated back to the previous location, away from this page.

More methods available on the phone application page implementation:

  • "RegisterForEvent": simplifies registration for events with the event aggregator, for example to listen for notifications from the underlying view model.

The page implements some helper functionality behind the scenes. For example, when the user is about to navigate away from the page, it checks the currently focused UI element and updates its binding expression, if applicable. This makes sure that e.g. text box bindings are updated correctly and the actual value of these controls is reflected in the view model layer when the navigation notifications are raised.

The next step is to prepare your view model for the just created page.

View Models

Your page view models should derive from "Liphofra.Mvvm.ViewModels.NavigationViewModelBase". This base type provides the following convenience properties:

  • A property named "ServiceLocator" of type "IServiceLocator": access to the IoC container. If you use the default implementation of the view model locator, this will be the the built-in "ServiceLocator".
  • A property named "NavigationService" of type "INavigationService" that provides access to common navigation features of Windows Phone, like navigating to other pages
  • A property named "EventAggregator" of type "IEventAggregator" property that provides acccess to the event aggregation features of Liphofra

If you don't change the default bootstrapping mechanism, these properties will contain the default Liphofra implementations of these interfaces.

The view model also supports the same new extension points as the corresponding page implementation, as overridable protected methods:

  • Notifications about navigations to the corresponding page:
    • The "OnInitialize" overridable: called once when the view model was created during a forward navigation and should initialize itself
    • The "OnRestoreState" overridable: called when a new instance of the view model was created during a back navigation (after tombstoning), or when a refresh is required in a fast app resume situation.
  • Notifications about navigations from the corresponding page:
    • The "OnNavigatingBack" overridable: called when the user navigates back from the page, provides the possibility to cancel navigation using the event args
    • The "OnNavigatingAway" overridable: called when the user navigates forward, away from the page. Cannot be cancelled.
    • The "OnNavigatedAway" overridable: called when the user has navigated forward, away from the page. Should be used to e.g. persist state.
    • The "OnNavigatedBack" overridable: called when the user has navigated back to the previous location, away from this page.

In addition, as a convenience the normal page overrides are invoked on the view model also:

  • The "OnNavigatedTo" overridable: this is the generic notification that the page was being navigated to, without the differentiation between the above "OnRestoreState" and "OnInitialize". It is also called when neither initialization nor a restore is required, e.g. when the app has just been deactivated.
  • The "OnNavigatingFrom" overridable: this is the generic notification that the page is about to being navigated from, without the differentiation between the above "OnNavigatingBack" and "OnNavigatingAway".
  • The "OnNavigatedFrom" overridable: this is the generic notification that the page was being navigated from, without the differentiation between the above "OnNavigatedBack" and "OnNavigatedAway".

The view model implementation of course also supports INotifyPropertyChanged (using the "OnPropertyChanged" base method) and has a default implementation of "IDisposable" that is automatically invoked

Connecting Pages and View Models

To bring together pages and their corresponding view models, so-called view model locators are used. Liphofra favors one-to-one relationships between views and view models, meaning that there typically are no singleton instances or similar constructs for view models, as you may be used to in other frameworks. This has some design implications for your application, for example it usually forces you to store state and data in lower layers ("business logic") rather than using the view models for that.

Liphofra has a dynamic view model locator implementation that makes it simple to set up and use this mechanism for most cases. Here is what needs to be done:

1.) In your application resources, add an entry that creates a dynamic view model locator, like:

<Application.Resources>
    <liphofra:DynamicViewModelLocator x:Key="DynamicViewModelLocator" />
</Application.Resources>

2.) In your pages, set the data context using that view model locator:

<liphofra:PhoneApplicationPage
    DataContext="{Binding [MyViewModel], Source={StaticResource DynamicViewModelLocator}}" />

Please note the square brackets around the view model type name – these are important and required due to a limitation of C#'s dynamic features on the phone. What this does is:

  • Treat the name given in the square brackets as type name of the view model you want to connect
  • Resolve the type name using all currently loaded assemblies (cached for better performance)
  • Create a new instance of that type using the configured implementation of the "IViewModelFactory".

The default implementation of th factory uses the configured IoC container to resolve the view model instance (so you could actually also do a singleton view model approach if you registered a view model instance as singleton) and configures its properties if the resolved type is an "INavigationViewModel" instance.

Advanced Scenarios

Liphofra also supports custom view model locators for each and every view and view model pair, if you want to do that. To this end, an abstract "ViewModelLocatorBase" type exists that provides properties for both design-time and runtime view models. You would typically derive from this locator to define the view model types to use for a view explicitly.

A convenience derived type from this base locator also exists, which only takes one generic type parameter. This implementation provides the identical types for both design-time and runtime.

More Features

Liphofra has more features than the above mentioned ones. In particular:

  • "Liphofra.Core.Storage.FileStorage" to persist and load any (JSON-serializable) type into isolated storage.
  • "Liphofra.Mvvm.Commands.RelayCommand" as a convenience default implementation for commanding.
  • "Liphofra.Mvvm.Converter.BooleanToVisibilityConverter": probably the most implemented value converter world-wide :).
  • Convenience extension methods for working with dictionaries (in "System.Collections.Generic" of Liphofra.Core).

Last edited Mar 29, 2013 at 6:30 PM by Mister_Goodcat, version 6

Comments

No comments yet.