1.2 ASP.NET控件
ASP.NET控件是ASP.NET Framework的核心。一个ASP.NET控件就是一个运行在服务器端并将实际的内容呈现到浏览器的一个.NET类。
例如在本章开始时创建的第一个ASP.NET页面中,一个Label控件用于显示当前的日期和时间。ASP.NET Framework内包含70多个控件,它们帮助你完成各种任务,从显示一个数据库记录列表到随机交替显示广告横幅等等。
本节提供一个ASP.NET Framework中控件的概览,你还会学到如何处理控件引发的事件,以及如何利用视图状态(View State)。
1.2.1 ASP.NET控件概览
ASP.NET Framework(2.0版)包含70多个控件。这些控件可以分为8组:
q 标准控件——标准控件用于呈现一些标准的表单元素,如按钮、输入框和标签。在下一章中将会详细地了解这些控件。
q 验证控件——验证控件用于在向服务器端提交数据前验证表单数据的正确性和有效性。比如,可以使用RequiredFieldValidator控件来检查用户是否为一个必填输入框输入了值。这些内容将在第3章展开讨论。
q Rich控件——Rich控件用于生成像日历、文件上传按钮、交替显示的广告横幅和多步骤用户向导这样的内容。对这些控件的讨论,将在第4章展开。
q 数据控件——数据控件用于使用数据,如数据库。举个例子,可以使用这些控件向数据库表提交一条新的记录,或显示一个数据库记录列表。本书的第三部分会更详细地论述这些控件。
q 导航控件——导航控件用于显示一些基本的页面导航元素,如菜单、树视图和面包屑。第17章讨论这些控件。
q 登录控件——登录控件用于显示登录表单、更改密码表单和注册表单。第2卷第1章介绍这些控件。
q Web部件控件——Web部件控制用于构建个性化门户应用程序。本书第2卷第三部分讨论这些控件。
q HTML控件——HTML控件用于把任何HTML标签转换为服务器端控件。本章的下一节将介绍这些控件。
除了HTML控件以外,只能严格地按同一种方法在页面中声明和使用ASP.NET控件。如果想在页面中显示一个文本输入框,那么可以这样来声明一个TextBox控件:
<asp:TextBox id="TextBox1" runat="Server" />
控件声明看起来像HTML标签声明。记住,不管怎样,控件不同于HTML标签,它是一个.NET类,而且是在服务器端执行而不是在Web浏览器中。
当TextBox控件呈现到浏览器时,它会生成下面的内容:
<input name="TextBox1" type="text" id="TextBox1" />
控件声明的第一部分——asp:前缀,指定控件的命名空间。所有标准的ASP.NET控件都包含在System.Web.UI.WebContorls命名空间,前缀asp:代表这个命名空间。
接下来,声明包含已声明的控件的名字。在本例中,声明了一个TextBox控件。
这个声明还包含一个ID属性。在代码中可用ID来引用这个控件。所有的控件必须有唯一的ID。
注解 应该始终为每个控件的ID属性赋值,就算并不需要针对这个控件编写代码。如果不提供ID属性,那么某些ASP.NET Framework的特性(比如双向数据绑定,two-way databinding)将不能正常工作。
声明还包括一个runat="Server"属性。这个属性将标签标记为一个服务器端控件。如果忽略此属性,那么这个TextBox标签会不运行直接就传给浏览器,而浏览器将简单地忽略这个标签。
最后,注意标签用一个正斜线结束。正斜线是创建</asp:TextBox>结束标签的快捷方式。如果你喜欢,也可以这样来声明TextBox控件:
<input name="TextBox1" type="text" id="TextBox1"></asp:TextBox>
在这种情况下,起始标签没有正斜线,而有一个显式的结束标签。
1.2.2 HTML控件
声明HTML控件的方式与声明标准ASP.NET控件的方式不同。ASP.NET Framework接受所有的HTML标签,并可为这些标签加上runat ="Server"属性,ranat="Server"属性把HTML标签转换为服务器端的ASP.NET控件。
比如,代码清单1-5中的页面就有一个转换为ASP.NET控件的<span>标签。
代码清单1-5 HtmlControls.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void Page_Load()
{
spanNow.InnerText = DateTime.Now.ToString("T");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>HTML Controls</title>
</head>
<body>
<form id="form1" runat="server">
<div>
At the tone, the time will be:
<span id="spanNow" runat="server" />
</div>
</form>
</body>
</html>
注意,代码清单1-5中的<span>标签除了添加了一个runat="Server"属性外,与普通的HTML<span>标签没有什么两样。
因为代码清单1-5中的<sapn>标签是一个服务器端的HTML控件,所以可以针对它编程。在代码清单1-5中,当前日期和时间在Page_Load()方法中赋给了<span>标签。
ASP.NET Framework包含了HTML控件,使已有的HTML页面转换到.NET Framework上更容易。本书中很少使用HTML控件,因为一般情况下,标准ASP.NET控件提供了相同的功能,而且更多。
1.2.3 理解和处理控件事件
大多数的ASP.NET控件支持一个或多个事件。例如,ASP.NET Button控件支持Click事件。点击由Button控件呈现到浏览器上的按钮后,Click事件在服务器端上引发。
代码清单1-6中的页面说明了如何编写在用户点击由Button控件呈现的按钮时执行的代码。(换一种说法,它演示如何创建一个Click事件处理程序。)
代码清单1-6 ShowButtonClick.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnSubmit_Click(object sender, EventArgs e)
{
Label1.Text = "Thanks!";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show Button Click</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button
id="btnSubmit"
Text="Click Here"
OnClick="btnSubmit_Click"
Runat="server" />
<br /><br />
<asp:Label
id="Label1"
Runat="server" />
</div>
</form>
</body>
</html>
注意,代码清单1-6中的Button控件有一个OnClick属性。这个属性指向叫做btnSubmit_Click()的子程序。btnSubmit_Click()子程序是Button的Click事件的处理程序。无论何时,只要一点击这个按钮,这个子程序就会执行(见图1-5)。
|
|
|
图1-5 引发Click事件 |
如果使用Visual Web Developer,可以使用多种方法自动地为控件添加事件处理程序。在代码示图(Source View)中,通过从左上方下拉列表中选择控件,再在右上方下拉列表中选择事件,为控件添加事件处理程序,事件处理程序代码就自动地加入到了页面中(见图1-6)。
在设计视图(Design View)中,可以双击一个控件来为控件的默认事件添加处理程序。双击控件会转入到代码视图并添加事件处理程序。
最后,在设计视图中,从设计器界面选择一个控件后,在属性窗口中通过点击事件按钮(闪电图标),再双击任一事件名为控件添加事件处理程序(见图1-7)。
|
|
|
|
图1-6 在代码视图中添加事件处理程序 |
图1-7 在属性窗口中添加事件处理程序 |
理解ASP.NET控件的事件发生在服务器端很重要。比如,当实际上点击了一个按钮时,Click事件并没有引发,直到包含这个按钮的页面被回传到服务器端,Click事件才会被引发。
ASP.NET Framework是一个服务器端Web应用程序框架。.NET Framework代码在服务器端运行而不是在Web浏览器中运行。透视ASP.NET内部,在页面回传到服务器端并在.NET Framework上下文中执行前,什么也不会发生。
注意,代码清单1-6中传给btnSubmit_Click()处理程序的两个参数。ASP.NET控件的所有事件处理程序都有同样的通用签名。
第一个参数是名叫sender的object参数,表示引发事件的控件。换一种说法,它表示点击的那个按钮。
可以把页面中的多个控件的事件串到同一个处理程序,然后用第一个参数来检测是那个控件引发了事件。例如,代码清单1-7的页面中有两个Button控件。当点击其中的任一按钮,该按钮上显示的数字被更新(见图1-8)。
代码清单1-7 ButtonCounters.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Button_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
btn.Text = (Int32.Parse(btn.Text) + 1).ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Button Counters</title>
</head>
<body>
<form id="form1" runat="server">
<div>
First Counter:
<asp:Button
id="Button1"
Text="0"
OnClick="Button_Click"
Runat="server" />
<br /><br />
Second Counter:
<asp:Button
id="Button2"
Text="0"
OnClick="Button_Click"
Runat="server" />
</div>
</form>
</body>
</html>
|
|
|
图1-8 一个事件处理程序处理两个按钮的事件 |
传给Click处理程序的第二个参数是名叫e的EventArgs参数,用于表示附加的事件关联的事件信息。点击按钮时,没有附加任何关联的事件信息到事件,所以在代码清单1-6和代码清单1-7中,第二个参数并不表示任何有用的信息。
另一种情况,当点击一个ImageButton控件而不是一个Button控件时,附加的事件信息被传给了事件处理程序。当点击一个ImageButton控件时,点击时的X坐标和Y坐标信息传递给了处理程序。
代码清单1-8中的ImageButton控件显示了一幅图片。点击图片时,所点击的点的X坐标和Y坐标信息显示在一个Label控件中(见图1-9)。

图1-9 点击ImageButton
代码清单1-8 ShowEventArgs.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnElephant_Click(object sender, ImageClickEventArgs e)
{
lblX.Text = e.X.ToString();
lblY.Text = e.Y.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show EventArgs</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ImageButton
id="btnElephant"
ImageUrl="Elephant.jpg"
Runat="server" OnClick="btnElephant_Click" />
<br />
X Coordinate:
<asp:Label
id="lblX"
Runat="server" />
<br />
Y Coordinate:
<asp:Label
id="lblY"
Runat="server" />
</div>
</form>
</body>
</html>
注意,传给btnElephant_Click()方法的第二个参数是一个ImageClickEventArgs参数。只要第二个参数不是默认的EventArgs参数,就可以知道一定有附加的事件信息传给了处理程序。
1.2.4 视图状态
万维网(World Wide Web)的基本协议HTTP协议,是一种无状态协议。每一次对网站的网页发出请求,在网站看来都是完全陌生的。
ASP.NET Framework成功地突破了HTTP协议的这个限制。比如,给一个Label控件的Text属性赋一个值,Label控件会在多次页面请求之间保存这个值。
以代码清单1-9中的页面进行这样一个思考:这个页面包含一个Button控件和一个Label控件,每次点击Button控件,Label控件显示的值都会增加1(见图1-10)。在回传到Web服务器端的过程序中,Label控件是怎样保存它的值的呢?
代码清单1-9 ShowViewState.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnAdd_Click(object sender, EventArgs e)
{
lblCounter.Text = (Int32.Parse(lblCounter.Text) + 1).ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show View State</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button
id="btnAdd"
Text="Add"
OnClick="btnAdd_Click"
Runat="server" />
<asp:Label
id="lblCounter"
Text="0"
Runat="server" />
</div>
</form>
</body>
</html>
ASP.NET Framework使用了一个技巧——视图状态。在浏览器中打开代码清单1-9的页面,查看页面源代码,可以看到一个像下面这样的名叫__VIEWSTATE的表单隐藏字段(hidden form field):
<input type="hidden" name="__VIEWSTATE" id="__
VIEWSTATE" value="/wEPDwUKLTc2ODE1OTYxNw9kFgICBA9kFgIC
Aw8PFgIeBFRleHQFATFkZGT3tMnThg9KZpGak55p367vfInj1w==" />
这个表单隐藏字段包含了Label控件的Text属性的值(所有其他控件的属性值也都保存在视图状态中)。当页面回传到服务器端时,ASP.NET Framework对这个字符串进行分解,重建所有保存在视图状态中的所有属性值。在页面回传到服务器端的过程中,ASP.NET Framework就是这样来保存控件属性的状态的。
默认情况下,ASP.NET Framework中的每个控件都启用了视图状态。如果更改日历控件的背景色,那么新的背景色就会在回传过程中被记住。如果更改下拉列表的被选项,那么新的被选项就会在回传过程中被记住。这些属性的值都会自动地保存在视图状态中(见图1-10)。
视图状态是一个好东西,但有时可能有点好过了头。__VIEWSTATE表单隐藏字段可能会太大。向视图状态中填充太多的数据可能会降低页面呈现的速度,因为隐藏字段的内容必须在Web服务器端和Web浏览器之间传来传去。
为页面启用跟踪(trace)后,可以查看页面中每个控件“消费”了多少个视图状态(见图1-11)。代码清单1-10中页面的<%@ Page %>指令中包含一个启用跟踪功能的 Trace ="true"属性。

图1-10 在回传过程中保存状态

图1-11 查看每个控件的视图状态大小
代码清单1-10 ShowTrace.aspx
<%@ Page Language="C#" Trace="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void Page_Load()
{
Label1.Text = "Hello World!";
Calendar1.TodaysDate = DateTime.Now;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show Trace</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="Label1"
Runat="server" />
<asp:Calendar
id="Calendar1"
TodayDayStyle-BackColor="Yellow"
Runat="server" />
</div>
</form>
</body>
</html>
打开代码清单1-10中的页面,页面的附加信息追加在页面的底部。其中控件树部分显示页面中每个ASP.NET控件使用的视图状态的数量。
每一个ASP.NET控件都有EnableViewState属性。如果把这个属性的值设为False,那么这个控件的视图状态就被禁用了。这样的话,这个控件的属性值就不会在回传到服务器端的过程中保存。
例如,代码清单1-11中的页面包含两个Label控件和一个Button控件。第一个Label控件的视图状态是禁用的,第二个Label控件的视图状态是启用的。点击按钮后,只有第二个Label控件的值在原来的基础上增加了1。
代码清单1-11 DisableViewState.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void btnAdd_Click(object sender, EventArgs e)
{
Label1.Text = (Int32.Parse(Label1.Text) + 1).ToString();
Label2.Text = (Int32.Parse(Label2.Text) + 1).ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Disable View State</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Label 1:
<asp:Label
id="Label1"
EnableViewState="false"
Text="0"
Runat="server" />
<br />
Label 2:
<asp:Label
id="Label2"
Text="0"
Runat="server" />
<br /><br />
<asp:Button
id="btnAdd"
Text="Add"
OnClick="btnAdd_Click"
Runat="server" />
</div>
</form>
</body>
</html>
有时,就算不用关心__VIEWSTATE表单隐藏字段的大小,也可能要禁用视图状态。比如,当使用一个Label控件显示窗体验证错误信息时,在每次页面提交后,都要从擦除原有信息开始。这种情况下,简单地禁用Label控件的视图状态就可以了。
注解 ASP.NET Framework 2.0版新增一个叫控件状态(Control State)的特性。控件状态类似于视图状态,不过它只保存临界状态信息。比如,GridView控件使用控件视图保存被选行信息,就算视图状态被禁用了,GridView控件仍然能记住哪一行被选中了。










