WPF and MVVM tutorial 05, The basic ViewModel.

As we saw in the previous posts, a view model should be an abstract implementation of what the view needs to show about the model. We should implement an observable collection of something, we should implement an INotifyPropertyChanged interface and we should have a collection of RelayCommands.

For these reasons, it simply to understand that we need a basic abstract ViewModel class, just to recycle some code.

image

The Basic View Model.

  1: namespace MVVM.ViewModel {
  2:     public abstract class ViewModel:INotifyPropertyChanged,IDisposable {
  3: 
  4:         INavigationActions navigator;
  5: 
  6:         public ViewModel() {
  7:             navigator = Application.Current as INavigationActions;
  8:             if (navigator != null) {
  9:                 navigator.PropertyChanged += application_PropertyChanged;
 10:             }
 11:         }
 12: 
 13:         void application_PropertyChanged(object sender, PropertyChangedEventArgs e) {
 14:             if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == "View")
 15:                 OnPropertyChanged("View");
 16:         }
 17: 
 18:         public INavigationActions NavigationActions {
 19:             get {
 20:                 return navigator;
 21:             }
 22:             set {
 23:                 if (navigator != value) {
 24:                     SetAction(value);
 25:                     OnPropertyChanged("NavigationActions");
 26:                 }
 27:             }
 28:         }
 29: 
 30:         protected virtual void SetAction(INavigationActions value) {
 31:             if (navigator != null)
 32:                 navigator.PropertyChanged -= application_PropertyChanged;
 33:             navigator = value;
 34:             if (navigator != null)
 35:                 navigator.PropertyChanged += application_PropertyChanged;
 36:         }
 37: 
 38:         #region INotifyPropertyChanged Members
 39:         /// <summary>
 40:         /// Raised when a property has a new value
 41:         /// </summary>
 42:         public event PropertyChangedEventHandler PropertyChanged;
 43:         /// <summary>
 44:         /// Raise the event
 45:         /// </summary>
 46:         /// <param name="propertyName">Property name that has new value</param>
 47:         protected virtual void OnPropertyChanged(string propertyName) {
 48:             PropertyChangedEventHandler handler = this.PropertyChanged;
 49:             if (handler != null) {
 50:                 var e = new PropertyChangedEventArgs(propertyName);
 51:                 handler(this, e);
 52:             }
 53:         }
 54:         #endregion
 55: 
 56:         #region IDisposable Members
 57:         /// <summary>
 58:         /// Implementation of the dispose method
 59:         /// </summary>
 60:         public void Dispose() {
 61:             this.OnDispose();
 62:         }
 63:         /// <summary>
 64:         /// The child class should implement a personal dispose procedure
 65:         /// </summary>
 66:         protected virtual void OnDispose() {
 67:             //do nothing because abstract
 68:         }
 69: 
 70:         #endregion
 71:     }
 72: }

A small summary of our code:

  1. An implementation of the INotifyPropertyChanged that we can use in the concrete views.
  2. An implementation of the IDisposable in order to clean our objects like collections and repositories.
  3. An INavigator interface to implement the navigation of our application. In this case I am using the navigator pattern for composite WPF applications. Beware because this is my implementation for the navigation but it depends on how you design your app (multi-win, tab, MDI).

The INavigator implementation.

The are thousands of ways to implement a navigator engine. The only common purpose in MVVM is that the View Model doesn’t know the View but the View knows the View Model. So in our example which part of the View can interact with the application and doesn’t need to know the View Model? The app.xaml file!

My idea is this one:

image

We will have a simple interface in the ViewModel namespace which will identify the navigation commands we want to execute:

  1: public interface INavigationActions:INotifyPropertyChanged 
  2: {
  3:     void OpenCustomersView();
  4:     void OpenCustomerView();
  5:     void OpenOrdersView();
  6:     void OpenOrderView();
  7:     void CloseCurrentView();
  8:     void CloseApplication();
  9:     bool QueryConfirmation(string title, string message);
 10:     void ShowError(string title, string message);
 11: }

In this way we can call everything from our viewmodel that doesn’t know the view.

Now we just need to implement the view in the Application file in this way:

  1: public partial class App : Application,INavigationActions,INotifyPropertyChanged {
  2:     #region INavigationActions Members
  3: 
  4:     public void OpenCustomersView() {
  5:         CustomersView customersView = new CustomersView();
  6:         customersView.Show();
  7:     }
  8: 
  9:     public void OpenCustomerView() {
 10: 
 11:     }
 12: 
 13:     public void OpenOrdersView() {
 14: 
 15:     }
 16: 
 17:     public void OpenOrderView() {
 18: 
 19:     }
 20: 
 21:     public void CloseCurrentView() {
 22: 
 23:     }
 24: 
 25:     public void CloseApplication() {
 26:         Application.Current.Shutdown();
 27:     }
 28: 
 29:     public bool QueryConfirmation(string title, string message) {
 30:         return MessageBox.Show(title, message, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
 31:     }
 32: 
 33:     public void ShowError(string title, string message) {
 34:         MessageBox.Show(title, message, MessageBoxButton.YesNo, MessageBoxImage.Error);
 35:     }

And there we go!!

Ops I forgot to mention that in the basic View Model we need to handle the INavigation

  1: public ViewModel() {
  2:     navigator = Application.Current as INavigationActions;
  3:     if (navigator != null) {
  4:         navigator.PropertyChanged 
  5:         += application_PropertyChanged;
  6:     }
  7: }
  8: 

For the rest you need to wait the next tutorial!!

Tags: