Skip to content

ViewModelBase

Rico Suter edited this page Jun 10, 2015 · 5 revisions

Base implementation of a view model.

Properties and methods:

  • IsLoading: Boolean property with internal counter. If the property is set to true, a counter gets increased and the return value is only false if the counter is 0. This is useful to have multiple asynchronous running task and the loading state is true until the last task has finished.
  • Initialize method which initializes the view model and should be called only once (usually in the view's constructor). The trick is to put most of the initialize logic in this method and not the constructor, because this method is only called when the application is started but not in the Visual Studio designer. This way the view model can be instantiated in the designer and does not throw an exception when using not initialized objects in the view model's constructor (e.g. services resolved by the ServiceLocator).
  • OnLoaded and OnUnloaded: When correctly wired (using the ViewModelHelper.RegisterViewModel() method), called when the view has been loaded or unloaded.

Implementation of a view model class

The following section shows how to implement a view model for a page and wire the page view with the page view model.

1. Create view model

First create a view model for the view

public class MyPageModel : ViewModelBase
{
    private int _myProperty;

    public MyPageModel()
    {
        DoItCommand = new RelayCommand(DoIt);
        DoItAsyncCommand = new AsyncRelayCommand(DoItAsync);
    }

    public ICommand DoItCommand { get; private set; }
    public ICommand DoItAsyncCommand { get; private set; }

    public int MyProperty 
    {
        get { return _myProperty; }
        set { Set(ref _myProperty, value);
    }

    public override void Initialize()
    {
        _client = ServiceLocator.Default.Resolve<IMyService>();
    }

    private void DoIt()
    {
        // TODO: Add command logic
    }

    private async Task DoItAsync()
    {
        // TODO: Add command logic
    }
}

As you can see, the service is resolved in the Initialize() method and not the constructor. This is needed for proper Visual Studio designer support: On design time, there are no services available and accessing them in the constructor would cause an exception. This way the Initialize() method is only called when the application is really started.

2. Connect the view model to the view

It is recommended to instantiate the view model as resource because this adds code completion/IntelliSense to the XAML editor:

<Paging:MtPage ...>
    <Paging:MtPage.Resources>
        <ViewModels:MyViewModel x:Key="ViewModel" />
    </Paging:MtPage.Resources>
    
    <Grid DataContext="{StaticResource ViewModel}">
        ...
    </Grid>
</Paging:MtPage>

To access the view model in the view's code-behind add a Model property:

public sealed partial class MyPage
{
    public MyPageModel Model 
    { 
        get { return (MyPageModel)Resources["ViewModel"]; }
    }

    public MainPage()
    {
        InitializeComponent();
        RegisterViewModel(Model); 
        // or ViewModelHelper.RegisterViewModel(Model, this); 
    }
}

The RegisterViewModel() method of the MtPage class (calls the corresponding ViewModelHelper method) can also be replaced (beware: page state life cycle methods, e.g. LoadState(), are not triggered this way):

public MainPage()
{
    InitializeComponent();
    Model.Initialize();
    Loaded += (sender, args) => Model.CallOnLoaded();
    Unloaded += (sender, args) => Model.CallOnUnloaded();

See ObservableObject for more information on how to implement property change tracking.

Using the IsLoading property

A long running asynchronous task should be implemented this way:

public class MyPageModel : ViewModelBase
{
    public MyPageModel()
    {
        DoItAsyncCommand = new AsyncRelayCommand(DoItAsync);
    }

    public ICommand DoItAsyncCommand { get; private set; }

    private async Task DoItAsync()
    {
        IsLoading = true;
        try
        {
            // TODO: Add your task logic
            IsLoading = false;
        }
        catch (Exception exception)
        {
            IsLoading = false;
            // TODO: Show exception
        }    
    }
}

The IsLoading property should be bound to a ProgressIndicator, ProgressRing or the Windows Phone system tray's progress bar (easiest way is by using the ViewModelHelper.BindViewModelToStatusBarProgress method).

Source code

Clone this wiki locally