Walkthrough: Using Authentication Service with Silverlight Business Application

[ This document was written for WCF Services Version 1 Service Pack 2 and might not be up to date Please see Release Notes or Changelog for a list of changes since WCF RIA Services ]

The Silverlight Business Application template creates a solution that automatically enables authentication (with Forms Authentication for the authentication mode), roles, and profiles. The solution includes data forms for logging in existing users and registering new users. You can use these features without writing any additional code. You can customize the solution by defining roles and profile properties.

In this walkthrough, you will learn how to use authentication, roles, and profiles in a Silverlight Business Application. You will restrict access to certain domain operations based on the user's credentials and customize the user interface based on user preferences. You will use the ASP.NET Web Site Administration Tool for managing roles and users in the site.

Prerequisites

This and the other walkthroughs presented in the Open Ria Services documentation require several prerequisite programs, such as Visual Studio and the Silverlight Developer Runtime and SDK, be installed and configured properly, in addition to Open Ria Services and the Open Ria Services Toolkit. They also require installing and configuring SQL Server 2008 R2 Express with Advanced Services and installing the AdventureWorks OLTP and LT database.

Detailed instructions for the satisfaction of each of these prerequisites are provided by the topics within the Prerequisites for Open Ria Services node. Follow the instructions provided there before proceeding with this walkthrough to ensure that you encounter as few problems as possible when working through this Open Ria Services walkthroughs.

Creating users and roles

You can use the provided features in a Silverlight Business Application to quickly implement authentication. In the following section, you will use the ASP.NET Configuration tool to create a user and role, and then log in as that user. You will register a new user through the registration form that is provided in the Silverlight Business Application.

To create site, roles and users

  1. In Visual Studio, select File, New, and then Project.

    The New Project dialog box appears.

  2. Select the Silverlight project type.

  3. Select the Silverlight Business Application template and name the application ExampleBusinessApplication.

  4. Click OK.

    Notice the project structure that is created. The Silverlight client project includes Silverlight pages in the Views folder. These pages enable logging in users and registering new users.

  5. To open the ASP.NET Web Site Administration Tool, first select the server project (ExampleBusinessApplication.Web) in Solution Explorer. open the ASP.NET Configuration tool.

  6. On the Project menu, select ASP.NET Configuration.

    If you do not see the ASP.NET Configuration option in the Project menu, it may be because you have selected the client project.

  7. Select the Security tab in the ASP.NET Web Site Administration Tool.

  8. In the Roles section, click the Create or Mange roles link.

  9. Add a new role named Managers and select the Add Role button.

  10. In the lower-right corner, click the Back button.

  11. In the Users section, click the Create user link.

  12. Create a new user with the following values and select the Managers role check box.

    User Name: CustomerManager

    Password: P@ssword

    E-mail: someone@example.com

    Security Question: Favorite color?

    Security Answer: Blue

    Managers role: selected

  13. Click the Create User button.

  14. Close the ASP.NET Web Site Administration Tool.

  15. Run the solution.

    The Home page for the application appears in a Web browser.

  16. In the upper-right corner of the page, click the login link.

    A Login dialog box appears.

  17. Enter CustomerManager for the user name and P@ssword for the password, and click OK button.

    You are now logged in as that user. Notice the text "Welcome CustomerManager" in the upper-right corner.

  18. Click the logout link.

    You are no longer logged in as CustomerManager. In the following steps, you will create a new user through the registration form.

  19. Click login link again.

  20. In the Login dialog box, click the Register now link.

    The registration form is now displayed.

  21. Fill out the registration form to create a new user account. Use the following values for the new user.

    Username: SalesUser

    Friendly name: SalesUser

    Email: someone@example.com

    Password: P@ssword

    Security Question: What was the color of your first car?

    Security Answer: Green

  22. Click OK to create the new user.

    Notice that you are now logged in as SalesUser.

  23. Close the browser.

  24. Open the ASP.NET Web Site Administration Tool again, and click the Security tab.

    Notice that 2 users now exist in the site and 2 roles exist even though you have only created one role.

  25. Click Create or Manage roles and notice the Managers role and the Registered Users.

    The Registered Users role was automatically created by the Business Application template.

  26. For Registered Users, click the Manage link.

    Notice that the user named SalesUser that you added through the application is in the Registered Users role.

  27. Close the ASP.NET Web Site Administration Tool.

Defining Access and profile properties

You restrict access to a domain operation by applying either the RequiresAuthenticationAttribute attribute or the RequiresRoleAttribute attribute to the domain operation. Domain operations without an attribute are available to all users. Applying an attribute to domain operation does not prevent the user from calling the domain operation; however, users without the required credentials will receive an exception.

Restrict Displayed Data by Roles

  1. In Solution Explorer, right-click the App_Data folder in the server project, select Add and then Existing Item.

  2. In the Add Existing Item dialog box, add the AdventureWorksLT sample database.

  3. In the server project, add a new item and select the ADO.NET Entity Data Model template from the Data templates.

  4. Name the model AdventureWorksModel.edmx and click Add.

    The Entity Data Model Wizard appears.

  5. Select the Generate from database option and then click Next.

  6. Select the AdventureWorksLT database and then click Next.

  7. From the list of database objects, select the Customer, Product, and SalesOrderHeader tables, and then click Finish.

    The entity data model appears in the designer.

  8. Build the solution.

  9. In the server project, add a new item and select the Domain Service Class template from the Web templates.

  10. Name the domain service AdventureWorksDomainService and then click Add.

  11. In the Add New Domain Service Class dialog box, select the Customer, Product, and SalesOrderHeader entities.

  12. Click OK to finish creating the domain service.

  13. In the AdventureWorksDomainService class file, add the RequiresAuthenticationAttribute attribute to GetSalesOrderHeader method.

    <RequiresAuthentication()> _
    Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
        Return Me.ObjectContext.SalesOrderHeaders
    End Function
    [RequiresAuthentication()]
    public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
    {
        return this.ObjectContext.SalesOrderHeaders;
    }
  14. Add the RequiresRoleAttribute attribute to the GetCustomers method, and set the name of the required role to "Managers".

    <RequiresRole("Managers")> _
    Public Function GetCustomers() As IQueryable(Of Customer)
        Return Me.ObjectContext.Customers
    End Function
    [RequiresRole("Managers")]
    public IQueryable<Customer> GetCustomers()
    {
        return this.ObjectContext.Customers;
    }

    The GetProducts domain operation is available to any user, GetSalesOrderHeaders is available to authenticated users, and GetCustomers is available to only users in the Managers role.

    The following shows the complete domain service.

    <EnableClientAccess()>  _
    Public Class AdventureWorksDomainService
        Inherits LinqToEntitiesDomainService(Of AdventureWorksLT_DataEntities)
    
        <RequiresRole("Managers")> _
        Public Function GetCustomers() As IQueryable(Of Customer)
            Return Me.ObjectContext.Customers
        End Function
    
        Public Function GetProducts() As IQueryable(Of Product)
            Return Me.ObjectContext.Products
        End Function
    
        <RequiresAuthentication()> _
        Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
            Return Me.ObjectContext.SalesOrderHeaders
        End Function
    End Class
    [EnableClientAccess()]
    public class AdventureWorksDomainService : LinqToEntitiesDomainService<AdventureWorksLT_DataEntities>
    {
        [RequiresRole("Managers")]
        public IQueryable<Customer> GetCustomers()
        {
            return this.ObjectContext.Customers;
        }
    
        public IQueryable<Product> GetProducts()
        {
            return this.ObjectContext.Products;
        }
    
        [RequiresAuthentication()]
        public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
        {
            return this.ObjectContext.SalesOrderHeaders;
        }
    }

You define a profile property in the Web.config file. When you add the property to the User class on the server, the corresponding property is generated for the client project.

Add profile properties

  1. In the server project, open the Web.config file.

  2. In the \ element, add a profile property named DefaultRows. The property will contain the user's preference for the number of rows to display.

    The following shows the profile section of the Web.config file.

    <profile>
      <properties>
        <add name="FriendlyName" />
        <add type="System.Int32" defaultValue="10" name="DefaultRows"/>
      </properties>
    </profile>
  3. Save the Web.config file.

  4. In the server project, expand the Models folder.

  5. Open the User.cs or User.vb file, and add a property named DefaultRows.

    Imports OpenRiaServices.Server.Authentication
    Imports System.Runtime.Serialization
    Namespace Web
        Partial Public Class User
            Inherits UserBase
    
            Public Property FriendlyName As String
    
            Public Property DefaultRows As Integer
    
        End Class
    End Namespace
    namespace ExampleBusinessApplication.Web
    {
        using System.Runtime.Serialization;
        using OpenRiaServices.Server.Authentication;
    
        public partial class User : UserBase
        {
            public string FriendlyName { get; set; }
    
            public int DefaultRows { get; set; }
        }
    }

Using Authentication Service from the client

Before calling a domain operation with restricted permissions, you should check that the user has the required credentials; otherwise, an exception is thrown. In the following section, you will check the user's credentials and populate from one to three DataGrid controls based on the user's credentials. You will also retrieve the number of records based on a property in the user profile. A default value of 10 is used for users that are not authenticated. This section does not include a way for users to set the DefaultRows profile property, but you will add that in a later section.

Add a Silverlight Page for displaying data

  1. In the client project, add a new item to the Views folder.

  2. Select the Silverlight Page template and name the new page Reports.xaml.

  3. Open the MainPage.xaml file and add a link for the Reports page by adding the following XAML after the HyperlinkButton named Link2 that links to the About page.

    <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
    
    <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" 
           NavigateUri="/Reports" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
  4. In the Assets\Resources folder, open the ApplicationStrings.resx file.

  5. Add a new string resource named ReportsPageTitle with the value of Reports.

  6. Save and close the ApplicationStrings.resx file.

  7. Open the Reports.xaml file, and add the following XAML to the Grid element to match the formatting of the other pages in the site.

    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
        <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
            <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
                       Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
            <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
                       Text="Display reports based on user permissions"/>
    
        </StackPanel>
    </ScrollViewer>
  8. Drag three DataGrid controls from the Toolbox to just before the end tag of the stack panel named ContentStackPanel.

    When you drag the DataGrid controls from the Toolbox, a reference to the System.Windows.Controls.Data assembly is added to the project and a prefix for the System.Windows.Controls namespace is added to the page.

  9. Name the DataGrid controls ProductsGrid, SalesOrdersGrid, and CustomersGrid.

  10. For each DataGrid control, set the Margin property to 5.

    The following example shows the complete Reports.xaml file.

    <navigation:Page x:Class="ExampleBusinessApplication.Views.Reports"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
               mc:Ignorable="d"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="Reports Page" >
        <Grid x:Name="LayoutRoot">
            <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
                <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
                    <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
                               Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
                    <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
                               Text="Display reports based on user permissions"/>
                    <data:DataGrid Name="ProductsGrid" Margin="5" />
                    <data:DataGrid Name="SalesOrdersGrid" Margin="5" />
                    <data:DataGrid Name="CustomersGrid" Margin="5" />
                </StackPanel>
            </ScrollViewer>
        </Grid>
    </navigation:Page>
  11. Open the Reports.xaml.cs or Reports.xaml.vb file.

  12. For C#, add using statements for the OpenRiaServices.Client, OpenRiaServices.Client.Authentication, and ExampleBusinessApplication.Web namespaces. For Visual Basic, add Imports statements for the OpenRiaServices.Client, OpenRiaServices.Client.Authentication, System.Windows.Controls, and ExampleBusinessApplication.Web namespaces.

  13. Create an instance of the AdventureWorksDomainContext named context, and create a variable named numberOfRows that contains the number of rows to retrieve.

    Private context As New AdventureWorksDomainContext
    Private numberOfRows As Integer = 10
    private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
    int numberOfRows = 10;
  14. Add a method named LoadRestrictedReports that calls the GetSalesOrderHeaderQuery method and the GetCustomersQuery method, if the user belongs to the Managers role, and populates the corresponding data grids with the results.

    If you call a domain operation when the user does not have the required credentials, the domain operation returns an exception. You can avoid this situation by checking the credentials before calling the domain operation.

    Private Sub LoadRestrictedReports()
        Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
        SalesOrdersGrid.ItemsSource = loadSales.Entities
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
    
        If (WebContext.Current.User.IsInRole("Managers")) Then
            Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
            CustomersGrid.ItemsSource = loadCustomers.Entities
            CustomersGrid.Visibility = System.Windows.Visibility.Visible
        Else
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
        End If
    End Sub
    private void LoadRestrictedReports()
    {
        LoadOperation<SalesOrderHeader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));
        SalesOrdersGrid.ItemsSource = loadSales.Entities;
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;
    
        if (WebContext.Current.User.IsInRole("Managers"))
        {
            LoadOperation<Customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));
            CustomersGrid.ItemsSource = loadCustomers.Entities;
            CustomersGrid.Visibility = System.Windows.Visibility.Visible;
        }
        else
        {
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
        }
    }
  15. Add a method named LoadReports that checks whether the user is authenticated and, if so, calls LoadRestrictedReports method. It also retrieves the profile property named DefaultRows and adds an event handler for the PropertyChanged event on the User object. Finally, it calls the GetProductsQuery method for all users.

    Private Sub LoadReports()
        If (WebContext.Current.User.IsAuthenticated) Then
            numberOfRows = WebContext.Current.User.DefaultRows
            AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
            LoadRestrictedReports()
        Else
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
        End If
    
        Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
        ProductsGrid.ItemsSource = loadProducts.Entities
    End Sub
    private void LoadReports()
    {
        if (WebContext.Current.User.IsAuthenticated)
        {
            numberOfRows = WebContext.Current.User.DefaultRows;
            WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);
            LoadRestrictedReports();
        }
        else
        {
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
        }
    
        LoadOperation<Product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));
        ProductsGrid.ItemsSource = loadProducts.Entities;
    }
  16. Add an event handler for the PropertyChanged event that calls LoadReports if the property DefaultRows has changed.

    Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
        If (e.PropertyName = "DefaultRows") Then
            LoadReports()
        End If
    End Sub
    void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "DefaultRows")
        {
            LoadReports();
        }
    }
  17. Add event handlers for the LoggedIn and LoggedOut events that either load or hide data based on the change in the user authentication credentials.

    Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
        LoadReports()
    End Sub
    
    Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
    End Sub
    void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
    {
        LoadReports();
    }
    
    void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)
    {
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
    }
  18. Add the following code to the constructor. This code loads the handlers and calls LoadReports.

    Public Sub New()
        InitializeComponent()
    
        Me.Title = ApplicationStrings.ReportsPageTitle
    
        AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
        AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
    
        LoadReports()
    End Sub
    public Reports()
    {
        InitializeComponent();
    
        this.Title = ApplicationStrings.ReportsPageTitle;
    
        WebContext.Current.Authentication.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
        WebContext.Current.Authentication.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);
    
        LoadReports();
    }

    The following shows the complete code file.

    Imports System.Windows.Navigation
    Imports System.Windows.Controls
    Imports OpenRiaServices.Client
    Imports OpenRiaServices.Client.Authentication
    Imports ExampleBusinessApplication.Web
    
    Partial Public Class Reports
        Inherits Page
    
        Private context As New AdventureWorksDomainContext
        Private numberOfRows As Integer = 10
    
        Public Sub New()
            InitializeComponent()
    
            Me.Title = ApplicationStrings.ReportsPageTitle
    
            AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
            AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
    
            LoadReports()
        End Sub
    
        Private Sub LoadReports()
            If (WebContext.Current.User.IsAuthenticated) Then
                numberOfRows = WebContext.Current.User.DefaultRows
                AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
                LoadRestrictedReports()
            Else
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
            End If
    
            Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
            ProductsGrid.ItemsSource = loadProducts.Entities
        End Sub
    
        Private Sub LoadRestrictedReports()
            Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
            SalesOrdersGrid.ItemsSource = loadSales.Entities
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
    
            If (WebContext.Current.User.IsInRole("Managers")) Then
                Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
                CustomersGrid.ItemsSource = loadCustomers.Entities
                CustomersGrid.Visibility = System.Windows.Visibility.Visible
            Else
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            End If
        End Sub
    
        Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
            If (e.PropertyName = "DefaultRows") Then
                LoadReports()
            End If
        End Sub
    
        Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
            LoadReports()
        End Sub
    
        Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
        End Sub
    End Class
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Navigation;
    using OpenRiaServices.Client;
    using OpenRiaServices.Client.Authentication;
    using ExampleBusinessApplication.Web;
    
    namespace ExampleBusinessApplication.Views
    {
        public partial class Reports : Page
        {
            private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
            int numberOfRows = 10;
    
            public Reports()
            {
                InitializeComponent();
    
                this.Title = ApplicationStrings.ReportsPageTitle;
    
                WebContext.Current.Authentication.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
                WebContext.Current.Authentication.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);
    
                LoadReports();
            }
    
            private void LoadReports()
            {
                if (WebContext.Current.User.IsAuthenticated)
                {
                    numberOfRows = WebContext.Current.User.DefaultRows;
                    WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);
                    LoadRestrictedReports();
                }
                else
                {
                    CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                    SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
                }
    
                LoadOperation<Product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));
                ProductsGrid.ItemsSource = loadProducts.Entities;
            }
    
            private void LoadRestrictedReports()
            {
                LoadOperation<SalesOrderHeader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));
                SalesOrdersGrid.ItemsSource = loadSales.Entities;
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;
    
                if (WebContext.Current.User.IsInRole("Managers"))
                {
                    LoadOperation<Customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));
                    CustomersGrid.ItemsSource = loadCustomers.Entities;
                    CustomersGrid.Visibility = System.Windows.Visibility.Visible;
                }
                else
                {
                    CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
    
            void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
            {
                LoadReports();
            }
    
            void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)
            {
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
            }
    
            void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "DefaultRows")
                {
                    LoadReports();
                }
            }
        }
    }
  19. Run the solution.

  20. Click the Reports link.

    Notice that when you are not logged in, only the products table is displayed on the Reports page.

  21. Click the login link and log in as SalesUser.

    Notice that the tables for products and sales orders are displayed.

  22. Log out and log in as CustomerManager.

    Notice that the tables for products, sales orders, and customers are displayed.

  23. Close the Web browser.

You can allow users to edit the DefaultRows profile property by adding child window. When the value is changed, you call the SaveUser method to save the value in the data source. You retrieve the current value through the properties on the User object of the current WebContext instance.

Add a window for setting profile property

  1. In the client project, add a new item to the Views folder.

  2. Select the Silverlight Child Window template and name the child window ProfileWindow.xaml.

  3. Click the Add button.

  4. In the ProfileWindow.xaml file, add the following XAML after the Grid.RowDefinitions element to include a ComboBox for selecting the number of rows to display in the reports.

    <StackPanel Orientation="Horizontal" Grid.Row="0">
        <TextBlock Text="Number of rows to display for reports: "></TextBlock>
        <ComboBox x:Name="defaultRows" Height="20" VerticalAlignment="Top">
            <ComboBoxItem Content="1"></ComboBoxItem>
            <ComboBoxItem Content="2"></ComboBoxItem>
            <ComboBoxItem Content="3"></ComboBoxItem>
            <ComboBoxItem Content="4"></ComboBoxItem>
            <ComboBoxItem Content="5"></ComboBoxItem>
            <ComboBoxItem Content="6"></ComboBoxItem>
            <ComboBoxItem Content="7"></ComboBoxItem>
            <ComboBoxItem Content="8"></ComboBoxItem>
            <ComboBoxItem Content="9"></ComboBoxItem>
            <ComboBoxItem Content="10"></ComboBoxItem>
            <ComboBoxItem Content="15"></ComboBoxItem>
            <ComboBoxItem Content="20"></ComboBoxItem>
        </ComboBox>
    </StackPanel>
  5. Set the Title property on the ChildWindow to Select Preferences.

  6. In the ProfileWindow.xaml.cs or ProfileWindow.xaml.vb file, add the following code to retrieve and set the profile property.

    Imports System.Windows.Controls
    Imports System.Windows
    
    Partial Public Class ProfileWindow
        Inherits ChildWindow
    
        Public Sub New()
            InitializeComponent()
    
            Dim userDefaultRows = WebContext.Current.User.DefaultRows.ToString()
            For Each cbi As ComboBoxItem In defaultRows.Items
                If (cbi.Content.ToString() = userDefaultRows) Then
                    defaultRows.SelectedItem = cbi
                    Exit For
                End If
            Next
        End Sub
    
        Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
            Dim newSelection = Integer.Parse(defaultRows.SelectionBoxItem.ToString())
            If (newSelection <> WebContext.Current.User.DefaultRows) Then
                WebContext.Current.User.DefaultRows = newSelection
                WebContext.Current.Authentication.SaveUser(True)
            End If
            Me.DialogResult = True
        End Sub
    
        Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
            Me.DialogResult = False
        End Sub
    
    End Class
    public partial class ProfileWindow : ChildWindow
    {
        public ProfileWindow()
        {
            InitializeComponent();
    
            string userDefaultRows = WebContext.Current.User.DefaultRows.ToString();
            foreach (ComboBoxItem cbi in defaultRows.Items)
            {
                if (cbi.Content.ToString() == userDefaultRows)
                {
                    defaultRows.SelectedItem = cbi;
                    break;
                }
            }
        }
    
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            int newSelection = int.Parse(defaultRows.SelectionBoxItem.ToString());
            if (newSelection != WebContext.Current.User.DefaultRows)
            {
                WebContext.Current.User.DefaultRows = newSelection;
                WebContext.Current.Authentication.SaveUser(true);
            }
            this.DialogResult = true;
        }
    
        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }
    }
  7. For Visual Basic, add Imports statements for the System.Windows.Controls, and System.Windows namespaces.

  8. Expand the Views\Login folder, and open the LoginStatus.xaml file.

  9. To add a settings link to the profile window, add the following XAML before the logout button.

    <Button x:Name="SettingsButton" Click="SettingsButton_Click" Content="settings" Style="{StaticResource LoginRegisterLinkStyle}" Margin="0,0,0,0"></Button>
    <TextBlock Text="  |  " Style="{StaticResource SpacerStyle}"/>
  10. In the LoginStatus.xaml.cs or LoginStatus.xaml.vb file, add the following click event handler for the settings link.

    Private Sub SettingsButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim settingsWindow As New ProfileWindow
        settingsWindow.Show()
    End Sub
    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        ExampleBusinessApplication.Views.ProfileWindow settingsWindow = new ExampleBusinessApplication.Views.ProfileWindow();
        settingsWindow.Show();
    }
  11. Run the solution.

  12. Log in as either CustomerManager or SalesUser and notice the login status bar now includes a link for settings.

  13. Click the settings link and set the default number of rows to show for the reports.

  14. Open the Reports page and notice that the DataGrid now contains the number of rows that you selected.

Last updated