生成用于从多个用户帐户中选择一个帐户的界面 (VB)

作者 :Scott Mitchell

在本教程中,我们将使用分页的可筛选网格生成用户界面。 具体而言,用户界面将包含一系列 LinkButton,用于根据用户名的起始字母筛选结果,以及用于显示匹配用户的 GridView 控件。 首先,我们将列出 GridView 中的所有用户帐户。 然后,在步骤 3 中,我们将添加筛选器 LinkButtons。 步骤 4 查看对筛选结果进行分页。 后续教程将使用步骤 2 到 4 中构造的接口来执行特定用户帐户的管理任务。

简介

向用户分配角色 教程中,我们为管理员创建了一个基本的界面,用于选择用户并管理其角色。 具体而言,界面为管理员提供了所有用户的下拉列表。 当有十几个左右的用户帐户时,这样的接口是合适的,但对于拥有数百或数千个帐户的网站来说,这种界面很不方便。 分页的可筛选网格更适合具有大量用户群的网站用户界面。

在本教程中,我们将生成这样的用户界面。 具体而言,用户界面将包含一系列 LinkButton,用于根据用户名的起始字母筛选结果,以及用于显示匹配用户的 GridView 控件。 首先,我们将列出 GridView 中的所有用户帐户。 然后,在步骤 3 中,我们将添加筛选器 LinkButtons。 步骤 4 查看对筛选结果进行分页。 后续教程将使用步骤 2 到 4 中构造的接口来执行特定用户帐户的管理任务。

让我们开始吧!

步骤 1:添加新 ASP.NET 页

在本教程和接下来的两个教程中,我们将研究各种与管理相关的功能和功能。 我们需要一系列 ASP.NET 页面来实现这些教程中介绍的主题。 让我们创建这些页面并更新站点地图。

首先在名为 Administration的项目中创建一个新文件夹。 接下来,将两个新的 ASP.NET 页添加到 文件夹,将每个页面与 Site.master 母版页链接。 为页面命名:

  • ManageUsers.aspx
  • UserInformation.aspx

此外,将两个页面添加到网站的根目录: ChangePassword.aspxRecoverPassword.aspx

此时,这四个页面应具有两个 Content 控件,每个控件分别用于母版页的 ContentPlaceHolders: MainContentLoginContent

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server"> 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="LoginContent" Runat="Server"> 
</asp:Content>

我们希望显示这些页面的 ContentPlaceHolder 的母版页的默认标记 LoginContent 。 因此,请删除 Content 控件的 Content2 声明性标记。 执行此操作后,页面的标记应仅包含一个 Content 控件。

文件夹中 ASP.NET 页 Administration 仅供管理用户使用。 我们在创建和管理角色教程中向系统添加了管理员角色;将对这两个页面的访问权限限制为此角色。 为此,请将文件 Web.config 添加到 文件夹, Administration 并将其元素配置为 <authorization> 允许具有管理员角色的用户并拒绝所有其他用户。

<?xml version="1.0"?> 
<configuration> 
 <system.web> 
 <authorization> 
 <allow roles="Administrators" /> 
 <deny users="*"/> 
 </authorization> 
 </system.web> 
</configuration>

此时,项目解决方案资源管理器应类似于图 1 中所示的屏幕截图。

已将四个新页面和一个Web.config文件添加到网站

图 1:已将四个新页面和一个 Web.config 文件添加到网站 (单击以查看全尺寸图像)

最后,更新站点地图 (Web.sitemap) 以包含页面条目 ManageUsers.aspx 。 在为角色教程添加的 后面 <siteMapNode> 添加以下 XML。

<siteMapNode title="User Administration" url="~/Administration/ManageUsers.aspx"/>

更新站点地图后,通过浏览器访问站点。 如图 2 所示,左侧的导航现在包含管理教程的项。

站点映射包括一个标题为“用户管理”的节点

图 2:站点地图包含标题为“用户管理”的节点 (单击以查看全尺寸图像)

步骤 2:列出 GridView 中的所有用户帐户

本教程的最终目标是创建一个分页的可筛选网格,管理员可以通过该网格选择要管理的用户帐户。 首先,在 GridView 中列出 所有用户 。 完成此操作后,我们将添加筛选和分页接口和功能。

ManageUsers.aspx打开 文件夹中的页面Administration并添加 GridView,将其ID设置为 UserAccounts “稍后,我们将编写代码,使用 Membership 类的 GetAllUsers 方法将用户帐户集绑定到 GridView。 如前面的教程中所述, GetAllUsers 方法返回 对象 MembershipUserCollection ,它是 对象的集合 MembershipUserMembershipUser集合中的每个都包含 、EmailIsApproved等属性UserName

若要在 GridView 中显示所需的用户帐户信息,请将 GridView 的 AutoGenerateColumns 属性设置为 False,并为 UserNameEmailComment 属性添加 BoundFields,并为 IsApprovedIsLockedOutIsOnline 属性添加 CheckBoxFields。 可以通过控件的声明性标记或通过“字段”对话框应用此配置。 图 3 显示了取消选中“自动生成字段”复选框并添加和配置 BoundFields 和 CheckBoxFields 后“字段”对话框的屏幕截图。

将三个 BoundFields 和三个 CheckBoxField 添加到 GridView

图 3:将三个绑定字段和三个 CheckBoxField 添加到 GridView (单击以查看全尺寸图像)

配置 GridView 后,请确保其声明性标记如下所示:

<asp:GridView ID="UserAccounts" runat="server" AutoGenerateColumns="False">
 <Columns>
 <asp:BoundField DataField="UserName" HeaderText="UserName" />
 <asp:BoundField DataField="Email" HeaderText="Email" />
 <asp:CheckBoxField DataField="IsApproved" HeaderText="Approved?" />
 <asp:CheckBoxField DataField="IsLockedOut" HeaderText="Locked Out?" />
 <asp:CheckBoxField DataField="IsOnline" HeaderText="Online?" />
 <asp:BoundField DataField="Comment" HeaderText="Comment" />
 </Columns>
</asp:GridView>

接下来,我们需要编写将用户帐户绑定到 GridView 的代码。 创建名为 BindUserAccounts 的方法以执行此任务,然后在第一次访问页面时从 Page_Load 事件处理程序调用它。

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 If Not Page.IsPostBack Then
 BindUserAccounts()
 End If
End Sub

Private Sub BindUserAccounts()
 UserAccounts.DataSource = Membership.GetAllUsers()
 UserAccounts.DataBind()
End Sub

花点时间通过浏览器测试页面。 如图 4 所示, UserAccounts GridView 列出了系统中所有用户的用户名、电子邮件地址和其他相关帐户信息。

用户帐户在 GridView 中列出

图 4:用户帐户列在 GridView (单击以查看全尺寸图像)

步骤 3:按用户名的首字母筛选结果

目前, UserAccounts GridView 显示 所有 用户帐户。 对于具有数百或数千个用户帐户的网站,用户必须能够快速缩减显示的帐户。 这可以通过将筛选 LinkButton 添加到页面来实现。 让我们向页面添加 27 个 LinkButtons:一个标题为“全部”,并为字母表的每个字母添加一个 LinkButton。 如果访问者单击“所有链接”按钮,GridView 将显示所有用户。 如果他们单击特定字母,则仅显示用户名以所选字母开头的用户。

我们的第一个任务是添加 27 个 LinkButton 控件。 一种选择是以声明方式创建 27 个 LinkButton,一次创建一个。 更灵活的方法是将 Repeater 控件与 一起使用 ItemTemplate ,该控件呈现 LinkButton,然后将筛选选项作为 String 数组绑定到 Repeater。

首先,将 Repeater 控件添加到 GridView 上方的页面 UserAccounts 。 将 Repeater 的 ID 属性设置为 FilteringUI 配置 Repeater 的模板,以便其 ItemTemplate 呈现一个 LinkButton,其 TextCommandName 属性绑定到当前数组元素。 正如我们在向用户分配角色教程中看到的那样,可以使用数据绑定语法完成Container.DataItem此操作。 使用 Repeater 的 SeparatorTemplate 显示每个链接之间的垂直线。

<asp:Repeater ID="FilteringUI" runat="server">
 <ItemTemplate>
 <asp:LinkButton runat="server" ID="lnkFilter" 
 Text='<%# Container.DataItem %>'
 CommandName='<%# Container.DataItem %>'></asp:LinkButton>
 </ItemTemplate>
 <SeparatorTemplate>|</SeparatorTemplate>
</asp:Repeater>

若要使用所需的筛选选项填充此 Repeater,请创建一个名为 BindFilteringUI的方法。 请确保在第一页加载时从 Page_Load 事件处理程序调用此方法。

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 If Not Page.IsPostBack Then
 BindUserAccounts()
 BindFilteringUI()
 End If
End Sub

Private Sub BindFilteringUI()
 Dim filterOptions() As String = {"All", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
 FilteringUI.DataSource = filterOptions
 FilteringUI.DataBind()
End Sub

此方法将筛选选项指定为数组filterOptions中的String元素 对于数组中的每个元素,Repeater 将呈现一个 LinkButton,其 TextCommandName 属性分配给数组元素的值。

图 5 显示了 ManageUsers.aspx 通过浏览器查看时的页面。

The Repeater Lists 27 Filtering LinkButtons

图 5:Repeater 列出了 27 个筛选链接按钮 (单击以查看全尺寸图像)

注意

用户名可以以任何字符开头,包括数字和标点符号。 若要查看这些帐户,管理员必须使用“所有 LinkButton”选项。 或者,可以添加 LinkButton 以返回以数字开头的所有用户帐户。 我留给读者做练习。

单击任何筛选 LinkButtons 会导致回发并引发 Repeater 的事件 ItemCommand ,但网格中没有变化,因为我们尚未编写任何代码来筛选结果。 类 Membership 包含一个 FindUsersByName 方法 ,该方法返回其用户名与指定搜索模式匹配的用户帐户。 我们可以使用此方法仅检索其用户名以单击的已筛选 LinkButton 的 指定的字母开头 CommandName 的用户帐户。

首先更新 ManageUser.aspx 页面的代码隐藏类,使其包含一个名为 UsernameToMatch 此属性的属性,可跨回发保留用户名筛选器字符串:

Private Property UsernameToMatch() As String
 Get
 Dim o As Object = ViewState("UsernameToMatch")
 If o Is Nothing Then
 Return String.Empty
 Else
 Return o.ToString()
 End If
 End Get
 Set(ByVal Value As String)
 ViewState("UsernameToMatch") = Value
 End Set
End Property

属性 UsernameToMatch 使用键“UsernameToMatch”将其赋值存储到 ViewState 集合中。 读取此属性的值时,它会检查集合中 ViewState 是否存在值;如果不存在,则返回默认值,即空字符串。 属性 UsernameToMatch 呈现一种常见模式,即保留值以查看状态,以便跨回发保留对属性所做的任何更改。 有关此模式的详细信息,请阅读 了解 ASP.NET 视图状态

接下来,更新 BindUserAccounts 方法,以便它调用 Membership.FindUsersByName(而不是调用 Membership.GetAllUsers),并传入附加了 SQL 通配符 %的属性UsernameToMatch的值。

Private Sub BindUserAccounts()
 UserAccounts.DataSource = Membership.FindUsersByName(Me.UsernameToMatch &"%")
 UserAccounts.DataBind()
End Sub

若要仅显示用户名以字母 A 开头的用户,请将 属性设置为 UsernameToMatch A,然后调用 BindUserAccounts 这将导致调用 Membership.FindUsersByName("A%"),这将返回用户名以 A 开头的所有用户。同样,若要返回 所有用户 ,请向 UsernameToMatch 属性分配空字符串,以便 BindUserAccounts 方法调用 Membership.FindUsersByName("%"),从而返回所有用户帐户。

为 Repeater 的 ItemCommand 事件创建事件处理程序。 每当单击其中一个筛选器 LinkButton 时,将引发此事件;它通过 RepeaterCommandEventArgs 对象传递单击的 CommandName LinkButton 的值。 我们需要将适当的值分配给 属性, UsernameToMatch 然后调用 BindUserAccounts 方法。 如果 为 CommandName All,请将空字符串 UsernameToMatch 分配给 ,以便显示所有用户帐户。 否则,请将 CommandName 值分配给 UsernameToMatch

Protected Sub FilteringUI_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles FilteringUI.ItemCommand
 If e.CommandName = "All" Then
 Me.UsernameToMatch = String.Empty
 Else
 Me.UsernameToMatch = e.CommandName
 End If

 BindUserAccounts()
End Sub

完成此代码后,测试筛选功能。 首次访问页面时,将显示所有用户帐户, (参考图 5) 。 单击 A LinkButton 会导致回发并筛选结果,仅显示以 A 开头的那些用户帐户。

使用筛选链接按钮显示其用户名以特定字母开头的用户

图 6:使用筛选链接按钮显示其用户名以特定字母开头的用户 (单击以查看全尺寸图像)

步骤 4:更新 GridView 以使用分页

图 5 和图 6 中显示的 GridView 列出了从 FindUsersByName 方法返回的所有记录。 如果有数百或数千个用户帐户,这可能会导致在查看所有帐户 (时导致信息过载,就像单击“所有链接按钮”或最初访问页面) 时一样。 为了帮助在更易于管理的区块中显示用户帐户,让我们将 GridView 配置为一次显示 10 个用户帐户。

GridView 控件提供两种类型的分页:

  • 默认分页 - 易于实现,但效率低下。 简而言之,使用默认分页,GridView 需要来自其数据源 的所有 记录。 然后,它仅显示相应的记录页。
  • 自定义分页 - 需要执行更多工作,但比默认分页更有效,因为使用自定义分页,数据源仅返回要显示的精确记录集。

分页数千条记录时,默认分页和自定义分页之间的性能差异可能很大。 由于我们正在构建此接口,假设可能有数百或数千个用户帐户,因此让我们使用自定义分页。

注意

有关默认分页和自定义分页之间的差异以及实现自定义分页所涉及的挑战的更深入讨论,请参阅 通过大量数据高效分页。 有关默认分页和自定义分页之间的性能差异的一些分析,请参阅 ASP.NET 中的自定义分页与 SQL Server 2005

若要实现自定义分页,我们首先需要某种机制来检索 GridView 显示的记录的精确子集。 好消息是 Membership , 类的 FindUsersByName 方法具有一个重载,它允许我们指定页面索引和页面大小,并仅返回属于该记录范围内的用户帐户。

具体而言,此重载具有以下签名: FindUsersByName(usernameToMatch, pageIndex, pageSize, totalRecords)

pageIndex 参数指定要返回的用户帐户页;pageSize 指示每页要显示的记录数。 totalRecords 参数是返回ByRef用户存储中用户帐户总数的参数。

注意

返回 FindUsersByName 的数据按用户名排序;无法自定义排序条件。

GridView 可以配置为利用自定义分页,但仅当绑定到 ObjectDataSource 控件时。 要使 ObjectDataSource 控件实现自定义分页,它需要两种方法:一种方法传递起始行索引和要显示的最大记录数,并返回属于该范围内的记录的精确子集;和返回正在分页的记录总数的方法。 重 FindUsersByName 载接受页索引和页面大小,并通过 ByRef 参数返回记录总数。 因此,此处存在接口不匹配。

一种选择是创建一个代理类来公开 ObjectDataSource 所需的接口,然后在内部调用 FindUsersByName 方法。 另一个选项(也是我们将在本文中使用的选项)是创建我们自己的分页接口,并使用该接口而不是 GridView 的内置分页接口。

创建第一个、上一个、下一个、最后一个分页接口

让我们使用 First、Previous、Next 和 Last LinkButtons 生成分页接口。 单击“第一个 LinkButton”时,会将用户带到数据的第一页,而“上一页”将返回上一页。 同样,Next 和 Last 将分别将用户移动到下一页和最后一页。 在 GridView 下添加四个 UserAccounts LinkButton 控件。

<p>
 <asp:LinkButton ID="lnkFirst" runat="server">  First</asp:LinkButton> |
 <asp:LinkButton ID="lnkPrev" runat="server">  Prev</asp:LinkButton> |
 <asp:LinkButton ID="lnkNext" runat="server">Next  </asp:LinkButton> |
 <asp:LinkButton ID="lnkLast" runat="server">Last  </asp:LinkButton>
</p>

接下来,为每个 LinkButton 的事件 Click 创建事件处理程序。

图 7 显示了通过 Visual Web 开发人员设计视图查看时的四个 LinkButton。

在 GridView 下添加 First、Previous、Next 和 Last LinkButtons

图 7:在 GridView 下添加“第一个”、“上一个”、“下一个”和“最后一个链接”按钮 (单击以查看全尺寸图像)

跟踪当前页索引

当用户首次访问 ManageUsers.aspx 页面或单击其中一个筛选按钮时,我们希望在 GridView 中显示数据的第一页。 但是,当用户单击其中一个导航 LinkButton 时,我们需要更新页面索引。 若要维护每页要显示的页索引和记录数,请将以下两个属性添加到页面的代码隐藏类:

Private Property PageIndex() As Integer
 Get
 Dim o As Object = ViewState("PageIndex")
 If o Is Nothing Then
 Return 0
 Else
 Return Convert.ToInt32(o)
 End If
 End Get
 Set(ByVal Value As Integer)
 ViewState("PageIndex") = Value
 End Set
End Property

Private ReadOnly Property PageSize() As Integer
 Get
 Return 10
 End Get
End Property

与 属性一UsernameToMatchPageIndex样, 属性将其值保存到视图状态。 只读 PageSize 属性返回硬编码值 10。 我邀请感兴趣的读者更新此属性以使用与 PageIndex相同的模式,然后扩充 ManageUsers.aspx 页面,以便访问页面的人员可以指定每页显示多少个用户帐户。

仅检索当前页的记录、更新页面索引以及启用和禁用分页接口 LinkButtons

有了分页接口并 PageIndex 添加了 和 PageSize 属性,我们就可以更新 BindUserAccounts 方法,使其使用适当的 FindUsersByName 重载。 此外,我们需要让此方法根据显示的页面启用或禁用分页接口。 查看数据的第一页时,应禁用“第一页”和“上一页”链接;查看最后一页时,应禁用 Next 和 Last。

使用以下代码更新 BindUserAccounts 方法:

Private Sub BindUserAccounts()
 Dim totalRecords As Integer
 UserAccounts.DataSource = Membership.FindUsersByName(Me.UsernameToMatch + "%", Me.PageIndex, Me.PageSize, totalRecords)
 UserAccounts.DataBind()

 ' Enable/disable the paging interface
 Dim visitingFirstPage As Boolean = (Me.PageIndex = 0)
 lnkFirst.Enabled = Not visitingFirstPage
 lnkPrev.Enabled = Not visitingFirstPage

 Dim lastPageIndex As Integer = (totalRecords - 1) / Me.PageSize
 Dim visitingLastPage As Boolean = (Me.PageIndex >= lastPageIndex)
 lnkNext.Enabled = Not visitingLastPage
 lnkLast.Enabled = Not visitingLastPage
End Sub

请注意,正在分页的记录总数由 方法的最后一 FindUsersByName 个参数确定。 返回指定的用户帐户页后,将启用或禁用四个 LinkButton,具体取决于是查看数据的第一页还是最后一页。

最后一步是为四个 LinkButton 的 Click 事件处理程序编写代码。 这些事件处理程序需要更新 属性, PageIndex 然后通过调用 BindUserAccounts First、Previous 和 Next 事件处理程序将数据重新绑定到 GridView 非常简单。 Click但是,Last LinkButton 的事件处理程序有点复杂,因为我们需要确定要显示的记录数以确定最后一页索引。

Protected Sub lnkFirst_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkFirst.Click
 Me.PageIndex = 0
 BindUserAccounts()
End Sub

Protected Sub lnkPrev_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkPrev.Click
 Me.PageIndex -= 1
 BindUserAccounts()
End Sub

Protected Sub lnkNext_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkNext.Click
 Me.PageIndex += 1
 BindUserAccounts()
End Sub

Protected Sub lnkLast_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkLast.Click
 ' Determine the total number of records
 Dim totalRecords As Integer
 Membership.FindUsersByName(Me.UsernameToMatch + "%", Me.PageIndex, Me.PageSize, totalRecords)
 ' Navigate to the last page index
 Me.PageIndex = (totalRecords - 1) / Me.PageSize
 BindUserAccounts()
End Sub

图 8 和 9 显示了运行中的自定义分页接口。 图 8 显示了 ManageUsers.aspx 查看所有用户帐户的第一页数据时的页面。 请注意,仅显示 13 个帐户中的 10 个。 单击“下一个”或“最后一个”链接会导致回发,将 更新 PageIndex 为 1,并将用户帐户的第二页绑定到网格 (请参阅图 9) 。

显示前 10 个用户帐户

图 8:单击查看 全尺寸图像 (显示前 10 个用户帐户)

单击“下一个链接”显示用户帐户的第二页

图 9:单击“下一个链接”显示用户帐户的第二页 (单击以查看全尺寸图像)

总结

管理员通常需要从帐户列表中选择用户。 在前面的教程中,我们介绍了如何使用填充了用户的下拉列表,但此方法的缩放效果不佳。 在本教程中,我们探索了一个更好的替代方法:一个可筛选的接口,其结果显示在分页的 GridView 中。 使用此用户界面,管理员可以快速高效地在数千个用户帐户中查找并选择一个。

编程愉快!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

Scott Mitchell 是多本 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可在 上或通过他的博客http://ScottOnWriting.NET联系 mitchell@4guysfromrolla.com Scott。

特别感谢

本教程系列由许多有用的审阅者查看。 本教程的首席审阅者是 Alicja Maziarz。 有兴趣查看我即将发布的 MSDN 文章? 如果是这样,请在