数据驱动的选项卡条用户控件






4.68/5 (18投票s)
2003年10月29日
5分钟阅读

213164

2187
使用可重用、外部驱动的 Tab Strip 控件,缩短实现时间。
目录
引言
最近,我开始涉足 ASP.NET,并开发了几个由外部 XSD/XML 文件对驱动的用户控件和窗体绑定算法。本文介绍了我开发的其中一个控件(实际上是最简单的那个),即 TabStrip。本文介绍的 TabStrip 与 Internet Explorer WebControls 中的 TabStrip 控件相同,该控件是微软支持的 ASP.NET 服务器端控件集合的一部分,更多信息可在此处讨论。我所做的唯一改动就是使其支持数据驱动。
为什么要这样做? 因为我很快就意识到:
- 每次想要一个 Tab Strip 时,都要定义所有这些 Tab,这会让我非常恼火;
- 在开发过程中,当 Tab Strip 发生变化时,我也不想频繁地修改代码;
- 为每个 Tab Strip 编写单一解决方案的事件处理程序,以及编写通用的单一解决方案代码,都不是我的风格。
我开始时的方法
我开始时的方法是可行的,但外观很难看——它结合了 HTML 和 ASP.NET 链接来定义我的“Tab 栏”。
<form id="Form1" method="post" runat="server"> <asp:Table id="Table1" style="Z-INDEX: 101; LEFT: 10px; POSITION: relative; TOP: -12px" runat="server" Width="557px"> <asp:TableRow> <asp:TableCell> <a href="projects.aspx" target="main">Projects</a></asp:TableCell> <asp:TableCell> <a href="addDefect.aspx" target="main">Add</a></asp:TableCell> <asp:TableCell> <a href="updateDefect.aspx" target="main">Update</a></asp:TableCell> <asp:TableCell> <a href="searchDefect.aspx" target="main">Search</a></asp:TableCell> <asp:TableCell> <a href="reportDefect.aspx" target="main">Reports</a></asp:TableCell> <asp:TableCell> <a href="settings.aspx" target="main">Settings</a></asp:TableCell> <asp:TableCell> <asp:LinkButton ID="lbtnAdmin" Runat="server">Admin</asp:LinkButton> </asp:TableCell> <asp:TableCell> <a href="help.aspx" target="main">Help</a></asp:TableCell> <asp:TableCell> <asp:LinkButton ID="lbtnLogout" Runat="server">Log Out</asp:LinkButton> </asp:TableCell> </asp:TableRow> </asp:Table> </form>这看起来像这样:
我最终得到的结果
在整合了 WebControls TabStrip 后,HTML
<%@ Page language="c#" Codebehind="tabStrip.aspx.cs" AutoEventWireup="false" Inherits="DHPoc.tabStrip" %> <%@ Register TagPrefix="uc1" TagName="ucTabStrip" Src="ucTabStrip.ascx" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <title>tabStrip</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name=vs_defaultClientScript content="JavaScript"> <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> <uc1:ucTabStrip id=UcTabStrip1 runat="server" TabStrip="Main"></uc1:ucTabStrip> </form> </body> </HTML>
可以生成一个漂亮的 Tab Strip(不包括我保留为链接按钮的管理员和登出按钮)。
用户控件
Tab Strip 用户控件负责两件事:
- 动态生成 Tab Strip 的内容和属性
- 响应用户点击控件时触发的事件。
动态生成 Tab Strip 内容
首先,我们需要创建一个用户控件,我将其命名为“ucTabStrip”。这个用户控件只是一个框架。
<%@ Register TagPrefix="ie" Namespace="Microsoft.Web.UI.WebControls" Assembly="Microsoft.Web.UI.WebControls" %> <%@ Control Language="c#" AutoEventWireup="false" Codebehind="ucTabStrip.ascx.cs" Inherits="DHPoc.ucTabStrip" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> <p> <ie:TabStrip id="tsMain" runat="server" AutoPostback=true></ie:TabStrip> </p>再简单不过了。在服务器端,控件使用 XPath 从 XML 文件中提取信息。所需文件之一是架构 (XSD) 定义,其结构如下:
使用我的XSD 编辑器和XML 编辑器,可以轻松定义此架构并开始将数据输入 XML 文件。
实现再次变得非常简单。几个数据表被用来管理 Tab 信息和样式设置。
public class ucTabStrip : System.Web.UI.UserControl { protected Microsoft.Web.UI.WebControls.TabStrip tsMain; private DataTable dtTabs; private DataTable dtDefaultStyle; private DataTable dtHoverStyle; private DataTable dtSelectedStyle; private DataTable dtSeparatorStyle; private DataTable dtDim; ...
动态内容是在 Page_Load
过程中为非回发页面加载而生成的。
private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { CreateDynamicContent(); } }
使用两个函数来加载表:
GetTabInfo
void GetTabInfo() { string tsName=this.Attributes["TabStrip"]; string configFilePath= System.Configuration.ConfigurationSettings.AppSettings["configFilePath"]; dtTabs=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/Tab/*", "*"); }
和
GetStyleInfo
void GetStyleInfo() { string tsName=this.Attributes["TabStrip"]; string configFilePath= System.Configuration.ConfigurationSettings.AppSettings["configFilePath"]; dtDefaultStyle=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/DefaultStyle/*", "*"); dtHoverStyle=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/HoverStyle/*", "*"); dtSelectedStyle=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/SelectedStyle/*", "*"); dtSeparatorStyle=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/SeparatorStyle/*", "*"); dtDim=Xml.GetTable( configFilePath+"UCTabStrip.xsd", configFilePath+"UCTabStrip.xml", "//TabStrip[Name=\""+tsName+"\"]/TabWidth | "+ "//TabStrip[Name=\""+tsName+"\"]/Height", "*"); }
是的,这些可以稍微优化一下,这样我们就不必为每次查询 XML 数据而不断地读取 XSD 文件了。
这个 Xml.GetTable
是什么?它是一个静态函数,是我在许多项目中使用的帮助库的一部分,用于将 XPath 查询返回到一个格式良好的 DataTable
中。它超出了本文的范围,所以你可以查看代码来了解它是如何工作的。
请注意,web.config 文件用于指定 XSD 和 XML 文件的路径,示例如下:
<appSettings> ... <add key="configFilePath" value="c:\projects.net\defectHunter\" /> ... </appSettings>
同时,请注意“TabStrip”属性的使用。我将在关于使用 Tab Strip 控件的部分讨论这一点。
最后,动态内容被创建。
private void CreateDynamicContent() { GetTabInfo(); GetStyleInfo(); tsMain.TabDefaultStyle.Add("text-align", "center"); tsMain.TabDefaultStyle.Add("border-style", "solid"); tsMain.TabDefaultStyle.Add("color", dtDefaultStyle.Rows[0]["TextColor"].ToString()); tsMain.TabDefaultStyle.Add("background-color", dtDefaultStyle.Rows[0]["BackColor"].ToString()); tsMain.TabDefaultStyle.Add("border-color", dtDefaultStyle.Rows[0]["BorderColor"].ToString()); tsMain.TabDefaultStyle.Add("border-width", dtDefaultStyle.Rows[0]["BorderWidth"].ToString()); tsMain.TabDefaultStyle.Add("width", dtDim.Rows[0]["TabWidth"].ToString()); tsMain.TabDefaultStyle.Add("height", dtDim.Rows[0]["Height"].ToString()); tsMain.TabHoverStyle.Add("color", dtHoverStyle.Rows[0]["TextColor"].ToString()); tsMain.TabHoverStyle.Add("background-color", dtHoverStyle.Rows[0]["BackColor"].ToString()); tsMain.TabHoverStyle.Add("border-color", dtHoverStyle.Rows[0]["BorderColor"].ToString()); tsMain.TabHoverStyle.Add("border-width", dtHoverStyle.Rows[0]["BorderWidth"].ToString()); tsMain.TabSelectedStyle.Add("border-bottom", "none"); tsMain.TabSelectedStyle.Add("color", dtSelectedStyle.Rows[0]["TextColor"].ToString()); tsMain.TabSelectedStyle.Add("background-color", dtSelectedStyle.Rows[0]["BackColor"].ToString()); tsMain.TabSelectedStyle.Add("border-color", dtSelectedStyle.Rows[0]["BorderColor"].ToString()); tsMain.TabSelectedStyle.Add("border-width", dtSelectedStyle.Rows[0]["BorderWidth"].ToString()); tsMain.SepDefaultStyle.Add("border-style", "solid"); tsMain.SepDefaultStyle.Add("border-top", "none"); tsMain.SepDefaultStyle.Add("border-left", "none"); tsMain.SepDefaultStyle.Add("border-right", "none"); tsMain.SepDefaultStyle.Add("color", dtSeparatorStyle.Rows[0]["TextColor"].ToString()); tsMain.SepDefaultStyle.Add("background-color", dtSeparatorStyle.Rows[0]["BackColor"].ToString()); tsMain.SepDefaultStyle.Add("border-color", dtSeparatorStyle.Rows[0]["BorderColor"].ToString()); tsMain.SepDefaultStyle.Add("border-width", dtSeparatorStyle.Rows[0]["BorderWidth"].ToString()); foreach (DataRow dr in dtTabs.Rows) { string tabType=dr["TabStyle"] as string; string text=dr["Text"] as string; switch(tabType) { case "Tab": { Microsoft.Web.UI.WebControls.Tab tab= new Microsoft.Web.UI.WebControls.Tab(); tab.Text=text; tsMain.Items.Add(tab); break; } case "Separator": { Microsoft.Web.UI.WebControls.TabSeparator sep= new Microsoft.Web.UI.WebControls.TabSeparator(); tsMain.Items.Add(sep); break; } } } }
不,没有 try-catch 块或其他任何形式的错误检测。ASP.NET 在告诉你 XML 定义出了什么问题方面做得很好,所以我真的认为没有这个必要。一旦 XML 定义正确,它将保持正确,直到你更改它。另外,我也没有实现 Tab Strip 控件支持的所有属性。我不想在指定每个属性时过于纠结,因为实际上我只需要颜色属性。其余的由用户控件预设,因此我的所有 ASP.NET 应用程序都具有一致的外观和感觉。如果你想要额外的属性,创建 XSD 中的元素并通过 C# 代码解析它们是非常简单的。
响应 Tab 选择事件
响应 Tab 选择事件实际上有点复杂,因为 Tab Strip 返回的是所选 Tab 的索引,而 XML 文件枚举了一系列 Tab 对象,包括 Tab 分隔符。因此,要确定哪个 Tab 被选中,只计算 Tab 组件,如下面的代码所示:
private void SelectedIndexChange(object sender, EventArgs e) { GetTabInfo(); string response=""; int n=0; foreach (DataRow dr in dtTabs.Rows) { if (dr["TabStyle"].ToString() != "Tab") { continue; } if (tsMain.SelectedIndex==n) { response=dtTabs.Columns.Contains("Response") ? (string)dr["Response"] : ""; break; } ++n; } Response.Write("<script>"+response+"</script>"); } }
现在,这是一个“Marc 需要的解决方案”——用一个响应字符串来响应。虽然灵活(例如,你可以调用 JavaScript 函数或执行一些内联 JavaScript 代码),但这可能不是你需要的。
使用 Tab Strip 用户控件
要将 Tab Strip 添加到你的窗体,只需添加两行。第一行指定了用户控件引用,通常放在 aspx 文件的顶部:
<%@ Register TagPrefix="uc1" TagName="ucTabStrip" Src="ucTabStrip.ascx" %>
第二行在你的窗体中指定你的 Tab Strip:
<uc1:ucTabStrip id=UcTabStrip1 runat="server" TabStrip="Main"> </uc1:ucTabStrip>
请注意“TabStrip”属性。此属性的值是用于访问 XML 文件中特定 Tab Strip 属性信息的索引器。在上面的行中,XML 文件有一个名为“Main”的 Tab Strip,它出现在 Tab Strip 定义中,如下所示:
就是这样!要将 XML 驱动的 Tab Strip 添加到你的窗体,就只需要这些。
下载
下载包含以下文件:
- xml.cs - XML XPath 查询解析器
- ucTabStrip.aspx - HTML 定义
- ucTabStrip.aspx.cs - 后台代码
- ucTabStrip.xsd - XSD 文件
- ucTabStrip.xml - 示例 XML 文件
未包含 WebControl 扩展。如果你还没有使用它,请从 Microsoft 下载并按照安装说明进行操作(实际上并不容易)。
此外,你可以随意重命名命名空间以符合你的偏好。
关于 DefectHunter 项目
这个用户控件通过 XML 规范动态创建 Tab,并与 Microsoft 的 WebControls TabStrip 控件配合使用,作为实现基于 Web 的缺陷跟踪应用程序的更大努力的一部分,该应用程序与 Anders Molin 一起开发。该项目最初作为“Code Project Project”启动,后来移出了 CP,然后由于缺乏参与而基本上停滞了。Anders 和我正在我们自己的项目上继续开发这个项目。DH 被定位为一个开源项目,任何有兴趣贡献的人都可以联系我和 Anders。