神仙?妖怪?谢谢!

Just do it...

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

演练:在Silverlight Business应用程序中使用身份验证服务
Silverlight Business应用程序模板创建的解决方案自动允许身份验证(验证模式为Forms),角色和个性化功能。解决方案包含了数据表格来登录已经存在的用户和注册新用户。我们不用写额外的代码就可以使用这些特性。我们也可以通过定义角色、个性化属性来自定义解决方案。

在本演练中,我们将学习如何在Silverlight Business应用程序中使用身份验证、角色和个性化功能。我们将根据用户的凭证来限制对某些域操作的访问,并根据用户的偏好来定制用户界面。我们将使用ASP.NET网站管理工具来管理角色和用户。

 

创建网站、用户、角色
我们可以使用 Silverlight Business应用程序模板提供的特性,来快速实施验证功能。在下面的章节,我们使用ASP.NET配置工具来创建用户和角色,并登陆此用户。我们还通过Silverlight Business应用程序提供的注册表格来注册新用户。

   1.  在Visual Studio中,选择"文件->新建->项目"."新建项目"对话框打开。
   2.  选择Silverlight项目类型。
   3.  选择Silverlight Business Application模板,并命名为ExampleBusinessApplication.
   
   4.  点击"OK".注意创建的项目结构。这个SL客户端项目中在Views文件夹中包含了SL页面。这些页面允许登陆用户和注册新用户。
   5.  打开ASP.NET 网站管理工具。(首先,在资源管理器中,选择服务端项目,然后打开ASP.NET配置工具。
   6.  在项目菜单中,选择ASP.NET Configuration.如果在项目菜单中,看不到ASP.NET Configuration选项,有可能是选择了客户端项目。
  
   7. 在ASP.NET网站管理工具中选择"安全"标签。
  
   8.  在"角色"的部分,点击"创建或管理角色"链接。
   9.  添加一个名为Managers的角色,并点击"添加角色"按钮。
  
   10.  在右下角,点击"返回"按钮。
   11.  在“用户"的部分,点击"创建用户"按钮。
   12.  使用下面的值来创建新用户,并选择Managers角色复选框。
    - User Name : CustomerManager
    - Password : P@ssword
    - E-mail : someone@example.com
    - Security Question : Favorite color?
    - Security Answer : Blue
    - Managers role : selected
   13.  点击"创建用户"按钮。
   14.  关闭ASP.NET网站管理工具。
   15.  运行解决方案。应用程序的首页将会显示在web浏览器中。
   16.  在页面的右上角,点击”登陆"链接。登陆窗口将会出现。
   17.  为用户名称输入CustomerManager,为密码输入p@ssword,并点击"OK"按钮。
  
   现在我们就登陆这个用户了,注意到在页面右上角出现文本"Welcome CustomerManager"。
   18.  点击"登出"按钮。这时我们就不再以CustomerManager登陆了。
   下面的步骤,我们通过注册表格创建一个新用户。
   19.  再次点击"登陆"链接。
   20.  在登陆对话框中,点击"现在就注册"链接。注册表格就会出现了。
   21.  用下面的信息填充注册表。
        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.  点击"OK",创建一个新用户。注意,我们现在已经作为SalesUser登陆了。
   23.  关闭浏览器。
   24.  打开ASP.NET网站管理工具,点击"安全"标签。注意到已经有了两个用户和角色,即使我们只创建了一个角色。
   25.  点击“创建或管理角色”,注意到Managers和Registered Users角色。Registered User角色是Business应用程序模板自动生成的。
  
   26.  对"Registered Users",点击"管理"链接。注意,通过应用程序添加的名为SalesUser的用户已经在Registered Users角色中了。
   27.  关闭ASP.NET网站管理工具。

 

定义访问权限和个性化属性

我们通过为域服务应用 RequiresAuthenticationAttribute和RequiresRoleAttribute属性,来限制对域服务的访问。如果域操作没有属性,对所有用户有效。在域操作上添加属性,并不能阻止用户调用域操作。只是没有所需凭证的用户会收到一个异常。

 

根据角色限制显示数据
   1.  在资源管理器中,在服务端项目中,点击App_Data文件夹,选择"添加->已存在项"。
   2.  在"添加已存在项"对话框中,添加AdventureWorksLT示例数据库。
   3.  在服务端,添加一个新项,并选择ADO.NET Entity Data Model模板。
   4.  命名模型为AdventureWorksModel.edmx,并点击"添加"."实体数据模型向导"将会出现。
   5.  选择"从数据库生成"选项,并点击"下一步".
   6.  选择AdventureWorksLT数据库,并点击"下一步"。
   7.  从数据库对象列表中,选择Customer,Product,以及SalesOrderHeader表,然后点击"完成"。实体数据模型将会出现在设计器中。
   8.  生成解决方案。
   9.  在服务端,添加一个新项,并选择Domain Service Class模板。
   10.  命名为AdventureWorksDomainService,然后点击"添加"。
   11.  在"添加新域服务类"对话框中,选择Customer,Product和SalesOrderHeader实体。
  
   12.  点击"OK"以完成创建域服务。
   13.  在AdventureWorksDomainService类中,对GetSalesOrderHeader方法添加 RequiresAuthenticationAttribute属性。

1 [RequiresAuthentication()]   
2 public IQueryable<salesorderheader> GetSalesOrderHeaders()   
3 {   
4     return this.ObjectContext.SalesOrderHeaders;   
5 

 

   14.  对GetCustomers方法添加RequiresRoleAttribute属性,并设置所需的角色为"Managers"。

1 [RequiresRole("Managers")]   
2 public IQueryable<customer> GetCustomers()   
3 {   
4     return this.ObjectContext.Customers;   
5 }

 

GetProducts 方法对所有用户都可用,GetSalesOrderHeader只对验证用户可用,GetCustomers方法只对属于Managers角色的用户可用。

代码
 1 [EnableClientAccess()]   
 2 public class AdventureWorksDomainService : LinqToEntitiesDomainService<adventureworkslt_dataentities>   
 3 {   
 4     [RequiresRole("Managers")]   
 5     public IQueryable<customer> GetCustomers()   
 6     {   
 7         return this.ObjectContext.Customers;   
 8     }   
 9     public IQueryable<product> GetProducts()   
10     {   
11         return this.ObjectContext.Products;   
12     }   
13     [RequiresAuthentication()]   
14     public IQueryable<salesorderheader> GetSalesOrderHeaders()   
15     {   
16         return this.ObjectContext.SalesOrderHeaders;   
17     }   
18 }

 

 

下面我们在Web.config文件中定义个性法属性。当我们把属性添加到服务端的用户类时,将会为客户端项目生成对应的属性。

   1.  在服务端项目,打开Web.config文件。
   2.  在元素内,添加名为DefaultRows的个性化属性。这个属性将保持用户对显示的数据数量的偏爱。

1 <profile>  
2   <properties>  
3     <add name="FriendlyName"></add>  
4     <add type="System.Int32" defaultvalue="10" name="DefaultRows"></add>  
5   </properties>
6 </profile>

 

   3.  保存Web.config文件。
   4.  在服务端项目中,展开Models文件夹。
   5.  打开User.cs或User.vb文件,添加名为DefaultRows属性。

代码
 1 namespace ExampleBusinessApplication.Web   
 2 {   
 3     using System.Runtime.Serialization;   
 4     using System.Web.Ria.ApplicationServices;   
 5     public partial class User : UserBase   
 6     {   
 7         [DataMember]   
 8         public string FriendlyName { getset; }   
 9         public int DefaultRows { getset; }   
10     }   
11 }

 

 

从客户端使用身份验证服务
在调用有限制权限的域操作时,我们应该对用户检查所需的凭证。否则,将会抛出一个异常。在下面的部分,我们将检查用户的凭证,并根据用户的凭证来填充一个到三个DataGrid控件。我们会根据用户个性化中的属性来得到显示的记录数量。对没有验证的用户,默认的值是10。这部分没有让用户更改DefaultRows个性化属性的方式,不过在以后的部分会添加这个方式。

 

添加一个 Silverlight页面来显示数据
   1.  在客户端,在Views文件夹中添加新项。
   2.  选择Silverlight Page模板,并命名为Reports.xaml。
   3.  打开MainPage.xaml文件,并名为Link2的超链接按钮下面添加一个指向Reports页面的链接。

代码
1 <rectangle x:name="Divider2" style="">  
2     <hyperlinkbutton x:name="Link3" style="" NavigateUri="/Reports" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>  
3     </hyperlinkbutton>
4 </rectangle>

 

   4.  在Assets\Resources文件夹内,打开ApplicationString.resx文件。
   5.  添加一个新的名为ReportsPageTitle的字符串资源,并设置值为Reports。
  
   6.  保存并关闭ApplicationString.resx文件。
   7.  打开Reports.xaml文件,并添加如下XAML到Grid元素内。

 

代码
1 <scrollviewer x:name="PageScrollViewer" style="">  
2     <stackpanel x:name="ContentStackPanel" style="">  
3         <textblock x:name="HeaderText" style="" Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>  
4         <textblock x:name="ContentText" style="" Text="Display reports based on user permissions"/>   
5     </stackpanel>
6 </scrollviewer>

 

   8.  拖拽三个DataGrid控件到名为ContentStackPanel的stack面板内。当我们从工具栏中拖拽DataGrid控件时,会在项目中添加一个对System.Windows.Controls.Data程序集的应用,并在页面内添加System.Windows.Controls命名空间。
   9.  命名DataGrid控件为ProductsGrid、SalesOrdersGrid、CustomerGrid。
   10.  对每个DataGrid控件,设置Margin为5.

代码
 1 <navigation:page x:class="ExampleBusinessApplication.Views.Reports">  
 2            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
 3            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
 4            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
 5            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
 6            xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  
 7            mc:Ignorable="d"  
 8            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"  
 9            d:DesignWidth="640" d:DesignHeight="480"  
10            Title="Reports Page" >  
11     <grid x:name="LayoutRoot">  
12         <scrollviewer x:name="PageScrollViewer" style="">  
13             <stackpanel x:name="ContentStackPanel" style="">  
14                 <textblock x:name="HeaderText" style="" Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>  
15                 <textblock x:name="ContentText" style="" Text="Display reports based on user permissions"/>  
16                 <data:datagrid name="ProductsGrid" margin="5"></data:datagrid>  
17                 <data:datagrid name="SalesOrdersGrid" margin="5"></data:datagrid>   
18                 <data:datagrid name="CustomersGrid" margin="5"></data:datagrid>  
19             </stackpanel>
20         </scrollviewer>
21     </grid>
22 </navigation:page>

 

   11.  打开Reports.xaml.cs或Reports.xaml.vb。
   12.  对c#,用using添加 System.Windows.Ria,System.Windows.Ria.ApplicationServices,ExampleBusinessApplication.Web, 和ExampleBusinessApplication.Resources命名空间。
   13.  创建AdventureWorksDomainService的名为context的实例,并创建一个名为numberOfRows的变量来保存检索的记录数量。

1 private AdventureWorksDomainContext context = new AdventureWorksDomainContext();   
2 int numberOfRows = 10;

 

   14.  添加一个名为LoadRestrictedReports的方法,这个方法调用GetSalesOrderHeaderQuery方法和 GetCustomerQuery方法。如果用户属于Managers角色,就显示对应的带结果的数据表格。
如果没有所需凭证的用户调用一个域操作时,域操作会返回一个异常。我们可以通过在调用域操作之前检查凭证来避免这种情况。

代码
 1 private void LoadRestrictedReports()   
 2 {   
 3     LoadOperation<salesorderheader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));   
 4     SalesOrdersGrid.ItemsSource = loadSales.Entities;   
 5     SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;   
 6     if (WebContext.Current.User.IsInRole("Managers"))   
 7     {   
 8         LoadOperation<customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));   
 9         CustomersGrid.ItemsSource = loadCustomers.Entities;   
10         CustomersGrid.Visibility = System.Windows.Visibility.Visible;   
11     }   
12     else  
13     {   
14         CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
15     }   
16 }

 

   15.  添加名为LoadReports的方法来检测是否用户已验证,如果已验证,就调用LoadRestricteReports方法。它还检索名为 DefaultRows的个性化属性,并对User对象的PropertyChanged事件添加事件处理程序。最后,对所有用户调用 GetProductsQuery方法。

代码
 1 private void LoadReports()   
 2 {   
 3     if (WebContext.Current.User.IsAuthenticated)   
 4     {   
 5         numberOfRows = WebContext.Current.User.DefaultRows;   
 6         WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);   
 7         LoadRestrictedReports();   
 8     }   
 9     else  
10     {   
11         CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
12         SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;   
13     }   
14     LoadOperation<product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));   
15     ProductsGrid.ItemsSource = loadProducts.Entities;   
16 }

 

   16.  对PropertyChanged事件添加事件处理程序,保证在DefaultRows属性改变时调用LoadReports方法。

1 void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)   
2 {   
3     if (e.PropertyName == "DefaultRows")   
4     {   
5         LoadReports();   
6     }   
7 }

 

   17.  为LoggedIn和LoggedOut事件添加事件处理方法,并根据用户凭证的状况装载或隐藏数据。

代码
1 void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)   
2 {   
3     LoadReports();   
4 }   
5 void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)   
6 {   
7     CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
8     SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;   
9 }

 

   18.  在构造函数内添加下面的代码:

代码
1 public Reports()   
2 {   
3     InitializeComponent();   
4     this.Title = ApplicationStrings.ReportsPageTitle;   
5     WebContext.Current.Authentication.LoggedIn += new System.EventHandler<authenticationeventargs>(Authentication_LoggedIn);   
6     WebContext.Current.Authentication.LoggedOut += new System.EventHandler<authenticationeventargs>(Authentication_LoggedOut);   
7     LoadReports();   
8 }

 


以下是完整代码

 

代码
 1 using System;   
 2 using System.Collections.Generic;   
 3 using System.Linq;   
 4 using System.Net;   
 5 using System.Windows;   
 6 using System.Windows.Controls;   
 7 using System.Windows.Documents;   
 8 using System.Windows.Input;   
 9 using System.Windows.Media;   
10 using System.Windows.Media.Animation;   
11 using System.Windows.Shapes;   
12 using System.Windows.Navigation;   
13 using System.Windows.Ria;   
14 using System.Windows.Ria.ApplicationServices;   
15 using ExampleBusinessApplication.Web;   
16 using ExampleBusinessApplication.Resources;   
17 namespace ExampleBusinessApplication.Views   
18 {   
19     public partial class Reports : Page   
20     {   
21         private AdventureWorksDomainContext context = new AdventureWorksDomainContext();   
22         int numberOfRows = 10;   
23         public Reports()   
24         {   
25             InitializeComponent();   
26             this.Title = ApplicationStrings.ReportsPageTitle;   
27             WebContext.Current.Authentication.LoggedIn += new System.EventHandler<authenticationeventargs>(Authentication_LoggedIn);   
28             WebContext.Current.Authentication.LoggedOut += new System.EventHandler<authenticationeventargs>(Authentication_LoggedOut);   
29             LoadReports();   
30         }   
31         private void LoadReports()   
32         {   
33             if (WebContext.Current.User.IsAuthenticated)   
34             {   
35                 numberOfRows = WebContext.Current.User.DefaultRows;   
36                 WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);   
37                 LoadRestrictedReports();   
38             }   
39             else  
40             {   
41                 CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
42                 SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;   
43             }   
44             LoadOperation<product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));   
45             ProductsGrid.ItemsSource = loadProducts.Entities;   
46         }   
47         private void LoadRestrictedReports()   
48         {   
49             LoadOperation<salesorderheader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));   
50             SalesOrdersGrid.ItemsSource = loadSales.Entities;   
51             SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;   
52             if (WebContext.Current.User.IsInRole("Managers"))   
53             {   
54                 LoadOperation<customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));   
55                 CustomersGrid.ItemsSource = loadCustomers.Entities;   
56                 CustomersGrid.Visibility = System.Windows.Visibility.Visible;   
57             }   
58             else  
59             {   
60                 CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
61             }   
62         }   
63         void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)   
64         {   
65             LoadReports();   
66         }   
67         void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)   
68         {   
69             CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;   
70             SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;   
71         }   
72         void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)   
73         {   
74             if (e.PropertyName == "DefaultRows")   
75             {   
76                 LoadReports();   
77             }   
78         }   
79     }   
80 }

 

   19.  运行解决方案。
   20.  点击Reports链接。注意到,当我们没登陆时,只有产品表显示在报告页面。
   21.  点击"登陆"链接,并以SalesUser登陆。会发现,产品和销售订单表显示出来。
  
   22.  登出并以CustomerManager登陆。会发现,三个表都显示了。
   23.  关闭web浏览器。

 

添加一个窗口来设置个性化属性
我们可以通过添加一个子窗口来允许用户编辑DefaultRows个性属性。当值改变后,我们调用SaveUser方法来把值保存到数据源。我们通过当前WebContext实例的User对象上的属性来检索当前的值。
   1.  在客户端,在Views文件夹中添加新项。
   2.  选择Silverlight Child Window模板,并命名为ProfileWindow.xaml。
  
   3.  点击"添加"按钮。
   4.  在ProfileWindow.xaml文件内,在Grid.RowDefinitions元素后添加XAML,来添加一个下拉列表选择要在报表中显示的行数。

代码
 1 <stackpanel orientation = "Horizontal" grid.row = "0" >  
 2     <textblock text = "Number of rows to display for reports: " ></textblock >  
 3     <combobox x:name = "defaultRows" height = "20" verticalalignment = "Top" >  
 4         <comboboxitem content = "1" ></ comboboxitem >  
 5         <comboboxitem content = "2" ></ comboboxitem >  
 6         <comboboxitem content = "3" ></ comboboxitem >  
 7         <comboboxitem content = "4" ></ comboboxitem >  
 8         <comboboxitem content = "5" ></ comboboxitem >  
 9         <comboboxitem content = "6" ></ comboboxitem >  
10         <comboboxitem content = "7" ></ comboboxitem >  
11         <comboboxitem content = "8" ></ comboboxitem >  
12         <comboboxitem content = "9" ></ comboboxitem >  
13         <comboboxitem content = "10" ></ comboboxitem >  
14         <comboboxitem content = "15" ></ comboboxitem >  
15         <comboboxitem content = "20" ></ comboboxitem >  
16     </combobox >  
17 </stackpanel >

 

   5.  设置ChildWindow的Title属性为Select References。
   6.  在ProfileWindow.xaml.cs中,添加下面的代码来检索和设置个性化属性。

 

代码
 1 public partial class ProfileWindow : ChildWindow  
 2 {  
 3     public ProfileWindow()  
 4     {  
 5         InitializeComponent();  
 6         string userDefaultRows = WebContext.Current.User.DefaultRows.ToString();  
 7         foreach (ComboBoxItem cbi in defaultRows.Items)  
 8         {  
 9             if (cbi.Content.ToString() == userDefaultRows)  
10             {  
11                 defaultRows.SelectedItem = cbi;  
12                 break ;  
13             }  
14         }  
15     }  
16 
17     private void OKButton_Click( object sender, RoutedEventArgs e)  
18     {  
19         int newSelection = int .Parse(defaultRows.SelectionBoxItem.ToString());  
20         if (newSelection != WebContext.Current.User.DefaultRows)  
21         {  
22             WebContext.Current.User.DefaultRows = newSelection;  
23             WebContext.Current.Authentication.SaveUser( true );  
24         }  
25         this .DialogResult = true ;  
26     }  
27 
28     private void CancelButton_Click( object sender, RoutedEventArgs e)  
29     {  
30         this .DialogResult = false ;  
31     } 
32 }

 

    7.  如果是VB,添加Imports命令引用System.Windows.Controls和System.Windows命名空间。
    8.  展开Views\Login文件夹,并打开LoginStatus.xaml文件。
    9.  添加一个指向profile文件的设置链接,在Logout按钮前添加下面的XAML  

代码
1 < id="highlighter_750424_clipboard" title="copy to clipboard" type="application/x-shockwave-flash" width="16" height="16" src="http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf" id="highlighter_750424_clipboard" type="application/x-shockwave-flash" title="copy to clipboard" allowscriptaccess="always" wmode="transparent" flashvars="highlighterId=highlighter_750424" menu="false" src="http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf" height="16" width="16">
2 

 

1 < button x:name = "SettingsButton" click = "SettingsButton_Click" content = "settings" style = "" margin = "0,0,0,0" >
2 </ button >  
3 < textblock text = "  |  " style = "" >  
4 </ textblock > 
5 

 

   10.  在LoginStatus.xaml.cs文件内,为设置链接添加点击事件处理方法。

 

代码
1 <id="highlighter_186072_clipboard" title="copy to clipboard" type="application/x-shockwave-flash" width="16" height="16" src="http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf" id="highlighter_186072_clipboard" type="application/x-shockwave-flash" title="copy to clipboard" allowscriptaccess="always" wmode="transparent" flashvars="highlighterId=highlighter_186072" menu="false" src="http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf" height="16" width="16">

 

 

代码
1 private void SettingsButton_Click( object sender, RoutedEventArgs e)  
2 {  
3     ExampleBusinessApplication.Views.ProfileWindow settingsWindow = new ExampleBusinessApplication.Views.ProfileWindow();  
4     settingsWindow.Show();  
5 
6 

 

   11.  运行解决方案。
   12.  以CustomerManager或SalesUser登陆,注意到在登陆状态栏,现在包含一个"设置"的链接。
   
   13.  点击"设置"链接,并设置默认的报表显示行数。
  
   14.  打开报表页面,会注意到DataGrid现在包含我们所选的行数。

 

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/blackant2/archive/2010/04/09/5466853.aspx

posted on 2010-06-05 16:06  E.Trock  阅读(1538)  评论(2编辑  收藏  举报