演练:展示 RIA Services

本演练概述了 WCF RIA Services 中的许多功能。在本演练中,您将创建一个 RIA Services 应用程序,该应用程序会检索 AdventureWorks OLTP 示例数据库中的表包含的数据。首先,通过指定 LoadOperation 来检索数据。然后,使用 DomainDataSource 控件检索该数据。您可为数据呈现控件指定排序、筛选和分页,并添加一个 DataForm 控件以呈现数据的详细视图。将验证规则应用于字段,并使用户能够编辑数据值。仅允许经身份验证的用户访问域操作。最后,您可定义两个相关表之间的关联,并显示相关数据。

Tip提示:
有关通过创建更为基本的 RIA Services 解决方案进行入门的更简短演练,请参阅演练:创建 RIA Services 解决方案演练:使用“Silverlight 业务应用程序”模板

必备条件

除 WCF RIA Services 和 WCF RIA Services 工具包之外,本演练和 WCF RIA Services 文档中提供的其他演练还要求正确安装和配置 Visual Studio 2010 和 Silverlight Developer 运行时及 SDK 等几个必备程序。它们还要求安装和配置具有高级服务的 SQL Server 2008 R2 Express,并安装 AdventureWorks OLTP 和 LT 数据库。

WCF RIA Services 的必备条件节点中的主题提供有关如何满足这些前提条件的详细说明。在继续本演练之前,请按照此处提供的说明执行操作,以确保您在执行本 RIA Services 演练时尽可能少地遇到问题。

创建并设置解决方案

在本节中,您将创建并设置解决方案。

创建新的 WCF RIA Services 应用程序

  1. 在 Visual Studio 2010 中,通过依次选择**“文件”“新建”“项目”**来创建新的 RIA Services 项目。

    此时将出现**“新建项目”**对话框。

  2. 在**“已安装的模板”窗格中,展开“Visual Basic”“Visual C#”节点,并选择“Silverlight”**类别。

  3. 选择**“Silverlight 业务应用程序”**模板,并将应用程序命名为 HRApp

    RIA_HRAppStart

  4. 单击**“确定”**。

    请注意创建的解决方案的结构:

    • 该解决方案包含两个项目:一个名为 HRApp 的 Silverlight 客户端项目和一个名为 HRApp.Web 的 ASP.NET Web 应用程序服务器项目。

    • 默认解决方案包含多个自动实现的功能,其中包括导航、用户登录和注销以及新用户注册。

    RIA_HRAppStructure

  5. 生成并运行 (F5) 应用程序,并探索默认实现。

  6. 关闭 Web 浏览器。

设置应用程序

  1. 在**“解决方案资源管理器”**中的客户端项目中打开 MainPage.xaml。

  2. 在 XAML 视图中,找到名为 ApplicationNameTextBlockTextBlock

    如以下标记所示,请注意应用程序名称是从资源中检索的。

    <TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" 
                       Text="{Binding ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper}}"/>
    
  3. 在**“解决方案资源管理器”**中,展开 Assets 文件夹,然后展开 Resources 文件夹。

  4. 打开 ApplicationStrings.resx 文件。

  5. 将 ApplicationName 资源更改为 HR Application

  6. 保存并关闭 ApplicationStrings.resx 文件。

  7. 在**“解决方案资源管理器”中,右击 Views 文件夹,单击“添加”,然后单击“新建项”**。

    显示**“添加新项”**对话框。

  8. 从**“已安装的模板”“Silverlight”类别中选择“Silverlight 页”**模板,并将其命名为 EmployeeList.xaml

    RIA_HRAppAddPage

  9. 单击**“添加”**。

  10. 如果 EmployeeList.xaml 未自动打开,请将其打开。

  11. <Grid> 标记之间添加以下 XAML。

    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource 
      PageScrollViewerStyle}" >
        <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
    
            <TextBlock Text="Employee List" Style="{StaticResource HeaderTextStyle}"/>
    
        </StackPanel>
    </ScrollViewer>
    
  12. 保存 EmployeeList.xaml 文件。

  13. 打开 MainPage.xaml。

  14. 通过在两个现有超链接按钮之间添加以下 XAML,向页面顶部添加一个新的超链接按钮。

    <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/EmployeeList" TargetName="ContentFrame" Content="Employee List"/>
    
    <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
    
  15. 运行应用程序,并注意**“主页”“关于”链接之间页面右上角的新“员工列表”**链接。单击该链接以在页面的正文中显示“员工列表”。

    RIA_HRAppPageView

显示数据

在本节中,您将在 AdventureWorks 示例数据库中为表创建一个 ADO.NET 实体数据模型。然后,创建一个将公开实体并显示客户端项目中的数据的域服务。

添加数据源

  1. 在**“解决方案资源管理器”中,右击“HRApp.Web”项目,单击“添加”,再单击“新建项”**。

    显示**“添加新项”**对话框。

  2. 在**“数据”类别中,选择“ADO.NET 实体数据模型”**模板。

    RIA_HRAppAddEntity

  3. 将名称更改为 AdventureWorks.edmx,然后单击**“添加”**。

    **“实体数据模型向导”**将打开。

  4. 在**“选择模型内容”页上,单击“从数据库生成”,然后单击“下一步”**。

  5. 在**“选择您的数据连接”**页上,创建到 AdventureWorks 数据库的连接。

  6. 将实体连接设置命名为 AdventureWorks_DataEntities,然后单击**“下一步”**。

  7. 在**“选择数据库对象”页上,展开“表”**节点。

  8. 在**“Employee”“PurchaseOrderDetail”**和“PurchaseOrderHeader”表的旁边添加复选标记。

  9. 将该模型命名空间命名为 AdventureWorks_DataModel,然后单击**“完成”**。

    实体数据模型将出现在设计器中。

  10. 生成解决方案。

添加域服务对象并查询数据

  1. 在**“解决方案资源管理器”中,右击“HRApp.Web”项目,单击“添加”,再单击“新建项”**。

    显示**“添加新项”**对话框。

  2. 在**“Web”类别中,选择“域服务类”**模板。

    RIA_HRAppAddService

  3. 将新项命名为 OrganizationService

  4. 单击**“添加”**。

  5. 在**“添加新的域服务类”对话框中,从“实体”列表中选择“Employee”“PurchaseOrderDetail”“PurchaseOrderHeader”,然后为每个实体选择“启用编辑”**。

  6. 确保已选中**“启用客户端访问”“为元数据生成关联类”**复选框。

  7. 单击**“确定”**。

    将 OrganizationService.cs/vb 和 OrganizationService.metadata.cs/vb 文件添加到项目。

  8. 打开 OrganizationService.cs/vb 文件。

    请注意,已为每个实体创建查询、插入、更新和删除方法。始终只为一个实体创建一个查询方法。由于已选中**“启用编辑”**,因此添加了插入、更新和删除方法。

  9. 自定义 GetEmployees() 查询方法,以通过将生成的代码替换为以下代码来返回按 EmployeeID 排序的员工。

    Public Function GetEmployees() As IQueryable(Of Employee)
        Return Me.ObjectContext.Employees.OrderBy(Function(e) e.EmployeeID)
    End Function
    
    public IQueryable<Employee> GetEmployees()
    {
        return this.ObjectContext.Employees.OrderBy(e => e.EmployeeID);
    }
    
  10. 生成解决方案。

    生成将在客户端项目中生成域上下文和实体的解决方案。

  11. 打开 EmployeeList.xaml。

  12. 从工具箱中,将 DataGrid 控件拖动到设计视图中的 TextBlock 控件后。

    若将 DataGrid 拖动到设计视图,则会添加对 System.Windows.Controls.Data 程序集的引用,并会向 Page 元素添加 sdk 前缀。

  13. 可通过以下方式来更改 DataGrid 控件的默认值:移除“高度”和“宽度”属性,使其只读,并将其设置为自动生成列,然后设置其最小高度。

    <sdk:DataGrid AutoGenerateColumns="True" IsReadOnly="True" Name="dataGrid1" MinHeight="100" />
    
  14. 保存 EmployeeList.xaml。

  15. 打开 EmployeeList.xaml.cs/vb。

  16. 添加以下 usingImports 语句。

    Imports System.ServiceModel.DomainServices.Client
    
    using HRApp.Web;
    using System.ServiceModel.DomainServices.Client;
    
  17. 通过向 EmployeeList.xaml.cs/vb 中添加以下代码,可初始化 OrganizationContext 类并加载员工数据。

    将基于服务器项目中的 OrganizationService 类在客户端项目中自动生成 OrganizationContext 类。

    Partial Public Class EmployeeList
        Inherits Page
    
        Dim _OrganizationContext As New OrganizationContext
        Public Sub New()
            InitializeComponent()
            Me.dataGrid1.ItemsSource = _OrganizationContext.Employees
            _OrganizationContext.Load(_OrganizationContext.GetEmployeesQuery())
        End Sub
    
        'Executes when the user navigates to this page.
        Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
    
        End Sub
    End Class
    
    public partial class EmployeeList : Page
    {
        OrganizationContext _OrganizationContext = new OrganizationContext();
        public EmployeeList()
        {
            InitializeComponent();
            this.dataGrid1.ItemsSource = _OrganizationContext.Employees;
            _OrganizationContext.Load(_OrganizationContext.GetEmployeesQuery());
        }
    
        // Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }
    }
    
  18. 运行该应用程序。

  19. 单击“员工列表”链接以查看 DataGrid

    RIA_HRAppDataGrid

添加自定义查询

  1. 在 HRApp.Web 项目中,打开 OrganizationService.cs/vb。

  2. 通过向类的主体中添加以下代码,可添加一个名为 GetSalariedEmployees 的新方法。

    Public Function GetSalariedEmployees() As IQueryable(Of Employee)
        Return Me.ObjectContext.Employees.Where(Function(e) e.SalariedFlag = True).OrderBy(Function(e) e.EmployeeID)
    End Function
    
    public IQueryable<Employee> GetSalariedEmployees()
    {
        return this.ObjectContext.Employees.Where(e => e.SalariedFlag == true).OrderBy(e => e.EmployeeID);
    }
    
  3. 生成解决方案。

  4. 在客户端项目中,打开 EmployeeList.xaml.cs/vb。

  5. 在构造函数中,将对 GetEmployeesQuery() 的调用替换为对 GetSalariedEmployeesQuery() 的调用。

    _OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery())
    
    _OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery());
    
  6. 运行应用程序并单击**“员工列表”**链接。

    请注意,已检查所有显示的员工的 SalariedFlag 值。列表中将不再显示 EmployeeID 为 1、2 和 4 的员工,因为这些员工未领工资。

    RIA_HRAppFilteredDataGrid

添加域数据源

  1. 打开 EmployeeList.xaml。

  2. 从**“工具箱”**中,将 DomainDataSource 控件拖动到设计视图中的 DataGrid 的前面。DomainDataSource 可能会在控件列表的底部出现。

    Tip提示:
    如果 DomainDataSource 控件未在“工具箱”中,则右击“工具箱”并单击“选择项”。在“Silverlight 组件”选项卡下,选中“DomainDataSource”并单击“确定”

    当您将 DomainDataSource 控件拖动到设计视图时,将在 Page 元素中为 System.Windows.Controls 命名空间创建一个包含前缀 riaControls 的引用。设计视图的左下角还会出现一个数据源图标。

  3. 对于 C# 解决方案,向 XAML 文件中添加以下命名空间声明。

    xmlns:ds="clr-namespace:HRApp.Web"
    
  4. 对于 Visual Basic 解决方案,向 XAML 文件中添加以下命名空间声明。

    xmlns:ds="clr-namespace:HRApp"
    
  5. 通过将现有 XAML 替换为以下 XAML,将 DomainDataSource 控件命名为 employeeDataSource 并设置 LoadSizeAutoLoad 和查询方法。

    <riaControls:DomainDataSource Name="employeeDataSource" LoadSize="20" QueryName="GetSalariedEmployees" AutoLoad="True">
    </riaControls:DomainDataSource>
    
  6. 通过添加以下 XAML 为 DomainDataSource 设置 DomainContext

    <riaControls:DomainDataSource Name="employeeDataSource" LoadSize="20" QueryName="GetSalariedEmployees" AutoLoad="True">
        <riaControls:DomainDataSource.DomainContext>
            <ds:OrganizationContext/>
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
    
  7. DataGrid 替换为以下 XAML。

    <sdk:DataGrid AutoGenerateColumns="True" IsReadOnly="True" Name="dataGrid1" MinHeight="100" Height="Auto" ItemsSource="{Binding Data, ElementName=employeeDataSource}" />
    
  8. 打开 EmployeeList.xaml.cs/vb。

  9. 在构造函数中,移除或注释掉用于实例化 OrganizationContext 实例和对 GetSalariedEmployeesQuery() 的调用的代码以及用于设置 DataGrid 控件的 ItemsSource 属性的代码。

    您不再需要显式加载数据,因为 DomainDataSource 将自动执行此操作。

    'Dim _OrganizationContext As New OrganizationContext
    Public Sub New()
        InitializeComponent()
        'Me.dataGrid1.ItemsSource = _OrganizationContext.Employees
        '_OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery())
    
    End Sub
    
    //OrganizationContext _OrganizationContext = new OrganizationContext();
    public EmployeeList()
    {
        InitializeComponent();
        //this.dataGrid1.ItemsSource = _OrganizationContext.Employees;
        //_OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery());
    }
    
  10. 运行应用程序并单击**“员工列表”**链接。

    应用程序将像往常一样工作。

向数据源添加排序、筛选和分页

  1. 打开 EmployeeList.xaml。

  2. DomainDataSource 中,添加以下 SortDescriptors 以指定 DataGrid 中的数据排序方式。

    此 XAML 演示如何按升序对 VacationHours 列进行排序。

    <riaControls:DomainDataSource Name="employeeDataSource" LoadSize="20" QueryName="GetSalariedEmployees" AutoLoad="True">
        <riaControls:DomainDataSource.DomainContext>
            <ds:OrganizationContext/>
        </riaControls:DomainDataSource.DomainContext>
        <riaControls:DomainDataSource.SortDescriptors>
            <riaControls:SortDescriptor PropertyPath="VacationHours" Direction="Ascending" />
        </riaControls:DomainDataSource.SortDescriptors>
    </riaControls:DomainDataSource>
    
  3. 运行应用程序并单击**“员工列表”**链接。

    按 VacationHours 对数据进行排序,并且您可以通过单击列标题来更改排序方向。

  4. 打开 EmployeeList.xaml。

  5. 若要允许用户通过提供一个值来筛选返回的记录,请在 DataGrid 前面添加以下 XAML。

    XAML 将添加一个 TextBox 控件,以便用户能输入一个值。

    <StackPanel Orientation="Horizontal" 
      HorizontalAlignment="Left">
        <TextBlock VerticalAlignment="Center" 
        Text="Min Vacation Hours Filter" />
        <TextBox x:Name="vacationHoursText" Width="75" 
      FontSize="11" Margin="4" Text="0"/>
    </StackPanel>
    
  6. DomainDataSource 中,添加一个已绑定到您在上一步骤中添加的 TextBox 控件的筛选器描述符。

    <riaControls:DomainDataSource.FilterDescriptors>
        <riaControls:FilterDescriptor 
             PropertyPath="VacationHours" 
             Operator="IsGreaterThanOrEqualTo"
             IgnoredValue=""
             Value="{Binding ElementName=vacationHoursText, Path=Text}"  >
        </riaControls:FilterDescriptor>
    </riaControls:DomainDataSource.FilterDescriptors>
    
  7. 运行应用程序并单击**“员工列表”**链接。

  8. 在**“最小休假小时数筛选器”**文本框中,键入 70。

    请注意,列出的员工具有的 VacationHours 将大于或等于 70。

    RIA_HRAppVacationFilter

  9. 打开 EmployeeList.xaml。

  10. 从**“工具箱”**中,将 DataPager 控件拖动到 DataGrid 的下方。

  11. 将页面大小设置为 5 并设置源,如以下 XAML 中所示。

    <sdk:DataPager PageSize="5" Source="{Binding Data, ElementName=employeeDataSource}" HorizontalAlignment="Left" />
    
  12. 运行应用程序并单击**“员工列表”**链接。

    DataGrid 下方只会显示经筛选的数据(一个页面 5 行数据)和页导航控件。

    RIA_HRAppPagedData

创建主/详细信息视图

在本节中,您将使用 Silverlight 工具包中的 DataForm 控件来提供数据的详细视图。默认情况下,Silverlight 业务应用程序项目模板将在 Libs 文件夹中包含 System.Windows.Controls.Data.DataForm.Toolkit.dll 二进制文件。

添加 DataForm

  1. 打开 EmployeeList.xaml。

  2. 添加以下命名空间声明。

    xmlns:dataForm="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
    
  3. DataPager 控件的后面,添加以下 XAML 以添加一个 DataForm 控件。

    此 XAML 将设置 DataForm 特性并指定要显示的列。

    <dataForm:DataForm x:Name="dataForm1" Header="Employee Information"
            AutoGenerateFields="False" HorizontalAlignment="Left"
            AutoEdit="False" AutoCommit="False" Width="400"
            CurrentItem="{Binding SelectedItem, ElementName=dataGrid1}" Margin="0,12,0,0">
        <dataForm:DataForm.EditTemplate>
            <DataTemplate>
                <StackPanel>
                    <dataForm:DataField Label="Employee ID">
                        <TextBox IsReadOnly="True" 
                  Text="{Binding EmployeeID, Mode=OneWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Login ID">
                        <TextBox Text="{Binding LoginID, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Hire Date">
                        <TextBox Text="{Binding HireDate, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Marital Status">
                        <TextBox Text="{Binding MaritalStatus, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Gender">
                        <TextBox Text="{Binding Gender, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }"  />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Vacation Hours">
                        <TextBox Text="{Binding VacationHours, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }"  />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Sick Leave Hours">
                        <TextBox Text="{Binding SickLeaveHours, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }"  />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Active">
                        <CheckBox IsChecked="{Binding CurrentFlag, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }"  />
                    </dataForm:DataField>
                </StackPanel>
            </DataTemplate>
        </dataForm:DataForm.EditTemplate>
    </dataForm:DataForm>
    
  4. 运行应用程序并单击**“员工列表”**链接。

    DataForm 将显示在 DataGrid 中的选定项的详细信息。

    RIA_HRAppDataForm

更新数据库

当您选中**“新的域服务类”对话框中的“启用编辑”**复选框时,将在域服务层中生成方法以更新、插入和删除实体。在本节中,您将向员工列表的用户界面中添加编辑按钮,以允许用户执行这些操作。

更新记录

  1. 打开 EmployeeList.xaml 文件。

  2. DataForm 控件的后面,添加以下 XAML 以添加一个“提交”按钮。

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,12,0,0">
        <Button x:Name="submitButton" Width="75" Height="23"  
            Content="Submit" Margin="4,0,0,0" Click="submitButton_Click"/>
    </StackPanel>
    
  3. DomainDataSource 控件中,为 SubmittedChanges 事件指定事件处理程序。

    <riaControls:DomainDataSource Name="employeeDataSource" LoadSize="20" QueryName="GetSalariedEmployees" AutoLoad="True" SubmittedChanges="employeeDataSource_SubmittedChanges">
    
  4. 打开 EmployeeList.xaml.cs/vb。

  5. 为按钮单击事件添加以下事件处理程序。

    将禁用 submitButton 以在处理操作时阻止用户再次提交更改。

    Private Sub submitButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        submitButton.IsEnabled = False
        employeeDataSource.SubmitChanges()
    End Sub
    
    private void submitButton_Click(object sender, RoutedEventArgs e)
    {
        submitButton.IsEnabled = false;
        employeeDataSource.SubmitChanges();
    }
    
  6. SubmittedChanges 事件添加事件处理程序,该事件将检查提交操作是否成功完成并启用 submitButton

    Private Sub employeeDataSource_SubmittedChanges(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SubmittedChangesEventArgs)
        If (e.HasError) Then
            MessageBox.Show(String.Format("Changes were not saved: {0}", e.Error.Message))
            e.MarkErrorAsHandled()
        End If
        submitButton.IsEnabled = True
    End Sub
    
    private void employeeDataSource_SubmittedChanges(object sender, SubmittedChangesEventArgs e)
    {
        if (e.HasError)
        {
            MessageBox.Show(string.Format("Changes were not saved: {0}", e.Error.Message));
            e.MarkErrorAsHandled();
        }
        submitButton.IsEnabled = true;
    }
    
  7. 运行应用程序并单击**“员工列表”**链接。

  8. 选择一名员工,并单击数据窗体右上角的铅笔图标以启用编辑。

    现在您可修改任何可编辑字段。

  9. 对员工数据进行更改,然后单击**“确定”**。

  10. 单击**“提交”**按钮以保存数据。

    仅在您单击“提交”按钮时将更改保存到服务器上的数据库中。

向域服务中添加自定义方法

  1. 在 HRApp.Web 服务器项目中,打开 OrganizationService.cs/vb 文件。

  2. 添加以下名为 ApproveSabbatical 的自定义方法。

    Public Sub ApproveSabbatical(ByVal current As Employee)
        Me.ObjectContext.Employees.AttachAsModified(current)
        current.CurrentFlag = False
    End Sub
    
    public void ApproveSabbatical(Employee current)
    {
        // Start custom workflow here
        this.ObjectContext.Employees.AttachAsModified(current);
        current.CurrentFlag = false;
    }
    
  3. 生成解决方案。

  4. 打开 EmployeeList.xaml。

  5. 在“提交”按钮后面,添加以下 XAML 以添加一个“批准休假”按钮。

    <Button x:Name="approveSabbatical" Width="115" Height="23"  Content="Approve Sabbatical"  Margin="4,0,0,0" Click="approveSabbatical_Click"/>
    
  6. 打开 EmployeeList.xaml.cs/vb。

  7. 为调用 ApproveSabbatical 域操作的按钮单击事件添加以下事件处理程序。

    Private Sub approveSabbatical_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim luckyEmployee As Employee
        luckyEmployee = dataGrid1.SelectedItem
        luckyEmployee.ApproveSabbatical()
        employeeDataSource.SubmitChanges()
    End Sub
    
    private void approveSabbatical_Click(object sender, RoutedEventArgs e)
    {
        Employee luckyEmployee = (Employee)(dataGrid1.SelectedItem);
        luckyEmployee.ApproveSabbatical();
        employeeDataSource.SubmitChanges();
    }
    
  8. 运行应用程序并单击**“员工列表”**链接。

  9. 单击**“批准休假”**按钮,请注意,已清除所选员工的“CurrentFlag”复选框。

验证数据

DataForm 控件可显示来自数据访问层 (DAL) 的验证错误。例如,如果您在 VacationHours 字段中输入一个非整数值,则将显示一个验证错误。下图显示了验证行为的一个示例。

RIA_HRAppValidation

当您选中**“新的域服务类”对话框中的“为元数据生成关联类”**复选框时,将创建一个包含元数据的文件。在此演练中,该元数据文件名为 OrganizationService.metadata.cs/vb。在本节中,您将向此文件中添加验证特性。将在客户端和服务器项目中强制实施验证规则。

您还将创建一个用户界面,以便能向数据库中添加新员工记录。将在新的用户界面中自动应用您在前面的部分中添加的验证规则。

添加基本验证

  1. 在 HRApp.web 项目中,打开 OrganizationService.metadata.cs/vb。

  2. GenderVacationHours 属性中添加以下特性。

    <Required()> _
    Public Property Gender As String
    
    <Range(0, 70)> _
    Public Property VacationHours As Short
    
    [Required]
    public string Gender { get; set; }
    
    [Range(0, 70)]
    public short VacationHours { get; set; }
    
  3. 生成解决方案。

  4. 运行应用程序并单击**“员工列表”**链接。

  5. 选择一名员工,并单击数据窗体右上角的铅笔图标以启用编辑。

  6. 在**“休假小时数”**字段中输入一个有效范围 (0-70) 之外的值,并将焦点移至另一个控件。

    您将看到休假小时数的验证错误。

    RIA_HRAppRangeValidation

  7. 删除**“性别”**字段中的值,并将焦点移至另一个控件。

    您将看到性别的验证错误。

添加自定义验证

  1. 在**“解决方案资源管理器”中,右击“HRApp.Web”项目,单击“添加”,再单击“新建项”**。

    显示**“添加新项”**对话框。

  2. 在**“代码”类别中,选择“代码文件”**模板。

  3. 将新项命名为 OrganizationService.shared.csOrganizationService.shared.vb

    客户端项目和服务器项目中都提供了以 .shared.cs.shared.vb 结尾的文件。利用共享文件,您可在这两个项目中运行相同的验证规则。在后面的步骤中生成解决方案之后,请查看客户端上隐藏的 Generated_Code 文件夹,您将看到 OrganizationService.shared.cs/vb 文件。

  4. 单击**“添加”**。

  5. 若要创建一个用于验证分配给 Gender 属性的值的自定义验证类,请向共享文件中添加以下代码。

    Imports System
    Imports System.ComponentModel.DataAnnotations
    
    Public Module GenderValidator
        Public Function IsGenderValid(ByVal gender As String, ByVal context As ValidationContext) As ValidationResult
            If gender = "M" OrElse gender = "m" OrElse gender = "F" OrElse gender = "f" Then
                Return ValidationResult.Success
            Else
                Return New ValidationResult("The Gender field only has two valid values 'M'/'F'", New String() {"Gender"})
            End If
        End Function
    End Module
    
    using System;
    using System.ComponentModel.DataAnnotations;
    
    namespace HRApp.Web
    {
        public static class GenderValidator
        {
            public static ValidationResult IsGenderValid(string gender, ValidationContext context)
            {
                if (gender == "M" || gender == "m" || gender == "F" || gender == "f")
                {
                    return ValidationResult.Success;
                }
                else
                {
                    return new ValidationResult("The Gender field only has two valid values 'M'/'F'", new string[] { "Gender" });
                }
            }
        }
    }
    
  6. 打开 OrganizationService.metadata.cs/vb。

  7. Gender 属性中添加以下自定义验证特性。

    <Required()> _
    <CustomValidation(GetType(GenderValidator), "IsGenderValid")> _
    Public Property Gender As String
    
    [CustomValidation(typeof(HRApp.Web.GenderValidator), "IsGenderValid")]
    [Required]
    public string Gender { get; set; }
    
  8. 生成解决方案。

  9. 运行应用程序并单击**“员工列表”**链接。

  10. 选择一名员工,并单击数据窗体右上角的铅笔图标以启用编辑。

  11. 在**“性别”**字段中输入一个 M 或 F 之外的值,并将焦点移至另一个控件。

    将出现自定义验证的结果。

    RIA_HRAppCustomValidation

添加新记录

在本节中,您将添加一个允许用户在“员工”表中创建新记录的窗体。

添加新记录

  1. 在 HRApp 项目中添加一个新项。

  2. 在**“Silverlight”类别中,选择“Silverlight 子窗口”**模板。

  3. 将新项命名为 EmployeeRegistrationWindow.xaml

    RIA_HRAppAddChildWindow

  4. 单击**“添加”**。

  5. 打开 EmployeeRegistrationWindow.xaml.cs/vb。

  6. 如果您使用的是 C#,则添加以下 using 语句。

    using HRApp.Web;
    
  7. 为使用用户值创建的新 Employee 实体添加一个属性。

    Public Property NewEmployee As Employee
    
    public Employee NewEmployee { get; set; }
    
  8. 打开 EmployeeRegistrationWindow.xaml。

  9. 向 EmployeeRegistrationWindow.xaml 中添加以下命名空间声明,以便在此窗口中使用 DataForm 控件。

    xmlns:dataForm="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
    
  10. 将以下 DataForm 控件添加到 EmployeeRegistrationWindow.xaml 中的“取消”按钮的前面。

    <dataForm:DataForm x:Name="addEmployeeDataForm"   AutoGenerateFields="False" AutoCommit="True" AutoEdit="True" CommandButtonsVisibility="None">
        <dataForm:DataForm.EditTemplate>
            <DataTemplate>
                <StackPanel>
                    <dataForm:DataField Label="Login ID">
                        <TextBox Text="{Binding LoginID, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="National ID">
                        <TextBox Text="{Binding NationalIDNumber, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Title">
                        <TextBox Text="{Binding Title, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Marital Status">
                        <TextBox Text="{Binding MaritalStatus, Mode=TwoWay}" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Gender">
                        <TextBox Text="{Binding Gender, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Salaried">
                        <CheckBox IsChecked="{Binding SalariedFlag, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }" />
                    </dataForm:DataField>
                    <dataForm:DataField Label="Active">
                        <CheckBox IsChecked="{Binding CurrentFlag, Mode=TwoWay,NotifyOnValidationError=True,  ValidatesOnExceptions=True }" />
                    </dataForm:DataField>
                </StackPanel>
            </DataTemplate>
        </dataForm:DataForm.EditTemplate>
    </dataForm:DataForm>
    
  11. 打开 EmployeeRegistrationWindow.xaml.cs/vb。

  12. 通过提交新实例或取消插入可添加以下代码以创建新的 Employee 实例和句柄。

    Partial Public Class EmployeeRegistrationWindow
        Inherits ChildWindow
    
        Public Sub New()
            InitializeComponent()
            NewEmployee = New Employee
            addEmployeeDataForm.CurrentItem = NewEmployee
            addEmployeeDataForm.BeginEdit()
        End Sub
    
        Public Property NewEmployee As Employee
    
        Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
            Me.addEmployeeDataForm.CommitEdit()
            Me.DialogResult = True
        End Sub
    
        Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
            NewEmployee = Nothing
            addEmployeeDataForm.CancelEdit()
            Me.DialogResult = False
        End Sub
    
    End Class
    
    public partial class EmployeeRegistrationWindow : ChildWindow
    {
        public EmployeeRegistrationWindow()
        {
            InitializeComponent();
            NewEmployee = new Employee();
            addEmployeeDataForm.CurrentItem = NewEmployee;
            addEmployeeDataForm.BeginEdit();    
        }
    
        public Employee NewEmployee { get; set; }
    
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            addEmployeeDataForm.CommitEdit();
            this.DialogResult = true;
        }
    
        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            NewEmployee = null;
            addEmployeeDataForm.CancelEdit();
            this.DialogResult = false;
        }
    }
    
  13. 打开 EmployeeList.xaml。

  14. DataPagerDataForm 之间,添加以下 XAML 以创建一个名为 addNewEmployee 的按钮。

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,12,0,0">
        <Button x:Name="addNewEmployee" Width="90" Height="23"  Content="Add Employee"  Margin="4,0,0,0" Click="addNewEmployee_Click"/>
    </StackPanel>
    
  15. 打开 EmployeeList.xaml.cs/vb。

  16. 添加以下代码以处理按钮单击事件并显示 EmployeeRegistrationWindow。

    Private Sub addNewEmployee_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim addEmp As New EmployeeRegistrationWindow()
        AddHandler addEmp.Closed, AddressOf addEmp_Closed
        addEmp.Show()
    End Sub
    
    private void addNewEmployee_Click(object sender, RoutedEventArgs e)
    {
        EmployeeRegistrationWindow addEmp = new EmployeeRegistrationWindow();
        addEmp.Closed += new EventHandler(addEmp_Closed);
        addEmp.Show();
    }
    
  17. 添加以下方法以处理 EmployeeRegistrationWindow 的 closed 事件。

    Private Sub addEmp_Closed(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim emp As EmployeeRegistrationWindow = sender
        If Not emp.NewEmployee Is Nothing Then
            Dim _OrganizationContext As OrganizationContext = employeeDataSource.DomainContext
            _OrganizationContext.Employees.Add(emp.NewEmployee)
            employeeDataSource.SubmitChanges()
        End If
    End Sub
    
    void addEmp_Closed(object sender, EventArgs e)
    {
        EmployeeRegistrationWindow emp = (EmployeeRegistrationWindow)sender;
        if (emp.NewEmployee != null)
        {
            OrganizationContext _OrganizationContext = (OrganizationContext)(employeeDataSource.DomainContext);
            _OrganizationContext.Employees.Add(emp.NewEmployee);
            employeeDataSource.SubmitChanges();
        }
    }
    
  18. 打开 OrganizationService.cs/vb。

  19. 通过添加以下代码来修改 InsertEmployee 方法。

    Public Sub InsertEmployee(ByVal employee As Employee)
        employee.HireDate = DateTime.Now
        employee.ModifiedDate = DateTime.Now
        employee.VacationHours = 0
        employee.SickLeaveHours = 0
        employee.rowguid = Guid.NewGuid()
        employee.ContactID = 1001
        employee.BirthDate = New DateTime(1967, 3, 18)
    
        If ((employee.EntityState = EntityState.Detached) _
                    = False) Then
            Me.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added)
        Else
            Me.ObjectContext.Employees.AddObject(employee)
        End If
    End Sub
    
    public void InsertEmployee(Employee employee)
    {
        employee.HireDate = DateTime.Now;
        employee.ModifiedDate = DateTime.Now;
        employee.VacationHours = 0;
        employee.SickLeaveHours = 0;
        employee.rowguid = Guid.NewGuid();
        employee.ContactID = 1001;
        employee.BirthDate = new DateTime(1967, 3, 18);
    
        if ((employee.EntityState != EntityState.Detached))
        {
            this.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added);
        }
        else
        {
            this.ObjectContext.Employees.AddObject(employee);
        }
    }
    
  20. 运行应用程序并单击**“员工列表”**链接。

  21. 单击**“添加员工”**按钮。

    这将打开 EmployeeRegistrationWindow。

    RIA_Employee_Registration

  22. 在该窗口中添加数据,并选中**“带薪”**复选框。

  23. 单击**“确定”**。

  24. 刷新页面并确保新员工在 DataGrid 中出现。

对用户进行身份验证

在本节中,您仅允许经身份验证的用户访问 ApproveSabbatical 方法。

添加身份验证

  1. 打开 OrganizationService.cs/vb。

  2. RequiresAuthentication 特性添加到 ApproveSabbatical 方法。

    当您将 RequiresAuthentication 特性应用于域操作时,请确保仅经身份验证的用户能调用该操作。如果匿名用户单击**“批准休假”**按钮,则不会执行此操作。

    <RequiresAuthentication()> _
    Public Sub ApproveSabbatical(ByVal current As Employee)
        Me.ObjectContext.Employees.AttachAsModified(current)
        current.CurrentFlag = False
    End Sub
    
    [RequiresAuthentication]
    public void ApproveSabbatical(Employee current)
    {
        // Start custom workflow here
        this.ObjectContext.Employees.AttachAsModified(current);
        current.CurrentFlag = false;
    }
    
  3. 打开 EmployeeList.xaml.cs/vb。

  4. 添加以下 usingImports 语句。

    Imports System.ServiceModel.DomainServices.Client.ApplicationServices
    Imports HRApp.LoginUI
    
    using System.ServiceModel.DomainServices.Client.ApplicationServices;
    using HRApp.LoginUI;
    
  5. 修改 approveSabbatical_Click 方法并添加一个 LoggedIn 处理程序以检查是否对用户进行了身份验证。

    Private Sub approveSabbatical_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        If WebContext.Current.User IsNot Nothing AndAlso WebContext.Current.User.IsAuthenticated Then
            Dim luckyEmployee As Employee = dataGrid1.SelectedItem
            luckyEmployee.ApproveSabbatical()
            employeeDataSource.SubmitChanges()
        Else
            AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Current_LoginCompleted
            Dim newWindow As New LoginRegistrationWindow
            newWindow.Show()
        End If
    End Sub
    
    Private Sub Current_LoginCompleted(ByVal sender As Object, ByVal e As AuthenticationEventArgs)
        Dim luckyEmployee As Employee = dataGrid1.SelectedItem
        luckyEmployee.ApproveSabbatical()
        employeeDataSource.SubmitChanges()
        RemoveHandler WebContext.Current.Authentication.LoggedIn, AddressOf Current_LoginCompleted
    End Sub
    
    private void approveSabbatical_Click(object sender, RoutedEventArgs e)
    {
        if (WebContext.Current.User.IsAuthenticated)
        {
            Employee luckyEmployee = (Employee)(dataGrid1.SelectedItem);
            luckyEmployee.ApproveSabbatical();
            employeeDataSource.SubmitChanges();
        }
        else
        {
            WebContext.Current.Authentication.LoggedIn += Authentication_LoggedIn;
            new LoginRegistrationWindow().Show();
        }
    }
    
    private void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
    {
        Employee luckyEmployee = (Employee)(dataGrid1.SelectedItem);
        luckyEmployee.ApproveSabbatical();
        employeeDataSource.SubmitChanges();
    
        WebContext.Current.Authentication.LoggedIn -= Authentication_LoggedIn;
    }
    
  6. 运行应用程序并单击**“员工列表”**链接。

  7. 选择一个员工记录,并单击**“批准休假”**按钮。

    您会被重定向到登录窗口。

  8. 单击**“立即注册”**链接。

  9. 填写必填字段以创建一个新帐户。

  10. 单击**“确定”**。

    使用该帐户登录后,导航链接下面的栏中将显示您的名称。

显示相关数据

利用 RIA Services ,您可轻松使用相关表中的数据。在本节中,您将添加一个新的 Silverlight 页,并显示 PurchaseOrderHeader 和 PurchaseOrderDetail 表中的数据。您还可自定义数据修改操作,以便同时修改相关数据。有关通过域操作修改相关表中的数据的示例,请参见复合层次结构

显示相关表中的数据

  1. 在 HRApp 项目中,右击 Views 文件夹,单击**“添加”,然后单击“新建项”**。

  2. 添加一个名为 PurchaseOrders.xaml 的新 Silverlight 页面。

  3. 打开 PurchaseOrders.xaml。

  4. <Grid> 标记之间添加以下 XAML。

    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource 
      PageScrollViewerStyle}" >
        <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
    
            <TextBlock Text="Purchase Orders" Style="{StaticResource HeaderTextStyle}"/>
        </StackPanel>
    </ScrollViewer>
    
  5. 打开 MainPage.xaml。

  6. 在 EmployeeList 链接的后面,添加以下 XAML 以包含指向 PurchaseOrders 页面的链接。

    <Rectangle x:Name="Divider3" Style="{StaticResource DividerStyle}"/>
    
    <HyperlinkButton x:Name="Link4" Style="{StaticResource LinkStyle}" NavigateUri="/PurchaseOrders" TargetName="ContentFrame" Content="Purchase Orders"/>
    
  7. 打开 OrganizationService.metadata.cs/vb。

  8. PurchaseOrderHeaderMetadata 类中的 PurchaseOrderDetails 属性添加 IncludeComposition 特性。

    <Include()> _
    <Composition()> _
    Public Property PurchaseOrderDetails As EntityCollection(Of PurchaseOrderDetail)
    
    [Include]
    [Composition]
    public EntityCollection<PurchaseOrderDetail> PurchaseOrderDetails { get; set; }
    
  9. 打开 OrganizationService.cs/vb。

  10. 更改 GetPurchaseOrderHeaders 方法,以便将 PurchaseOrderDetails 中的相关记录与查询一起检索。

    Public Function GetPurchaseOrderHeaders() As IQueryable(Of PurchaseOrderHeader)
        Return Me.ObjectContext.PurchaseOrderHeaders.Include("PurchaseOrderDetails").OrderBy(Function(p) p.PurchaseOrderID)
    End Function
    
    public IQueryable<PurchaseOrderHeader> GetPurchaseOrderHeaders()
    {
        return this.ObjectContext.PurchaseOrderHeaders.Include("PurchaseOrderDetails").OrderBy(p => p.PurchaseOrderID);
    }
    
  11. 打开 PurchaseOrders.xaml。

  12. 在**“数据”菜单上单击“显示数据源”,打开“数据源”**窗口。

  13. 将**“PurchaseOrderHeader”**节点拖动到 PurchaseOrders.xaml 的设计图面。

    RIA_Order_Header

    将出现一个包含 PurchaseOrderHeader 表中的列的 DataGrid

  14. 在**“数据源”窗口中,展开“PurchaseOrderHeader”**节点。

  15. 将位于**“PurchaseOrderHeader”节点内部的“PurchaseOrderDetails”**节点拖动到设计图面中的 PurchaseOrderHeader 的 DataGrid 正下方。

    RIA_Order_Details

    将出现一个包含 PurchaseOrderDetails 表中的列的 DataGrid

  16. 在 XAML 视图中,查找 PurchaseOrderHeaderPurchaseOrderDetailsDataGrid 控件。

  17. 移除每个 DataGrid 中的 Width=”400” 属性,使其将填充可用宽度。

  18. 在 PurchaseOrderHeader DataGrid 的前面,添加以下 TextBlock 控件以标记数据。

    <TextBlock Text="Order Headers"></TextBlock>
    
  19. 在 PurchaseOrderDetails DataGrid 的前面,添加以下 TextBlock 控件以标记数据。

    <TextBlock Text="Order Details"></TextBlock>
    
  20. 若要限制检索的记录数,请向 DomainDataSource 控件中添加以下筛选器描述符。

    <riaControls:DomainDataSource.FilterDescriptors>
        <riaControls:FilterDescriptor PropertyPath="PurchaseOrderID" Operator="IsLessThan" Value="10"></riaControls:FilterDescriptor>
    </riaControls:DomainDataSource.FilterDescriptors>
    

    下面演示了 PurchaseOrders.xaml 的完整 XAML。

    <navigation:Page x:Class="HRApp.Views.PurchaseOrders" 
               xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
               mc:Ignorable="d"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="PurchaseOrders Page" 
               xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" 
               xmlns:my="clr-namespace:HRApp.Web" 
               xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
        <sdk:Page.Resources>
            <CollectionViewSource x:Key="purchaseOrderHeaderPurchaseOrderDetailsViewSource" Source="{Binding Path=Data.PurchaseOrderDetails, ElementName=purchaseOrderHeaderDomainDataSource}" />
        </sdk:Page.Resources>
        <Grid x:Name="LayoutRoot">
            <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource 
              PageScrollViewerStyle}" >
                <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
    
                    <TextBlock Text="Purchase Orders" Style="{StaticResource HeaderTextStyle}"/>
                    <riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:PurchaseOrderHeader, CreateList=true}" Height="0" LoadedData="purchaseOrderHeaderDomainDataSource_LoadedData_1" Name="purchaseOrderHeaderDomainDataSource" QueryName="GetPurchaseOrderHeadersQuery" Width="0">
                        <riaControls:DomainDataSource.DomainContext>
                            <my:OrganizationContext />
                        </riaControls:DomainDataSource.DomainContext>
                        <riaControls:DomainDataSource.FilterDescriptors>
                            <riaControls:FilterDescriptor PropertyPath="PurchaseOrderID" Operator="IsLessThan" Value="10"></riaControls:FilterDescriptor>
                        </riaControls:DomainDataSource.FilterDescriptors>
                    </riaControls:DomainDataSource>
                    <TextBlock Text="Order Headers"></TextBlock>
                    <sdk:DataGrid AutoGenerateColumns="False" Height="200" ItemsSource="{Binding ElementName=purchaseOrderHeaderDomainDataSource, Path=Data}" Name="purchaseOrderHeaderDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected">
                        <sdk:DataGrid.Columns>
                            <sdk:DataGridTextColumn x:Name="employeeIDColumn" Binding="{Binding Path=EmployeeID}" Header="Employee ID" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="freightColumn" Binding="{Binding Path=Freight}" Header="Freight" Width="SizeToHeader" />
                            <sdk:DataGridTemplateColumn x:Name="modifiedDateColumn" Header="Modified Date" Width="SizeToHeader">
                                <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                    <DataTemplate>
                                        <sdk:DatePicker SelectedDate="{Binding Path=ModifiedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <sdk:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=ModifiedDate, StringFormat=\{0:d\}}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellTemplate>
                            </sdk:DataGridTemplateColumn>
                            <sdk:DataGridTemplateColumn x:Name="orderDateColumn" Header="Order Date" Width="SizeToHeader">
                                <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                    <DataTemplate>
                                        <sdk:DatePicker SelectedDate="{Binding Path=OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <sdk:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=OrderDate, StringFormat=\{0:d\}}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellTemplate>
                            </sdk:DataGridTemplateColumn>
                            <sdk:DataGridTextColumn x:Name="purchaseOrderIDColumn" Binding="{Binding Path=PurchaseOrderID, Mode=OneWay}" Header="Purchase Order ID" IsReadOnly="True" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="revisionNumberColumn" Binding="{Binding Path=RevisionNumber}" Header="Revision Number" Width="SizeToHeader" />
                            <sdk:DataGridTemplateColumn x:Name="shipDateColumn" Header="Ship Date" Width="SizeToHeader">
                                <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                    <DataTemplate>
                                        <sdk:DatePicker SelectedDate="{Binding Path=ShipDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, TargetNullValue=''}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <sdk:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=ShipDate, StringFormat=\{0:d\}}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellTemplate>
                            </sdk:DataGridTemplateColumn>
                            <sdk:DataGridTextColumn x:Name="shipMethodIDColumn" Binding="{Binding Path=ShipMethodID}" Header="Ship Method ID" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="statusColumn" Binding="{Binding Path=Status}" Header="Status" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="subTotalColumn" Binding="{Binding Path=SubTotal}" Header="Sub Total" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="taxAmtColumn" Binding="{Binding Path=TaxAmt}" Header="Tax Amt" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="totalDueColumn" Binding="{Binding Path=TotalDue}" Header="Total Due" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="vendorIDColumn" Binding="{Binding Path=VendorID}" Header="Vendor ID" Width="SizeToHeader" />
                        </sdk:DataGrid.Columns>
                    </sdk:DataGrid>
                    <TextBlock Text="Order Details"></TextBlock>
                    <sdk:DataGrid AutoGenerateColumns="False" Height="200" ItemsSource="{Binding Source={StaticResource purchaseOrderHeaderPurchaseOrderDetailsViewSource}}" Name="purchaseOrderDetailsDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected">
                        <sdk:DataGrid.Columns>
                            <sdk:DataGridTemplateColumn x:Name="dueDateColumn" Header="Due Date" Width="SizeToHeader">
                                <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                    <DataTemplate>
                                        <sdk:DatePicker SelectedDate="{Binding Path=DueDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <sdk:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=DueDate, StringFormat=\{0:d\}}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellTemplate>
                            </sdk:DataGridTemplateColumn>
                            <sdk:DataGridTextColumn x:Name="lineTotalColumn" Binding="{Binding Path=LineTotal}" Header="Line Total" Width="SizeToHeader" />
                            <sdk:DataGridTemplateColumn x:Name="modifiedDateColumn1" Header="Modified Date" Width="SizeToHeader">
                                <sdk:DataGridTemplateColumn.CellEditingTemplate>
                                    <DataTemplate>
                                        <sdk:DatePicker SelectedDate="{Binding Path=ModifiedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellEditingTemplate>
                                <sdk:DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Path=ModifiedDate, StringFormat=\{0:d\}}" />
                                    </DataTemplate>
                                </sdk:DataGridTemplateColumn.CellTemplate>
                            </sdk:DataGridTemplateColumn>
                            <sdk:DataGridTextColumn x:Name="orderQtyColumn" Binding="{Binding Path=OrderQty}" Header="Order Qty" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="productIDColumn" Binding="{Binding Path=ProductID}" Header="Product ID" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="purchaseOrderDetailIDColumn" Binding="{Binding Path=PurchaseOrderDetailID}" Header="Purchase Order Detail ID" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="purchaseOrderIDColumn1" Binding="{Binding Path=PurchaseOrderID}" Header="Purchase Order ID" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="receivedQtyColumn" Binding="{Binding Path=ReceivedQty}" Header="Received Qty" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="rejectedQtyColumn" Binding="{Binding Path=RejectedQty}" Header="Rejected Qty" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="stockedQtyColumn" Binding="{Binding Path=StockedQty}" Header="Stocked Qty" Width="SizeToHeader" />
                            <sdk:DataGridTextColumn x:Name="unitPriceColumn" Binding="{Binding Path=UnitPrice}" Header="Unit Price" Width="SizeToHeader" />
                        </sdk:DataGrid.Columns>
                    </sdk:DataGrid>
                </StackPanel>
            </ScrollViewer>
        </Grid>
    </navigation:Page>
    
  21. 运行应用程序并单击**“采购订单”**链接。

  22. 选择位于 PurchaseOrderHeader DataGrid 中的不同的记录。

    请注意,将自动显示相关的 PurchaseOrderDetail 记录。

    RIA_Details_and_Header

后续步骤

本演练为您展示了 RIA Services 中的许多功能。若要了解特定区域的详细信息,请参见本文档中的其他演练。

另请参见

任务

演练:在 Silverlight 业务应用程序中显示数据
演练:将身份验证服务用于 Silverlight 业务应用程序