When you are developing Silverlight applications following the MVVM pattern, you will eventually run into the fact that you’ll have to create a base class for your viewmodel classes. This post will show how to create a useful base class for your viewmodel classes and also how to create a base class for your view classes, which a lot of developers don’t do. In the end, I will show how to wire everything up.

The Application

To demonstrate my base classes, I will create an application which shows a login view at start up. It’s quite simple:

image

But, this application has everything I need to explain my base classes:

  • Data validation.
  • A button that needs to be enabled and disabled.
  • A view.
  • A viewmodel.

The folder structure of the finished project will look like this:

image

 

A base class for your ViewModel

A useful base class for your viewmodel classes should at least support the following stuff:

  • Change notification: In other words, it should implement INotifyPropertyChanged.
  • The most flexible data validation mechanism: It should implement INotifyDataErrorInfo.

Change notification is easily implemented when you are using the PRISM framework. You can let your viewmodel classes derive from PRISM’s NotificationObject class. This class has a very convenient method: RaisePropertyChanged<T>, which you can call like this when you need to notify the UI:

   1: RaisePropertyChanged<string>(() => UserName);

Thus this is a very safe implementation of the INotifyPropertyChanged interface.

PRISM doesn’t define a base class which implements the INotifyDataErrorInfo interface, but it provides another class we can use: The ErrorsContainer<T> class. The following code snippet will show my complete base class for a viewmodel:

   1: using System;

   2: using System.ComponentModel;

   3: using Microsoft.Practices.Prism.ViewModel;

   4: using System.Linq.Expressions;

   5: using System.Collections.Generic;

   6:  

   7: namespace SilverlightApplication4

   8: {

   9:     public class ValidatingViewModel : 

  10:         NotificationObject, INotifyDataErrorInfo

  11:     {

  12:        //Begin INotifyDataErrorInfo members

  13:         public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

  14:  

  15:         public System.Collections.IEnumerable GetErrors(string propertyName)

  16:         {

  17:             return _container.GetErrors(propertyName);

  18:         }

  19:     

  20:  

  21:         public bool HasErrors

  22:         {

  23:             get { return _container.HasErrors;}

  24:         }

  25:         //End INotifyDataErrorInfo members

  26:  

  27:         //ValidationContainer which can we delegate most of the validation to

  28:         private ErrorsContainer<string> _container;

  29:  

  30:         public ValidatingViewModel()

  31:         {

  32:             _container = new ErrorsContainer<string>(OnErrorsChanged);

  33:             

  34:         }

  35:  

  36:         protected virtual void SetErrors<TProp>(Expression<Func<TProp>> propExpression, IEnumerable<string> errors)

  37:         {

  38:             _container.SetErrors(propExpression, errors);

  39:         }

  40:         protected virtual void ClearErrors<TProp>(Expression<Func<TProp>> propExpression)

  41:         {

  42:             _container.ClearErrors(propExpression);

  43:         }

  44:  

  45:         

  46:         protected virtual void OnErrorsChanged(string propertyName)

  47:         {

  48:             EventHandler<DataErrorsChangedEventArgs> temp = ErrorsChanged;

  49:             if (temp != null)

  50:             {

  51:                 temp(this,new DataErrorsChangedEventArgs(propertyName));

  52:             }

  53:         }

  54:     }

  55: }

You can see on line 9 that it derives from PRISM’s NotificationObject, so it already has support for change notification. On lines 11-25 you can see the mandatory members you have to implement because of the INotifyDataErrorInfo interface. You can also see that I delegate most of the work of implementing these members to the _container variable, which is defined on line 28 and instantiated on line 32. When instantiating the ErrorsContainer, you have to provide a delegate which it can call when the errors it holds change. The method which is called by the ErrorsContainer is defined on line 46. It’s protected virtual because derived classes may override it, but you typically don’t need to call this method yourself. You can see that when the ErrorsContainer calls this method, I fire the ErrorsChanged event, which is defined by the INotifyDataErrorInfo interface. This event will let the UI know that it needs to go to the InValid or Valid state. On lines 36 and 40 I have defined two safe methods which derived classes can use to set or clear validation errors for a property. This way, the fact that I use PRISM’s ErrorsContainer, is completely shielded from any derived classes.

To show how to use this base class, the following code shows the viewmodel for the login view:

   1: using System.Windows.Input;

   2: using Interaction;

   3: using Microsoft.Practices.Prism.Commands;

   4: using SilverlightApplication4;

   5: using ViewModels;

   6:  

   7: namespace PRISMLoginApplication.ViewModels

   8: {

   9:     public class LoginViewViewModel : ValidatingViewModel,ILoginViewViewModel

  10:     {

  11:         private string _userName;

  12:         private string _password;

  13:         private DelegateCommand _loginCommand;

  14:         private InteractionRequest<MessageWindowViewModel> _dialogRequest;

  15:  

  16:         public string UserName

  17:         {

  18:             get

  19:             {

  20:                 return _userName;

  21:             }

  22:             set

  23:             {

  24:                 _userName = value;

  25:                 if (_userName == null || _userName == "")

  26:                 {

  27:                     SetErrors<string>(() => UserName, new string[] { "Username is mandatory!" });

  28:                     

  29:                 }

  30:                 else

  31:                 {

  32:                     ClearErrors<string>(() => UserName);

  33:                 }

  34:                 EnableOrDisableLoginButton();

  35:             }

  36:         }

  37:         public string Password

  38:         {

  39:             get

  40:             {

  41:                 return _password;

  42:             }

  43:             set

  44:             {

  45:                 _password = value;

  46:                 if (_password == null || _password == "")

  47:                 {

  48:                     SetErrors<string>(() => Password, new string[] { "Password is mandatory!" });

  49:  

  50:                 }

  51:                 else

  52:                 {

  53:                     ClearErrors<string>(() => Password);

  54:                 }

  55:                 EnableOrDisableLoginButton();

  56:             }

  57:         }

  58:  

  59:         public IInteractionRequest DialogRequest

  60:         {

  61:             get

  62:             {

  63:                 return _dialogRequest;

  64:             }

  65:            

  66:         }

  67:         public ICommand LoginCommand

  68:         {

  69:             get

  70:             {

  71:                 return _loginCommand;

  72:             }

  73:         }

  74:  

  75:         public LoginViewViewModel()

  76:         {

  77:             _dialogRequest = new InteractionRequest<MessageWindowViewModel>();

  78:             _loginCommand = new DelegateCommand(ShowDialog, () => !HasErrors);

  79:             UserName = null;

  80:             Password = null;

  81:         }

  82:  

  83:         private void ShowDialog()

  84:         {

  85:            _dialogRequest.Raise(new MessageWindowViewModel("You are succesfully logged in!","Succes"));

  86:         }

  87:         private void EnableOrDisableLoginButton()

  88:         {

  89:             _loginCommand.RaiseCanExecuteChanged();

  90:         }

  91:  

  92:     }

  93: }

On lines 16-57 you can see that it defines two properties to hold the entered username and password. You can also see that it checks whether the username and password aren’t empty. If they are, the SetErrors method provided by my base class is called to indicate a validation error. On lines 67-73  a LoginCommand is defined, which is used by the login button in the view. This command is instantiated on line 78, I use PRISM’s DelegateCommand, which has support for delegates and for notifying the UI whether the bound element should be enabled or disabled. That’s why in  the setters of the UserName and Password properties the EnableOrDisableLoginButton method is called. This method is defined on line 87 and it let’s the bound login button know that something has changed and it might need to enable or disable itself. Don’t worry about the DialogRequest and InteractionRequest stuff, it’s just a way of showing a child window in a MVVM way. You can see on line 83 that when the LoginCommand is called, I simply show a ChildWindow which notifies the user that he / she has been logged in.

A base class for your View

While creating a base class for your ViewModel classes is quite common, a base class for your View which handles some MVVM stuff isn’t. Yet I found myself writing the same stuff in my views over and over, so a base class would be beneficial. Here’s the code:

   1: using System;

   2: using System.Diagnostics;

   3: using System.Windows;

   4: using System.Windows.Controls;

   5:  

   6: namespace PRISMLoginApplication.Views

   7: {

   8:     public class UserControlWithViewModelBase : UserControl

   9:     {

  10:  

  11:         public UserControlWithViewModelBase(object viewModel)

  12:         {

  13:             if (viewModel == null)

  14:             {

  15:                 throw new ArgumentNullException("viewModel can't be null, use default constructor instead.");

  16:             }

  17:             DataContext = viewModel;

  18:             if (DataContext is IDisposable)

  19:             {

  20:                 Unloaded += new RoutedEventHandler(DisposeViewModel);

  21:             }

  22:             

  23:         }

  24:  

  25:         public UserControlWithViewModelBase()

  26:         {

  27:             //Only here for designer support......

  28:         }

  29:  

  30:         private void DisposeViewModel(object sender, RoutedEventArgs e)

  31:         {

  32:             Debug.Assert(DataContext is IDisposable);

  33:  

  34:             ((IDisposable)DataContext).Dispose();

  35:             DataContext = null;

  36:         }

  37:      

  38:     }

  39: }

It isn’t as “spectacular” as the ViewModel base class, but it still provides some useful things:

  • Lines 11-21: A constructor which accepts a ViewModel object. This sets the DataContext property, which is common when a view has a ViewModel. Next to that it checks whether the viewmodel implements IDisposable. I frequently found myself wanted to do some clean up in my viewmodel objects when the related view goes out of scope. Instead of using a DisposeCommand on all my viewmodel objects and binding each view to this, the code above seemed the right thing to do. It uses a familiar .Net construct (IDisposable) which a viewmodel can simply implement without worrying about the rest. I know that code in the code behind of a view is frowned upon by many MVVM purists because it’s difficult to test, but the code above makes life so much easier. Maybe you are wondering why I haven’t made this base class generic for the type of the viewmodel, but then it couldn’t be instantiated in XAML.
  • Lines 25-28: A default constructor. It’s only there so that you won’t lose designer support in Visual Studio and Expression Blend. I probably know what you are thinking at this point: “But the view is instantiated in XAML right? Then it needs to have a default constructor!”. You are completely right, but in PRISM views are often instantiated in another way, namely using the Dependency Injection container of your choice. It then get’s added to a so called “Region”, which defines where your view will show up in the UI.

Let’s take a look at both the code and the XAML of the LoginView:

   1: <views:UserControlWithViewModelBase x:Class="PRISMLoginApplication.LoginView"

   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   6:     mc:Ignorable="d"

   7:     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

   8:     xmlns:mi="clr-namespace:Interaction"

   9:     xmlns:windows="clr-namespace:SilverlightApplication4"

  10:     xmlns:views="clr-namespace:PRISMLoginApplication.Views"

  11:     d:DesignHeight="300" d:DesignWidth="400">

  12:     <i:Interaction.Behaviors>

  13:         <mi:ShowDialogBehavior NotificationSource="{Binding DialogRequest}">

  14:             <mi:ShowDialogBehavior.Window>

  15:                 <windows:MessageWindow/>

  16:             </mi:ShowDialogBehavior.Window>

  17:         </mi:ShowDialogBehavior>

  18:     </i:Interaction.Behaviors>

  19:  

  20:     <Grid x:Name="LayoutRoot" Background="White">

  21:         <Grid.RowDefinitions>

  22:             <RowDefinition Height="Auto"/>

  23:             <RowDefinition Height="Auto"/>

  24:             <RowDefinition Height="Auto"/>

  25:             <RowDefinition Height="Auto"/>

  26:         </Grid.RowDefinitions>

  27:         <Grid.ColumnDefinitions>

  28:             <ColumnDefinition Width="*"/>

  29:             <ColumnDefinition Width="*"/>

  30:         </Grid.ColumnDefinitions>

  31:  

  32:         <TextBlock Text="Username: " Grid.Row="1" Grid.Column="0"  HorizontalAlignment="Right" Margin="4,4,4,0"/>

  33:         <TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Width="100" Margin="0,4,4,0" Text="{Binding UserName,ValidatesOnNotifyDataErrors=True,NotifyOnValidationError=True,Mode=TwoWay}"/>

  34:         <TextBlock Text="Password: " Grid.Row="2" HorizontalAlignment="Right" Margin="0,4,3,0"/>

  35:         <TextBox Grid.Row="2" Margin="0,4" Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Password,ValidatesOnNotifyDataErrors=True,NotifyOnValidationError=True,Mode=TwoWay}" Name="_txtPassword"/>

  36:         <Button Content="Log in" Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left"  VerticalAlignment="Top" Command="{Binding LoginCommand}" />

  37:  

  38:     </Grid>

  39: </views:UserControlWithViewModelBase>

   1: using PRISMLoginApplication.ViewModels;

   2: using PRISMLoginApplication.Views;

   3:  

   4: namespace PRISMLoginApplication

   5: {

   6:     public partial class LoginView : UserControlWithViewModelBase

   7:     {

   8:         public LoginView(ILoginViewViewModel viewModel) : base(viewModel)

   9:         {

  10:             InitializeComponent();

  11:         }

  12:     }

  13: }

The most important thing in the XAML, is that I’ve changed the root element on lines 1 and 39 to my own base class. This is mandatory as the XAML and the code behind can’t define different base classes, since they are both part of the same partial class. In the code behind you can see on line 8 that I’ve neatly abstracted the viewmodel for this view in an interface, which is of course implemented by the viewmodel shown earlier. The rest is taken care of by calling the constructor of my base class.

Wiring everything up

So we have a view with a constructor which accepts a viewmodel and we have a viewmodel with all the logic. How should we associate these two with each other? In PRISM you can use regions for this. Let’s take a look at the XAML of the MainPage:

   1: <UserControl x:Class="PRISMLoginApplication.Views.MainPage"

   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   6:     mc:Ignorable="d"

   7:     d:DesignHeight="300" d:DesignWidth="400"

   8:     xmlns:regions="clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism">

   9:     

  10:     <Grid x:Name="LayoutRoot" Background="White">

  11:         <!--Prism region placeholder-->

  12:         <ContentControl regions:RegionManager.RegionName="mainRegion"/>

  13:     </Grid>

  14: </UserControl>

The ContentControl is merely a placeholder which is registered under the name “mainRegion”. My LoginView will be added to this ContentControl. Initializing your Silverlight / PRISM application is the task of the so called “Bootstrapper”. Let’s take a look at my Bootstrapper class:

   1: using System.Windows;

   2: using Microsoft.Practices.Prism.Regions;

   3: using Microsoft.Practices.Prism.UnityExtensions;

   4: using Microsoft.Practices.Unity;

   5: using PRISMLoginApplication.Util;

   6: using PRISMLoginApplication.ViewModels;

   7: using PRISMLoginApplication.Views;

   8:  

   9: namespace PRISMLoginApplication.Bootstrapping

  10: {

  11:     public class LogInApplicationBootStrapper : UnityBootstrapper

  12:     {

  13:         protected override void ConfigureContainer()

  14:         {

  15:             base.ConfigureContainer();

  16:             Container.RegisterType<object, LoginView>(ViewNames.LOGIN_VIEW);

  17:             Container.RegisterType<ILoginViewViewModel, LoginViewViewModel>();

  18:         }

  19:  

  20:         protected override DependencyObject CreateShell()

  21:         {

  22:             return new MainPage();

  23:         }

  24:         protected override void InitializeShell()

  25:         {

  26:             Application.Current.RootVisual = (UIElement)Shell;

  27:  

  28:             //Instruct PRISM to load the LoginView in the main region.

  29:             Container.Resolve<IRegionManager>().RequestNavigate(

  30:                 RegionNames.MAIN_REGION, ViewNames.LOGIN_VIEW);

  31:         }

  32:     }

  33: }

On line 11 you can see that my bootstrapper is derived from the UnityBootstrapper class provided by PRISM. This is a key decision, dependent on the dependency injection container you use you’ll have to use a specific derived class from the abstract Bootstrapper class. PRISM 4 supports two DI containers out of the box, namely Unity and MEF. I’ve found MEF to be not really suitable as a DI container (it misses a few key features) so I usually pick Unity. You can of course use your own DI container, Ninject for example, but you’ll manually have to create a Bootstrapper derived class to wire it into the rest of PRISM.

On lines 13-18, it configures the DI container. This is the place to register concrete types with their interfaces, so that they can be injected later in your application. On line 16 I register the LoginView as type object. In PRISM 4 all view objects must be registered as an object and you must provide a specific name. On line 17 I register the LoginViewViewModel using the ILoginViewViewModel. Here it pays off to abstract your viewmodels with an interface. Don’t forget to call the base implementation of this method, as PRISM registers a couple of required objects as well.

On lines 20-23 you can see the overridden CreateShell() method. This method is responsible for (you’ve guessed it) creating the shell. The shell is usually just the MainPage which defines the main layout of your application. This Shell is usually split up in a couple of regions. My shell is the MainPage shown earlier, so I can just instantiate and return it here.

Finally on lines 24-31 you can see the overridden InitializeShell() method. This method is responsible for putting your application in it’s initial state. You can see that I set the Shell (my MainPage) as the root visual of my application. Remember the region I defined in the XAML of my MainPage with the name “mainRegion”? On line 29 I ask the DI container for an object of type IRegionManager (this type was registered by calling the base implementation of ConfigureContainer()). You can use this IRegionManager object to load a view in a region. On line 16 I’ve registered my LoginView supplying a name, by calling the RequestNavigate() method here and supplying a region name (“mainRegion”) and the same name I used on line 16, I instruct the IRegionManager to load the LoginView in the region shown earlier.

The beauty of this is that the IRegionManager will ask Unity for an instance with the specified name. This instance will be of course of type LoginView as I registered this type with that name. Unity will create an instance of LoginView and the LoginView will get it’s ILoginViewViewModel constructor argument automatically injected by Unity, because I registered the concrete type LoginViewViewModel using this interface on line 17. If the viewmodel also had interface arguments in it’s constructor, these will also be automatically resolved by Unity. This way you’ll create a nice loose coupling between all the components of your application based on interfaces. The LoginView will of course pass it’s constructor argument to the constructor of it’s base class, which is my UserControlWithViewModelBase type.

There is only one thing left to do, that’s telling the Bootstrapper to actually run. This is usually done in the Startup event of your Silverlight application:

   1: using System;

   2: using System.Windows;

   3: using PRISMLoginApplication.Bootstrapping;

   4:  

   5: namespace PRISMLoginApplication

   6: {

   7:     public partial class App : Application

   8:     {

   9:  

  10:         public App()

  11:         {

  12:             this.Startup += this.Application_Startup;

  13:             this.Exit += this.Application_Exit;

  14:             this.UnhandledException += this.Application_UnhandledException;

  15:  

  16:             InitializeComponent();

  17:         }

  18:  

  19:         private void Application_Startup(object sender, StartupEventArgs e)

  20:         {

  21:             new LogInApplicationBootStrapper().Run();

  22:         }

  23:  

  24:         private void Application_Exit(object sender, EventArgs e)

  25:         {

  26:  

  27:         }

  28:  

  29:         private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)

  30:         {

  31:             // If the app is running outside of the debugger then report the exception using

  32:             // the browser's exception mechanism. On IE this will display it a yellow alert 

  33:             // icon in the status bar and Firefox will display a script error.

  34:             if (!System.Diagnostics.Debugger.IsAttached)

  35:             {

  36:  

  37:                 // NOTE: This will allow the application to continue running after an exception has been thrown

  38:                 // but not handled. 

  39:                 // For production applications this error handling should be replaced with something that will 

  40:                 // report the error to the website and stop the application.

  41:                 e.Handled = true;

  42:                 Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });

  43:             }

  44:         }

  45:  

  46:         private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)

  47:         {

  48:             try

  49:             {

  50:                 string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;

  51:                 errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");

  52:  

  53:                 System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");

  54:             }

  55:             catch (Exception)

  56:             {

  57:             }

  58:         }

  59:     }

  60: }

You can see it in the snippet above on lines 19-22. Other than that and removing the setting of the RootVisual property, I haven’t changed this class.

Concluding

I first wanted to make this post only about the base classes, but I think the extra background information about PRISM, will come in handy for a lot of people. Of you liked this post, make sure that you visit my session about Silverlight and PRISM on the Dev Days 2011 in the Netherlands, on 28 and 29 April. You can still vote for my sessions here, in the left most column. You can find the whole working application here. See you at the Dev Days 2011!

This entry was posted in Various and tagged , , by Alex van Beek. Bookmark the permalink.
Alex van Beek

About Alex van Beek

Alex van Beek is oud-medewerker van Info Support. Zijn blogposts zijn dan ook gesloten voor comments.

4 thoughts on “Silverlight 4: Creating useful base classes for your views and viewmodels with PRISM 4

  1. Just how many Silverlight apps have you developed? Wow – very nice and clean implementation of the VM in Silverlight that also Validates!

    Will take a very close review of your base classes, and PRISM. I was told that PRISM was very complex, and not much payback – but from what I have seen – it looks very interesting.

  2. Hi Richard!

    Thanks again for your kind words! It’s a common misunderstanding that Prism is very complex or heavyweight. If you want to find out more about Prism you can take a look at my DevDays’11 session at: http://tinyurl.com/6fg98bp .It’s in dutch, but your name seems dutch to me :-)

  3. how about generic base page???
    public class ViewBase : Page where T : ViewModelBase
    {

    }