65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (18投票s)

2003年10月29日

5分钟阅读

viewsIcon

213164

downloadIcon

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 用户控件负责两件事:

  1. 动态生成 Tab Strip 的内容和属性
  2. 响应用户点击控件时触发的事件。

动态生成 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。

© . All rights reserved.