Compartir a través de


Bootstrapping an MVVM Windows Store app Quickstart using C#, XAML, and Prism

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

From: Developing a Windows Store business app using C#, XAML, and Prism for the Windows Runtime

Previous page | Next page

Learn how to bootstrap a Windows Store business app that uses the Model-View-ViewModel (MVVM) pattern and Prism for the Windows Runtime. Prism provides core services to a Windows Store business app, including support for bootstrapping MVVM apps, state management, validation of user input, navigation, event aggregation, data binding, commands, and settings.

Download

You will learn

  • How to bootstrap a Windows Store app that uses the MVVM pattern and a dependency injection container.
  • How to add specific startup behavior to a Windows Store app that uses the MVVM pattern.
  • How to bootstrap a Windows Store app that uses the MVVM pattern without a dependency injection container.

Applies to

  • Windows Runtime for Windows 8.1
  • C#
  • Extensible Application Markup Language (XAML)

This Quickstart uses the Unity container for dependency resolution and construction during the bootstrapping process. However, you are not required to use Unity, or any other dependency injection container, when bootstrapping an MVVM Windows Store app. To understand how to perform bootstrapping without using a dependency injection container, see Bootstrapping without a dependency injection container.

Building and running the Quickstart

Build the HelloWorldWithContainer Quickstart as you would a standard project:

  1. On the Microsoft Visual Studio menu bar, choose Build > Build Solution.
  2. After you build the project, you must deploy it. On the menu bar, choose Build > Deploy Solution. Visual Studio also deploys the project when you run the app from the debugger.
  3. After you deploy the project, pick the Quickstart tile to run the app. Alternatively, from Visual Studio, on the menu bar, choose Debug > Start Debugging.

When the app runs you will see the page shown in the following diagram.

The page lists some of the architectural features of Prism, and has a Button that allows you to navigate to a second page. Selecting the Navigate To User Input Page button will take you to the second page of the app, as shown in the following diagram.

This page allows you to enter data into two TextBox controls. If you suspend the app on this page any data will be serialized to disk, and when the app resumes the data will be deserialized and displayed in the TextBox controls. This is accomplished by using the RestorableState attribute for the data retained in the view model, and the SessionStateService class for the data retained in the repository. For more info about the SessionStateService class and the RestorableState attribute see Handling suspend, resume, and activation.

[Top]

Solution structure

The HelloWorldWithContainer Visual Studio solution contains two projects: HelloWorldWithContainer, and Microsoft.Practices.Prism.StoreApps. The HelloWorldWithContainer project uses Visual Studio solution folders to organize the source code into these logical categories:

  • The Assets folder contains the splash screen and logo images.
  • The Common folder contains the styles resource dictionary used in the app.
  • The Services folder contains the IDataRepository interface and its implementing class.
  • The ViewModels folder contains the view model classes that are exposed to the views.
  • The Views folder contains the views that make up the UI for the app's page.

The Microsoft.Practices.Prism.StoreApps library contains reusable classes used by this Quickstart. For more info about this library, see Prism for the Windows Runtime reference. With little or no modification, you can reuse many of the classes from this Quickstart in another app. You can also adapt the organization and ideas that this Quickstart provides.

[Top]

Key classes in the Quickstart

The MvvmAppBase class provides core startup behavior for an MVVM app, with its constructor being the entry point for the app. The App class adds app specific startup behavior to the app.

There are two view classes in the app, MainPage and UserInputPage that bind to the MainPageViewModel and UserInputPageViewModel classes respectively. Each view class derives from the VisualStateAwarePage class, provided by the Microsoft.Practices.Prism.StoreApps library, that provides view management and navigation support. Each view model class derives from the ViewModel base class, provided by the Microsoft.Practices.Prism.StoreApps library, that provides support for navigation and suspend/resume functionality. A static ViewModelLocator object, provided by the Microsoft.Practices.Prism.StoreApps library, is used to manage the instantiation of view models and their association to views. This approach has the advantage that the app has a single class that is responsible for the location and instantiation of view model classes. For more info about how the ViewModelLocator object manages the instantiation of view models and their association to views, see Using the MVVM pattern.

[Top]

Bootstrapping an MVVM app using the MvvmAppBase class and a dependency injection container

The MvvmAppBase class, provided by the Microsoft.Practices.Prism.StoreApps library, is responsible for providing core startup behavior for an MVVM app, and derives from the Application class. The MvvmAppBase class constructor is the entry point for the app. The following diagram shows a conceptual view of how app startup occurs.

The MvvmAppBase class helps bootstrap Windows Store apps with suspension, navigation, and other services.

In order to bootstrap an app using the MvvmAppBase class, the App class must derive from the MvvmAppBase class, as shown in the following code examples.

HelloWorldWithContainer\App.xaml

<prism:MvvmAppBase
    ...
    xmlns:prism="using:Microsoft.Practices.Prism.StoreApps">
   <Application.Resources>
      ...
   </Application.Resources>
</prism:MvvmAppBase>

HelloWorldWithContainer\App.xaml.cs

sealed partial class App : MvvmAppBase

[Top]

Adding app specific startup behavior to the App class

When deriving from the MvvmAppBase class, app specific startup behavior can be added to the App class. A required override in the App class is the OnLaunchApplication method from where you will typically perform your initial navigation to a launch page, or to the appropriate page based on a search, sharing, or secondary tile launch of the app. The following code example shows the OnLaunchApplication method in the App class.

HelloWorldWithContainer\App.xaml.cs

public override Task OnLaunchApplication(LaunchActivatedEventArgs args)
{
   NavigationService.Navigate("Main", null);
   return Task.FromResult<object>(null);
}

This method navigates to the MainPage in the app, when the app launches. "Main" is specified as the logical name of the view that will be navigated to. The default convention specified in the MvvmAppBase class is to append "Page" to the name and look for that page in a .Views child namespace in the project. Alternatively, another convention can be specified by overriding the GetPageType method in the MvvmAppBase class.

Note  The OnLaunchApplication method returns a Task, allowing it to launch a long running operation. If you don't have a long running operation to launch you should return an empty Task.

 

The app uses the Unity dependency injection container to reduce the dependency coupling between objects by providing a facility to instantiate instances of classes and manage their lifetime based on the configuration of the container. An instance of the container is created as a singleton in the App class, as shown in the following code example.

HelloWorldWithContainer\App.xaml.cs

IUnityContainer _container = new UnityContainer();

If you require app specific initialization behavior you should override the OnInitialize method in the App class. For instance, this method should be overridden if you need to initialize services, or set a default factory or default view model resolver for the ViewModelLocator object. The following code example shows the OnInitialize method.

HelloWorldWithContainer\App.xaml.cs

protected override void OnInitialize(IActivatedEventArgs args)
{
    // Register MvvmAppBase services with the container so that view models can take dependencies on them
    _container.RegisterInstance<ISessionStateService>(SessionStateService);
    _container.RegisterInstance<INavigationService>(NavigationService);
    // Register any app specific types with the container
    _container.RegisterType<IDataRepository, DataRepository>();

    // Set a factory for the ViewModelLocator to use the container to construct view models so their 
    // dependencies get injected by the container
    ViewModelLocator.SetDefaultViewModelFactory((viewModelType) => _container.Resolve(viewModelType));
}

This method registers the SessionStateService and NavigationService instances from the MvvmAppBase class with the container as singletons, based on their respective interfaces, so that the view model classes can take dependencies on them. The DataRepository class is then registered with the container, based on its interface. The DataRepository class provides data for display on the MainPage, and methods for reading and writing data input from one of the TextBox controls on the UserInputPage. The OnInitialize method then sets the default view model factory for the ViewModelLocator object so that it uses the container to construct view model instances whose dependencies are injected by the container.

In this Quickstart the ViewModelLocator object uses a convention-based approach to locate and instantiate view models from views. This convention assumes that view models are in the same assembly as the view types, that view models are in a .ViewModels child namespace, that views are in a .Views child namespace, and that view model names correspond with view names and end with "ViewModel". The ViewModelLocator class has an attached property, AutoWireViewModel, that is used to manage the instantiation of view models and their association to views. In the view's XAML this attached property is set to true to indicate that the view model class should be automatically instantiated from the view class.

HelloWorldWithContainer\Views\MainPage.xaml

prism:ViewModelLocator.AutoWireViewModel="true"

The AutoWireViewModel property is a dependency property that is initialized to false, and when its value changes the AutoWireViewModelChanged event handler in the ViewModelLocator class is called to resolve the view model for the view. The following code example shows how this is achieved.

Microsoft.Practices.Prism.StoreApps\ViewModelLocator.cs

private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    FrameworkElement view = d as FrameworkElement;
    if (view == null) return; // Incorrect hookup, do no harm

    // Try mappings first
    object viewModel = GetViewModelForView(view);
    // Fallback to convention based
    if (viewModel == null)
    {
        var viewModelType = defaultViewTypeToViewModelTypeResolver(view.GetType());
        if (viewModelType == null) return;

        // Really need Container or Factories here to deal with injecting dependencies on construction
        viewModel = defaultViewModelFactory(viewModelType);
    }
    view.DataContext = viewModel;
}

The AutoWireViewModelChanged method first attempts to resolve the view model based on mappings that are not present in this Quickstart. If the view model cannot be resolved using this approach, for instance if the mapping wasn't registered, the method falls back to using the convention-based approach outlined earlier to resolve the correct view model type. The view model factory, set by the OnInitialize method in the App class, uses the dependency injection container to construct view model instances whose dependencies are injected by the container. When the view model instances are constructed, dependencies specified by the constructor parameters are resolved by the container and then passed into the view model. This is referred to as constructor injection. This approach removes the need for an object to locate its dependencies or manage their lifetimes, allows swapping of implemented dependencies without affecting the object, and facilitates testability by allowing dependencies to be mocked. Finally, the method sets the DataContext property of the view type to the registered view model instance.

[Top]

Bootstrapping without a dependency injection container

You are not required to use Unity, or any other dependency injection container, when bootstrapping Windows Store apps. The HelloWorld Quickstart demonstrates how to bootstrap a Windows Store app that uses the MVVM pattern by registering factory methods against view types, with a view model locator object.

As previously mentioned, if you require app specific initialization behavior you should override the OnInitialize method in the App class. For instance, this method should be overridden if you need to initialize services, or set a default factory or default view model resolver for the ViewModelLocator object. The following code example shows the OnInitialize method.

HelloWorld\App.xaml.cs

protected override void OnInitialize(IActivatedEventArgs args)
{
    // New up the singleton data repository, and pass it the state service it depends on from the base class
    _dataRepository = new DataRepository(SessionStateService);

    // Register factory methods for the ViewModelLocator for each view model that takes dependencies so that you can pass in the
    // dependent services from the factory method here.
    ViewModelLocator.Register(typeof(MainPage).ToString(), () => new MainPageViewModel(_dataRepository, NavigationService));
    ViewModelLocator.Register(typeof(UserInputPage).ToString(), () => new UserInputPageViewModel(_dataRepository, NavigationService));
}

This method creates a singleton from the DataRepository class, passing in the SessionStateService from the MvvmAppBase class. The DataRepository class provides data for display on the MainPage, and methods for reading and writing data input from one of the TextBox controls on the UserInputPage. The OnInitialize method also registers a factory method for each view type with the static ViewModelLocator object. This ensures that the ViewModelLocator object instantiates the correct view model object for a view type, passing in dependent services to the view model constructor from the factory method.

[Top]