第38章ASP.NET开发在进行Web开发时通常会出现这样的情况:即可用的工具的功能虽然强大,但不符合具体项目的需求,可能是给定控件的工作方式并不像所期望的那样,也可能是一部分代码本来的目的是能够在多个页面上重用,但是许多开发人员实现起来却相当复杂。在这些情况下,定制控件的建立就尤为迫切。简言之,定制控件可以把多个现有的控件包装在一起,这些现有控件还可能有指定布局的额外属性;定制控件也可以与现有的控件完全不同。使用定制控件与使用ASP.NET中的控件一样简单,能使Web站点的编码非常容易。本章的第一部分将介绍控件开发人员可用的选项,并编写一个简单的用户控件,我们还将介绍构建高级控件的基础知识,但不详细探讨它们,这些主题需要一本书的篇幅来讨论。接着介绍Master页面,这是ASP.NET2.0的一个新技术,可以为Web站点提供模板。使用Master页面可以在Web站点上通过大量的重用代码,实现复杂的Web页面布局。本章还将说明如何使用Web导航服务器控件和Master页面,提供Web站点上一致的导航布局。站点导航可以把用户分为不同的组,只允许某些用户(注册到站点上的用户,或站点管理员)访问某些部分。本章还将介绍Web站点的安全性和登录,通过Web登录服务器控件很容易实现该功能。之后介绍一些高级样式设置技巧,即提供和选择Web站点的主题,主题把Web页面的显示与其功能分隔开。我们可以为站点提供CSS样式表,给Web服务器控件提供不同的样式。最后使用WebPart定位和定制页面上的控件,让用户动态地个性化Web页面。本章的主要内容如下:●用户控件和定制控件●Master页面●站点导航●安全性●主题●WebPart本章将开发一个大型示例应用程序,它包含本章和上一章介绍的所有技术。这个应用程序PCSDemoSite在本章的下载代码中。要包含所有的代码,会使本章非常长,但在学习其中的技术之前不需要运行它。相关的代码段会在需要时列出,其他代码(大多数是前面介绍的内容或简单代码)请读者自学。38.1用户控件和定制控件在过去,实现定制控件是非常复杂的,尤其在大型系统中,由于使用定制控件需要复杂的注册过程,因此定制控件的实现就更为复杂。即使在简单的系统上,创建定制控件所需进行的编码也是一个相当复杂的过程。老版本Web语言的脚本编码功能也不能对手工编写的对象模型提供较好的访问,因此各个方面的性能都比较差。.NETFramework使用简单的编程技术,为定制控件的创建提供了一个理想的设置。ASP.NET服务器控件的每个方面都可以随意定制,包括模板制作、客户端脚本编码等功能。但是,也不必为所有这些功能编写代码;控件越简单,创建就越容易。另外,.NET系统中固有的程序集动态查询功能使Web应用程序在新Web服务器上的安装如同复制包含代码的目录结构一样简单。要使用自己创建的控件,只需复制包含这些控件的程序集和其他代码即可。甚至可以把频繁使用的控件放在Web服务器上一个位于全局程序集缓存器(GAC)的程序集中,这样服务器上所有的Web应用程序就可以访问它们了。本章将介绍两类不同的控件:●用户控件--即把现有的ASP.NET页转化为控件●定制控件--即组合几个控件的功能、扩展现有的控件以及从头创建新的控件我们将创建一个简单的控件,显示一副扑克牌(黑桃、方块、红桃和梅花),以便轻松地把它嵌入到其他ASP.NET页面中,以此来阐明用户控件的用法。对于定制控件,不打算详细介绍,只探讨基本规则和查找定制控件的地址。38.1.1用户控件用户控件是用ASP.NET代码创建的控件,就像在标准的ASP.NETWeb页面中创建控件一样,不同之处在于一旦创建了用户控件,就可以轻松地在多个ASP.NET页面中重用它们。例如,假定已经创建了一个显示数据库中信息的页面,信息也许是关于订单的,就不必创建一个固定的页面去显示信息,而可以把相关的代码放到用户控件中,然后把该控件插入到任意多个不同的Web页面中。此外,可以给用户控件定义属性和方法,例如,可以指定Web页面上显示数据库表时的背景色属性,或者指定一个方法,重新进行数据库查询,以检查数据库中的变化。下面创建一个简单的用户控件,与其他章节一样,本章的示例项目也可以从Wrox网站上下载。一个简单的用户控件在VisualStudio中,在C:\ProCSharp\Chapter38目录下创建一个新Web站点PCSUserC-WebApp1。一旦生成标准文件,就可以选择Website|AddNewItem...菜单选项,添加名称为PCSUserC1.ascx的Web用户控件,如图38-1所示。给项目添加的文件的扩展名为.ascx和.ascx.cs,它们的工作方式与前面的.aspx文件非常相似。.ascx文件包含ASP.NET代码,看起来与普通的.aspx文件非常相似。.ascx.cs文件是后台代码文件,它为用户控件定义了定制代码,定义的方式与在.aspx.cs文件中定义窗体的方式一样。与.aspx文件相似,也可以在设计视图或源代码视图中查看.ascx文件。在源代码视图中查看文件,可以发现一个重要的区别:.ascx文件没有显示HTML代码,特别是没有form元素,原因在于:用户控件要插入到其他文件的ASP.NET窗体中,因此不需要自己的form标记。生成的代码如下所示。图38-1%@ControlLanguage=c#AutoEventWireup=trueCodeFile=PCSUserC1.ascx.csInherits=PCSUserC1%这非常类似于在.aspx文件中生成的%@Page%指令,但指定了Control,而不是Page,CodeFile属性指定了后台代码文件,Inherits指定了在后台代码文件中页面继承的类名。.ascx.cs文件中生成的代码与自动生成的.aspx.cs文件一样,其中包含一个空的类定义和Page_Load()事件处理程序。本例的简单控件是一个显示图形的控件,显示的图形对应于扑克牌中的花色(即梅花、方块、红桃和黑桃)。这里所需的图形是VisualStudio附带的图形;它们在本章的下载代码中,位于CardSuitImages目录,其文件名分别是CLUB.BMP、DIAMOND.BMP、HEART.BMP和SPADE.BMP。把这些图形文件复制到项目目录的新子目录Images中,以便在后面使用它们。如果不能访问这个目录,可以使用其他任意图形,因为它们对于代码的功能而言并不重要。注意:与VisualStudio的以前版本不同,在VisualStudio外部对Web站点结构进行的修改会自动反映到IDE上。只需单击SolutionExplorer窗口中的Refresh按钮,就会看到新的Images目录和位图文件。给新控件添加一些代码。在PCSUserC1.ascx的HTML视图中添加下列代码:%@ControlLanguage=c#AutoEventWireup=trueCodeFile=PCSUserC1.ascx.csInherits=PCSUserC1%tablecellspacing=4trvalign=middletdasp:ImageRunat=serverID=suitPicImageURL=~/Images/club.bmp//tdtdasp:LabelRunat=serverID=suitLabelClub/asp:Label/td/tr/table这段代码定义了控件的默认状态,即一个梅花图形和一个标签。图像路径前面的~表示从Web站点的根目录开始。在给控件添加功能之前,先把这个控件添加到项目的Web页面WebForm1.aspx上,测试这个默认状态。为了在.aspx文件中使用定制的控件,首先需要指定如何引用该控件,也就是说,如何在HTML中引用代表控件的标记名称。为此,在Default.aspx中代码的顶部使用%Register%指令,如下所示。%@RegisterTagPrefix=PCSTagName=UserC1Src=PCSUserC1.ascx%属性TagPrefix和TagName指定要使用的标记名称(指定的格式为TagPrefix:TagName),使用属性Src指向包含用户控件的文件。现在,添加下面的元素,就可以使用控件了:formid=Form1method=postrunat=serverdivPCS:UserC1Runat=serverID=myUserControl//div/form这就是测试用户控件需要做的所有工作,运行项目的结果如图38-2所示。(点击查看大图)图38-2可以看出,这个控件在表格布局中组合了两个现有的控件,即图形控件和标签控件,因此它属于合成控件一类。为了控制显示的花色图形,可以在元素上使用属性。用户控件元素上的属性会自动映射到用户控件的特性上,因此,只需给控件的后台代码PCSUserC1.ascx.cs添加一个特性。这个特性称为Suit,让它接收合适的花色值。为了便于表示控件的状态,可以定义一个枚举,来保存4个花色名称。最佳方式是在Web站点上添加一个目录App_Code(App_Code是另一个特殊的目录,与App_Data一样,它的功能取决于编程人员,这里是为Web应用程序保存其他代码文件。要添加这个目录,可以右击SolutionExplorer中的Website,单击AddASP.NETFolderApp_Code),然后在这个目录中添加一个.cs文件suit.cs,其代码如下:usingSystem;publicenumsuit{club,diamond,heart,spade}类PCSUserC1需要一个成员变量,以保存花色类型currentSuit:publicpartialclassPCSUserC1:System.Web.UI.UserControl{protectedsuitcurrentSuit;再添加一个访问这个成员变量的属性Suit:publicsuitSuit{get{returncurrentSuit;}set{currentSuit=value;suitPic.ImageUrl=~/Images/+currentSuit.ToString()+.bmp;suitLabel.Text=currentSuit.ToString();}}这里的set存取器把图形的URL设置为前面复制的一个文件,并把要显示的文本设置为花色名称。下面需要给Default.aspx添加代码以访问这个新的属性。使用刚才添加的属性选择花色:ASP.NET处理器可以从提供的字符串中选择出正确的枚举项。但为了使该控件更有趣、更吸引人,下面使用一个单选按钮列表来选择花色:ClubDiamondHeartSpade还需要给列表的SelectedIndexChanged事件添加事件处理程序。双击设计视图中的单选按钮列表,就可以添加处理程序。注意:把列表的Autopostback属性设置为true,是因为除非进行回送操作,否则将不在服务器上执行suitList_SelectedIndexChanged事件处理程序,在默认状态下,这个控件也不会触发回送操作。在Default.aspx.cs中,方法suitList_SelectedIndexChanged()需要以下代码:publicpartialclassDefault{protectedvoidsuitList_SelectedIndexChanged(objectsender,EventArgse){myUserControl.Suit=(suit)Enum.Parse(typeof(suit),suitList.SelectedItem.Value);}}我们知道,元素ListItem上的value属性代表前面定义的枚举suit的有效值,因此简单地把这些值解析为枚举类