Share via


Exercise 1: Introducing MVVM and Customizing the UI by UI Role

This exercise will add a ViewModel to the application’s home screen (view) while introducing MVVM practices. Authentication features will be modified to check against the sample database’s user and roles tables.

Setup Authentication

  1. Open the SlEventManager solution in Visual Studio 2010 (copy the Starting Point solution).
    Note:
    This lab uses the aspnet_ tables and ASP.NET authentication. You can see the tables in the application’s database by following the steps below.

    Expand the App_Data folder and double click SlEventManager.mdf. This will show the Server Explorer panel, and will expand the Data Connections entry for your project’s database. Expand the Tables item for this database, and you’ll see various tables whose names begin with aspnet_.

    Right click on aspnet_Users and select Show Table Data, and it will show the contents of the table. You should see two rows, representing two user accounts (ian and administrator) that are present in the database provided for this example project.

    These aspnet_ tables are standard ones recognized by various ASP.NET features for working with users, role-based security, and profiles. It’s common for these tables to end up in an auto-generated SQL Express database called ASPNETDB.MDF—Visual Studio can arrange for this database to be generated for you. (And in fact it will hide it by default, so it’s not always obvious that it’s done it.) But we’re not going to use a generated database. We’re going to use the tables already present in the SlEventManager.mdf database, for two reasons. First, the database is preloaded with some user accounts and roles that you’ll be using in this lab. Second, keeping everything in one database is simpler for deployment reasons.

    The one downside is that we need to configure the ASP.NET web application to use our database. By default it will go ahead and generate one for you. In fact, if you’ve already been playing with the login controls that the Silverlight Business Application template adds to new applications, it’s possible that this has already happened. Select the App_Data folder and then click the Show All Files button at the top of Solution Explorer, and if you see an ASPNETDB.MDF, that means the automatic generation has already happened. Not that this is a problem. We’ll switch to the example database now.
  2. Open the SlEventManager.Web project’s web.config file.
  3. Find the <system.web> element.
  4. Delete the <roleManager enabled="true" /> element.
  5. Delete the <profile> element and all its children.
  6. Inside the <system.web> element, add the following:

    XML

    <membership defaultProvider="SlEventManagerMembershipProvider" userIsOnlineTimeWindow="15"> <providers> <clear /> <add name="SlEventManagerMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="SlEventManagerDb" applicationName="/SlEventManager" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" requiresUniqueEmail="true" passwordFormat="Hashed" /> </providers> </membership> <roleManager enabled="true" defaultProvider="SlEventManagerRoleProvider"> <providers> <clear /> <add name="SlEventManagerRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="SlEventManagerDb" applicationName="/SlEventManager" /> </providers> </roleManager> <profile enabled="true" defaultProvider="SlEventManagerProfileProvider"> <providers> <clear /> <add name="SlEventManagerProfileProvider" connectionStringName="SlEventManagerDb" applicationName="/SlEventManager" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </providers> <properties> <add name="FriendlyName" /> </properties> </profile>
    Note:
    The editorial process has split some of the lines to fit them on the page. If you copy and paste this in you should be fine, but don’t try to reproduce the line breaks in the middle of the type attributes.

    This explicitly configures Membership, Role, and Profile providers. Silverlight Business Applications use all three of these. (The Membership feature is the one that deals with user logins, by the way.)

    This explicit configuration lets us do two things. First, it lets us specify an applicationName; this scopes the user accounts, roles, and profiles to our application, and it’s considered good practice always to set this. Second, it lets us specify the database connection string to be used for each of these features, which is how ASP.NET will know to use our database. All three are using the SlEventManagerDb connection string.
  7. Find the <connectionStrings> section of the web.config. Add another entry for the authentication database:

    XML

    <add name="SlEventManagerDb" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\SlEventManager.mdf;Integrated Security=True;User Instance=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
    Note:
    The connectionString should not have any line breaks in your web.config.
  8. Run the application.
  9. Click on the login link at the top right of the page. A login UI will appear.
  10. Log in with a username of administrator, and a password of P@ssw0rd (that’s a number zero after the w).
    Note:
    The login should succeed, verifying that your application is successfully using the aspnet_Users table (and associated tables such as aspnet_Membership) to for managing logins. (Feel free to verify that it’s not simply allowing any credentials, by trying to log in with a non-existent account, or the wrong password. The login should fail when you try that.)
  11. Close the application.
  12. Back in Visual Studio, inspect the data in the aspnet_Roles table.
    Note:
    This contains two rows, defining two roles: Event Administrators and Registered Users. (In the database supplied, the administrator account belongs to both roles, while the ian account belongs only to the second. Role membership is defined in the aspnet_UsersInRoles table.) The Registered Users role is one you’ll typically get by default in this sort of application, because the Silverlight Business Application template includes code to generate it.

    The AddUser method in the SlEventManager.Web project’s UserRegistrationService.cs(C#) or UserRegistrationService.vb(VB) file uses the ASP.NET Roles class to create the Registered Users role if it doesn’t already exist, and automatically adds newly-created users to that role.
  13. Run the application
  14. If the application still shows you as being logged in, log out.
  15. Use the login UI to create a new user account (all the code for registering as a new user is built into the template).
    Note:
    Once you’ve done this you should see new entries in the aspnet_Users, aspnet_Membership, and aspnet_UsersInRoles tables, representing your new account, and its membership of the Registered Users role.

Create Registration Buttons

  1. Go to Home.xaml in the SlEventManager project’s Views folder.
  2. Add the following StackPanel after the existing one:

    XAML

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button x:Name="registerForEventButton" Content="Register" /> <Button x:Name="unregisterForEventButton" Content="Unregister" /> </StackPanel>
    Note:
    Right now both panels will be visible at once, but we’ll fix that with a ViewModel that will provide bindable properties to control the visibility of these two panels.
  3. Add a new class to your SlEventManager project (with Shift+Alt+C or the Add→Class… context menu in the Solution Explorer) called ViewModelBase.
    Note:
    A ViewModel needs to notify Silverlight whenever its properties change so that and data binding expressions that connect to the ViewModel know to update the target UI element. The usual way to do this is to implement INotifyPropertyChanged. So it’s common to define a base class to provide a common implementation of this interface for all ViewModels.
  4. Add this using declaration:

    C#

    using System.ComponentModel;

    Visual Basic

    Imports System.ComponentModel

  5. Implement the class as shown here:

    C#

    public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }

    Visual Basic

    Public Class ViewModelBase Implements INotifyPropertyChanged Public Event PropertyChanged As PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Protected Sub OnPropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class

Create the ViewModel

  1. Add a new folder to the SlEventManager project called ViewModels.
    Note:
    ViewModels are typically designed either for a particular view (i.e. a particular XAML file and its code behind) or some part of the view (e.g., an individual item in a list inside a view). You will be defining a ViewModel for the Home.xaml view.
  2. Add a class called HomeViewModel in the ViewModels folder.
  3. Make this new class derive from the ViewModelBase class you created.
  4. The ViewModel needs to determine the visibility of the administrator buttons, so add the following property definition:

    C#

    private Visibility _adminButtonsVisibility; public Visibility AdminButtonsVisibility { get { return _adminButtonsVisibility; } set { if (_adminButtonsVisibility != value) { _adminButtonsVisibility = value; OnPropertyChanged("AdminButtonsVisibility"); } } }

    Visual Basic

    Private _adminButtonsVisibility As Visibility Public Property AdminButtonsVisibility() As Visibility Get Return _adminButtonsVisibility End Get Set(ByVal value As Visibility) If _adminButtonsVisibility <> value Then _adminButtonsVisibility = value OnPropertyChanged("AdminButtonsVisibility") End If End Set End Property
    Note:
    This is the typical boilerplate implementation for a property that raises change notifications. This particular property’s type is Visibility—that’s because we’re going bind this to one of the StackPanels’ Visibility properties.
  5. Add another similar property called AttendeeButtonsVisibility.

    C#

    private Visibility _attendeeButtonsVisibility; public Visibility AttendeeButtonsVisibility { get { return _attendeeButtonsVisibility; } set { if (_attendeeButtonsVisibility != value) { _attendeeButtonsVisibility = value; OnPropertyChanged("AttendeeButtonsVisibility"); } } }

    Visual Basic

    Private _attendeeButtonsVisibility As Visibility Public Property AttendeeButtonsVisibility() As Visibility Get Return _attendeeButtonsVisibility End Get Set(ByVal value As Visibility) If _attendeeButtonsVisibility <> value Then _attendeeButtonsVisibility = value OnPropertyChanged("AttendeeButtonsVisibility") End If End Set End Property
  6. Add the following code to have the ViewModel set these properties :

    C#

    private void UpdateForUserRole() { bool isLoggedIn = WebContext.Current.User.IsAuthenticated; bool isAdmin = isLoggedIn && WebContext.Current.User.IsInRole("Event Administrators"); AdminButtonsVisibility = isAdmin ? Visibility.Visible : Visibility.Collapsed; AttendeeButtonsVisibility = (isLoggedIn && !isAdmin) ? Visibility.Visible : Visibility.Collapsed; }

    Visual Basic

    Private Sub UpdateForUserRole() Dim isLoggedIn As Boolean = WebContext.Current.User.IsAuthenticated Dim isAdmin As Boolean = isLoggedIn AndAlso WebContext.Current.User.IsInRole("Event Administrators") AdminButtonsVisibility = If(isAdmin, Visibility.Visible, Visibility.Collapsed) AttendeeButtonsVisibility = If((isLoggedIn AndAlso (Not isAdmin)), Visibility.Visible, Visibility.Collapsed) End Sub
    Note:
    This uses the WebContext class, which is provided by WCF RIA Services. This has built-in support for the concept of user authentication and roles. This relies on the AuthenticationService in the SlWebManager.Web project’s Services folder.
  7. We need to call the UpdateForUserRole method when the UI first loads, but also whenever the user logs in or out. So add the following constructor to your ViewModel:

    C#

    public HomeViewModel() { WebContext.Current.Authentication.LoggedIn += (s, e) => UpdateForUserRole(); WebContext.Current.Authentication.LoggedOut += (s, e) => UpdateForUserRole(); UpdateForUserRole(); }

    Visual Basic

    Public Sub New() AddHandler WebContext.Current.Authentication.LoggedIn, Sub(s, e) UpdateForUserRole() AddHandler WebContext.Current.Authentication.LoggedOut, Sub(s, e) UpdateForUserRole() UpdateForUserRole() End Sub
    Note:
    The WebContext.Current.Authentication object offers these events to notify you when the user logs in or out, enabling us to know when we should update the ViewModel’s state.
  8. Create an instance of this ViewModel and bind the view to it by adding the field declaration and initializer in the Home.xaml.cs(C#) or Home.xaml.vb(VB) codebehind:

    C#

    HomeViewModel _viewModel = new HomeViewModel();

    Visual Basic

    Dim _viewModel As New HomeViewModel()
  9. Add this using statement:

    C#

    using SlEventManager.ViewModels;

    Visual Basic

    Imports SlEventManager.ViewModels
  10. In the view’s constructor, assign this into the DataContext for the view:

    C#

    this.DataContext = _viewModel;

    Visual Basic

    Me.DataContext = _viewModel
  11. In Home.xaml, go to the original StackPanel, the one containing the buttons for editing and creating events.
  12. Add the following attribute to the 2 StackPanels to cause the panel’s visibility to be controlled by the ViewModel.

    XAML

    Visibility="{Binding Path=AdminButtonsVisibility}"
  13. Add a similar attribute to the other StackPanel, binding its Visibility to the AttendeeButtonsVisibility property.
  14. Run the application.
    Note:
    Initially you will most likely not be logged in (unless you checked the checkbox to remain logged in earlier.) When not logged on, you will see no buttons at all. But if you log in as a normal user (e.g. ian, password P@ssw0rd), you should see the Register and Unregister buttons appear. If you log back out these will disappear again. If you log in as administrator (P@ssw0rd) you will see the buttons for editing and creating events.