在代码隐藏中动态构建 ASP.NET 网页





4.00/5 (11投票s)
本文介绍了一种在代码隐藏中动态创建整个页面内容的技术,用于构建 ASP.NET 网页。
引言
思考一下多年来网页是如何构建的。如果您经历过早期(约 1995 年),您可能使用文本编辑器或早期 HTML 编辑器纯粹以 HTML 构建网页。几年之内,出现了经典的 ASP 和 VBScript 等脚本语言技术。程序员开始将网页构建为大部分静态 HTML,其中包含一些 VBScript,用于构建动态或数据驱动的区域 — 但页面仍然主要是静态 HTML。然后出现了 ASP.NET,并引入了代码隐藏概念。Visual Studio 环境中的 C# 和 VB.NET 使编写无错误代码变得更加容易。现在,您的整个页面都可以通过代码以编程方式构建。那么您怎么做呢?您可能只是将经典的 ASP 编程风格带入 ASP.NET,并继续将页面构建为静态文本和代码的混合体。Visual Studio 中的“工具箱”概念进一步简化了此技术,它提供了相当强大的支持,可以将在控件放置在页面上并设置其属性 — 几乎可以像通过拖放构建整个网站一样好。
这些构建网页的方法存在于一个范围内。一端是纯静态 HTML 页面。在另一端,您可以完全通过代码构建网页,在代码隐藏中创建所有 HTML 元素。然后是介于两者之间的模糊地带,页面是静态 HTML 与脚本片段的混合体,用于构建页面的任何数据驱动部分。我敢肯定您见过很多经典的 ASP 页面,包含大量静态文本,穿插着五个、十个甚至更多的 VBScript 代码片段。这不是一个好设计,但也许是当时可用技术所能做的最好的了。
我如何采用这项技术
实际上,我构建的第一个数据驱动的网站是 1995 年为一个网页编程课程的项目。这早于 Java 和 VBScript。当时,您确实只有范围的两个端点。您可以构建纯静态 HTML 页面。但如果您想要任何数据驱动的组件到网页,您就必须通过 cgi-bin 编程(Perl 也是一个选项)以编程方式构建整个网页。不幸的是,编程 cgi-bin 非常繁琐,并且调试起来很麻烦。
我的第一个商业数据驱动的 Web 项目是用经典的 ASP 和 VBScript 编写的,使用的是 Microsoft 旧的 Visual InterDev。坦率地说,结果一团糟,几乎没怎么工作。然后它被移植到 C# 的 ASP.NET。由于不熟悉 ASP.NET,最初的端口几乎只是将脚本元素重写为 C#,而架构变化不大。我敢肯定,您许多人都见过类似的移植,就像我在其他程序员编写的其他项目中所见一样。在第二次迭代的 ASP.NET 中,我发现自己将越来越多的代码移到了代码隐藏。我开始构建自己的 ASP.NET 类库,并采用更多面向对象的技巧。多年来,我所做的大部分工作都是 C++ 和 MFC 中的 Windows 桌面应用程序,因此我熟悉面向对象的设计和丰富的类库。在这个及其他几个项目上,我发现页面逻辑越复杂,就越适合放在代码隐藏中。最后,在完成这个及其他几个项目后,我决定为新项目完全将所有代码移到代码隐藏。换句话说,我将网页编程完全移到了频谱的另一端。结果,我能够比尝试编写 HTML 时更快地构建更强大的数据驱动的 Web 应用程序。
如果这个故事的全部或部分听起来很熟悉,您可能想考虑这项技术。
本文的目的
自 cgi-bin 以来,我们已经走了很长的路。使用 C# 或 VB.NET,现在可以在代码隐藏中完全动态地构建您的网页。但是,如果您的网页编程技能随着经典 ASP 的发展而演变,那么您可能没有过多考虑这种构建网页的方法。一旦您学会了它,它实际上是一种非常简单的技术,并且具有一些巨大的优点,但它需要改变您对网页编程的看法。
本文的目的是向您介绍在代码隐藏中动态构建数据驱动网页的想法。我将开始为您构建自己的类库,以使这项技术更有效率。我将向您展示如何动态构建页面。最后,我将解释优点和缺点。
为什么要阅读本文
如果您
- 您正在规划一个新的数据驱动的 Web 项目,并正在寻找更好的技术。
- 您被卡在混乱的“经典 ASP 到 ASP.NET 移植”中,需要一些编程技术来提供一个最健壮的最终应用程序(我知道您中有很多人,因为我阅读了工作职位发布)。
- 从学术角度来看,即使您不采用这种策略,它也能拓宽您的编程技能,并增进您对网页构建的理解。
- 即使您对纯代码隐藏方法不感兴趣,也应该阅读本文,因为许多 Web 项目动态地构建页面的小部分 — 如果不是整个页面 — 并且您将希望了解这项技术,以防您遇到这样的项目。
- 这可能是您下次工作面试中的一个问题。
扩展 .NET Web 类
除非您拥有丰富的代码类库,否则在代码隐藏中动态构建网页不会极大地简化您的网站开发。基本的 ASP.NET 类令人印象深刻,但还不够。您需要在自己的库中增强它们。如果您只是尝试使用标准的 ASP.NET 类来构建页面,您将永远看不到该方法的优势。
构建自己的代码库意味着
- 此方法对于小型项目效率较低,但对于大型项目效率更高。
- 您可以创建一个自定义类库,可以在多个项目之间共享,使每个新项目都更快、更容易开发。
该项目包含一些示例类,供您入门。
TextWriter 类
我们通过代码构建网页。这意味着不再需要输入 HTML。事实上,我们希望避免输入 HTML 标签(或至少将其最小化)。您实际编写的任何 HTML 标签都应该在一个专门设计用于编写 HTML 标签的类中。这使我们可以利用 Visual Studio 的功能,如自动完成/智能感知和动态语法检查。其中一个类是 TextWriter
类。
这是该类的一部分
public class TextWriter
{
public TextWriter()
{
}
static public string MakeParagraph(string s)
{
return "<p>" + s + "</p>";
}
static public string MakeLine(string s)
{
return s + "<br />";
}
static public string MakeH1Text(string s)
{
return "<h1>" + s + "</h1>";
}
}
该类的目的只是编写 HTML 格式的文本。请注意,所有成员都是静态的。这只是文本写入器类中可能包含的函数类型的一个小样本。任何时候,当您发现自己在该类之外编写 HTML 标签时,也许是时候在该类中添加另一个函数了。
在下面的代码示例中,我们将看到这个类在起作用。
MyLiteral 类
Literal
控件只是一个包含字面 HTML 标记的控件。生成页面 HTML 时,控件的任何文本都会被字面地写入页面。当您动态构建页面时,您将使用许多 Literal。因此,派生您自己的 Literal
类是有意义的,如下所示。请注意使用构造函数来设置文本值,使控件的编程效率更高。另请注意使用 TextWriter
类。
public class MyLiteral : Literal
{
public MyLiteral(string strText)
{
Text = strText;
}
public void AddParagraph(string strText)
{
Text += TextWriter.MakeParagraph(strText);
}
}
诚然,这个扩展的 Literal
类没什么大不了的,尽管即使是这个小的类增强功能,每次使用 Literal
也能为您节省几行代码。您会发现自己为更复杂的 Web 控件添加了许多自定义功能。
MyPanel 类
在 ASP.NET 中,Label
控件生成包含在 <span> 标签中的文本。Panel
控件生成包含在 <div> 标签中的文本。为了方便处理这些控件并设置它们的 CSS 样式,您应该从这些控件派生自己的类。下面是您自己自定义 Panel
类的示例。
public class MyPanel : Panel
{
public MyPanel(string strClass)
{
this.CssClass = strClass;
}
// Add a child panel to this panel
public MyPanel AddDiv(string strClass)
{
MyPanel panel = new MyPanel(strClass);
Controls.Add(panel);
return panel;
}
// add a child panel to this panel
public MyPanel AddDiv(string strClass, string strText)
{
MyPanel panel = AddDiv(strClass);
panel.AddLiteral(strText);
return panel;
}
// Add a literal control with text to this panel
public Literal AddLiteral(string strText)
{
Literal lit = new Literal();
lit.Text = strText;
Controls.Add(lit);
return lit;
}
// Add a label control with text to this panel
public Label AddLabel(string strClass, string strText)
{
Label label = new Label();
label.Text = strText;
label.CssClass = strClass;
Controls.Add(label);
return label;
}
public void AddParagraph(string strText)
{
AddLiteral(TextWriter.MakeParagraph(strText));
}
} // end of class declaration
进一步的子类化
为了使其更有效率,您可能希望进一步子类化 MyPanel
。也许您有一个显示菜单栏的面板。您可以创建一个 MenuPanel
类,并在需要它的任何地方将其添加到您的页面中。
作为一般规则,请考虑从 System.Web.UI
的任何控件派生自己的子类,包括表格、文本框、复选框、按钮等。例如,以编程方式构建表格可能会很乏味,因为您必须反复为每个单元格和行设置属性。创建自己的表格类可以使使用表格更加容易。
“基类”
良好的面向对象设计意味着您不会一遍又一遍地编写相同的代码片段。如果您这样做,那么您的类设计就存在问题。在任何专业的、大型的、数据驱动的 Web 网站中,都将有一些代码被许多页面访问。例如,错误处理代码是许多页面共同拥有的。因此,创建一个从 System.Web.UI.Page
派生的 BasePage
类是一个好主意。然后从 BasePage
派生所有 Web 页面。
您可以对母版页和用户控件做同样的事情。此项目包含用于母版页的 BaseMasterPage
和用于用户控件的 BaseUserControl
。
即使您在项目开始时不知道要在基类中放入什么,也应该遵循这种技术。到项目结束时,您可能会惊讶于有多少代码可以被每个页面共享。
页面和 PlaceHolder 控件
那么使用这种编程技术,页面看起来是什么样的?它们应包含尽可能少的标记。下面显示了一个简单的页面。最重要的元素 — 实际上是唯一的控件 — 是 PlaceHolder
控件。每个页面都有一个占位符。
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>This is a simple page example</title>
<link rel="stylesheet" type="text/css" href="Css/StyleSharePoint.css">
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder id="LocalPlaceHolder" runat="server"></asp:PlaceHolder>
</div>
</form>
</body>
</html>
此文件中的所有代码(占位符除外)均由 Visual Studio 自动生成。
在代码隐藏中,页面作为控件构建并添加到占位符中,如下所示(请注意,页面是从 BasePage
派生的)
public partial class _Default : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
Literal lit = new Literal();
lit.Text = TextWriter.MakeH1Text("This is a sample page without a master page.");
LocalPlaceHolder.Controls.Add(lit);
lit = new Literal();
lit.Text = TextWriter.MakeParagraph(
"This sample project shows how to build out pages dynamically in the code behind.");
LocalPlaceHolder.Controls.Add(lit);
// in this case, "blockstyle" is the css style for my panel
MyPanel panel = new MyPanel("blockstyle");
panel.AddLiteral("This block of text shows how to build a panel.");
LocalPlaceHolder.Controls.Add(panel);
MyLabel label = new MyLabel("inlinestyle");
label.AddParagraph("This is a label.");
}
}
注意我们如何动态地将每个页面元素创建为一个控件并将其添加到占位符中。该项目还包括示例,展示如何与母版页和用户控件一起执行此操作。
该方法的优点
对面向对象编程和类重用的强大支持 — 这是这种 Web 编程方法最强的论点之一。使用经典的 ASP 技术,即使是 ASP.NET,也会导致在多个页面上重复相同的代码。随着您将越来越多的页面逻辑移入从代码隐藏调用的类中,您消除了大量冗余代码。这减少了错误,并使大型项目更易于管理和更健壮。
智能感知和实时语法检查使编码变得轻而易举 — 这些 Visual Studio 功能使编写代码非常简单、快速和高效。它确实对这种编程风格产生了影响。没有这些技术,这种开发的论点将不那么有说服力。
最适合数据驱动的网站,例如从数据库读取的目录页面 — 该系统最适合高度数据驱动的 Web 项目,其中页面的大部分内容来自数据库或其他数据源。只需为每种数据展示类型 — 列表、表单等 — 派生控件类。然后,在代码隐藏中从这些控件动态构建页面。
最适合页面设计快速变化的页面,例如来自无法决定自己想要的最终用户的设计师和管理层 — 我曾参与过一个项目,高层管理人员始终无法决定页面的最终设计。他们不断改变主意。单个页面可能需要返工五次或更多次。我发现通过这种方式构建页面可以很容易地通过一点点代码调整来改变每个页面的外观。
轻松支持层叠样式表 (CSS) — 您可能一开始不这么认为,但一旦您开始使用这种方法,您就会发现它与 CSS 配合得非常好。与 MyPanel
类一样,只需将 CSS 类样式名称作为派生类的构造函数即可简化将样式应用于控件。嵌套面板控件使级联样式变得容易。
Cookie 和会话变量的管理 — 设计良好的 Web 应用程序都有处理 cookie 和会话变量的类。这种设计使得从您的控件访问这些类更加容易。
最适合逻辑高度复杂的页面 — 我发现这项技术最适合逻辑非常复杂的页面 — 例如,购物车或必须处理会话变量或 cookie 的登录页面。在一些项目中,我通过采用这种编程技术,成功地修复了从未完全正常工作的页面。如果您的项目中有复杂的页面,您似乎无法摆脱所有错误,请尝试采用这项技术,看看它是否能解决问题。
缺点
您将失去工具箱 — 那些喜欢将控件从 Visual Studio 工具箱拖放到网页上的用户可能不喜欢这种 Web 开发方法。但是,您仍然可以像示例项目中所展示的那样,将此技术与用户控件一起使用。
文档稀疏 — MSDN 关于 ASP.NET 的大部分文档都假设您会将控件放置到页面文件中并在标签中设置它们的属性。要从这些示例中弄清楚如何以编程方式执行相同操作可能很困难。对于您在类似本文这样流行 ASP.NET 开发的网站上找到的许多代码片段,这也同样适用。AJAX 控件尤其存在这个问题。我花了一段时间才通过这种方法让 AJAX 控件正常工作,因为我找不到说明以编程方式构建 AJAX 控件的示例。但一旦我弄清楚了基本原理,我就一帆风顺了。
最不适合大量内容不经常更改的“宣传册”式网页 — 就像这项技术最适合从数据库中获取内容的高度数据驱动的 Web 页面一样,它最不适合高度静态的 Web 页面,例如宣传册类型的网站或简单的数据网站,只需将数据网格放在页面上并连接到数据源就足够了。但是,如果我要开始一个此类项目,我仍然会考虑使用这项技术。
对于小型、一次性项目效率较低 — 如前所述,随着项目规模的增大,该技术的效率也会提高。这是因为构建自己的类库对于该方法的效率很重要。但请记住,小型项目通常会变成大型项目。
项目
包含的项目展示了如何使用这项技术来构建一些简单的页面。它展示了带有母版页和不带母版页的技术,以及您如何与用户控件一起使用该技术。该项目是为 Visual Studio 2005 编写的 C#。但是,相同的原理可以应用于 VB.NET。
结论
Web 开发从静态 HTML 到经典 ASP 再到 ASP.NET 的转变,使得将越来越多的页面显示逻辑移到代码隐藏和设计良好的类对象中变得更加容易。本文将重点关注该范围的极端 — 将 **所有** 编码逻辑移到所有页面的代码隐藏中。
与任何极端的编程方法一样,我预计在您的下一个项目中采用其全部内容会遇到阻力。但是,您可以采用这些技术并逐渐将它们纳入您的项目中 — 特别是页面重写。这项技术的核心是构建一个可重用的类库。我认为,随着您的类库的增长,您会发现采用这项技术并快速构建复杂且健壮的 Web 项目会更容易。我在几个项目中非常成功地使用了这项技术,几乎所有的页面表示逻辑都在代码隐藏中。
如果您正在处理一个“网状”的混乱经典 ASP 页面项目,您正试图移植到 ASP.NET,我强烈建议您考虑将您的项目迁移到这种开发方法。这并不是您在一个迭代中完成的事情,而是随着每次迭代,将越来越多的代码移到代码隐藏。使用面向对象的技巧来构建类库。在几次迭代中,您将拥有一个健壮、设计良好且非常易于管理的项目。
对我而言,采用这项技术需要改变思维方式,纠正多年来关于 Web 开发的固有观念。在一篇文章中不可能传达该技术的各个方面。我可以用一本书来介绍这项技术。但请查看项目并认真考虑该方法。当您遇到 Web 编程挑战时,请考虑如何使用这项技术来解决它,而不是您通常的处理方式。随着时间的推移,您可能会发现自己能够用更少的时间编写更健壮的网站。