ASP.NET 4 和 Visual Studio 2010 Web 开发概述 3 — Web Forms

Web Forms 从 ASP.NET 1.0 发布起就是核心特性。在ASP.NET 4 中有许多增强,包括:

  • 设置 meta 标签的能力
  • 对视图状态更多的控制
  • 使用浏览器兼容能力的更简单的方法
  • 支持对 Web Forms 使用路由
  • 对产生的ID的更多控制
  • 在数据控件中持久化选中行的能力
  • 在 FormView 和 ListView 控件中呈现 HTML 的更多控制
  • 对数据源控件的过滤支持
1) 使用 Page.MetaKeywords 和 Page.MetaDescription 属性设置 Meta 标记

ASP.NET 4 给 Paeg 类添加了 2 个新属性,MetaKeywords 和 MetaDescription。这两个属性的工作方式和页面的 Title 属性工作方式是一样的。下面是规则:

  • 如果 head 元素中没有匹配属性名的 meta 标签, meta 标签会在呈现时添加到页面上
  • 如果已经有 meta 标签,这些属性作为存在标签内容的 get 方法和 set 方法使用

可以在运行时设置这些属性,也可以在 @ Page 指令中设置。例如:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs"
Inherits="_Default"
Keywords="These, are, my, keywords"
Description="This is a description" %>

这会覆盖页面中声明的 meta 标签内容,如果存在的话。

description meta 标签的内容用于提高 Google 的搜索列表预览。(见 Improve snippets with a meta description makeover)Google 和 Windows Live Search 都不使用 keywords 的内容做任何事情,但其他搜索引擎可能会。更多信息参见 Meta Keywords Advice

2) 为单独的控件启用视图状态

默认地,视图状态是为页面启用的,这样页面上的每个控件都可以存储视图状态,即使不需要。视图状态数据是包含在页面生成的标记中,会增加发送页面到客户以及回送的时间。存储多于必须的视图状态会导致明显的性能退化。旧版本的ASP.NET中,开发人员可以禁用控件的视图状态,但不得不为每个单独的控件设置。在 ASP.NET 4 中,Web服务器控件包含了一个 ViewStateMode 属性来默认禁用页面的视图状态并只为页面中需要的控件启用它。

ViewStateMode 属性有三个值:Enabled,Disabled 和 Inherit。Enabled 为控件及其设置相同属性为 Inherit 值或不设置的子控件启用视图状态。Disabled 禁用视图状态,而 Inherit 表示控件使用父控件的 ViewStateMode 值。例如:

< form id ="form1" runat ="server" >
< script runat ="server" > 1: 2: protected override void OnLoad(EventArgs e) { 3: if (!IsPostBack) { 4: label1.Text = label2.Text = "[DynamicValue]"; 5: } 6: base.OnLoad(e); 7: } 8: </ script >
< asp:PlaceHolder ID ="PlaceHolder1" runat ="server" ViewStateMode ="Disabled" >
Disabled: < asp:Label ID ="label1" runat ="server" Text ="[DeclaredValue]" />< br />
< asp:PlaceHolder ID ="PlaceHolder2" runat ="server" ViewStateMode ="Enabled" >
Enabled: < asp:Label ID ="label2" runat ="server" Text ="[DeclaredValue]" />
</ asp:PlaceHolder >
</ asp:PlaceHolder >
< hr />
< asp:button ID ="Button1" runat ="server" Text ="Postback" />
<% 1: -- Further markup here -- %>

可以看到,PlaceHolder1 控件禁用了视图状态,子控件 label1 继承了这个属性值(Inherit 是默认值),因此不会保存视图状态。PlaceHolder2 控件 ViewStateMode 设置为 Enabled,所以 label2 继承了这个属性保存了视图状态。当页面第一次加载时,两个标签的 Text 属性都被设置为 "[DynamicValue]"。

然而在 postback 后,label1 显示 [DeclaredValue],label2 显示 [DynamicValue]。

也可以在 @ Page 指令中设置 ViewStateMode:

<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Default.aspx.cs"
Inherits="WebApplication1._Default"
ViewStateMode="Disabled" %>

Page 类只不过是另一种控件,它作为页面中所有其他控件的父控件作用。Page 实例 ViewSateMode 的默认值是 Enabled,控件的默认值是 Inherit。

仅当 EnableViewState 属性为 true 时,ViewStateMode 属性的值才会决定视图状态是否要维护。如果 EnableViewState 属性为 false,视图状态将不会被维护,即使 ViewStateMode 被设置为 Enabled。

一个良好的使用是在母版页中使用 ContentPlaceHolder 控件,可以为母版页把 ViewStateMode 设置为 Disabled 然后把需要视图状态的 ContentPlaceHolder 控件单独启用它。

3) 浏览器能力的变化

ASP.NET 通过使用一种叫做浏览器能力(browser capabilities)的特性来检测用户使用的浏览器的能力。浏览器能力是通过 HttpBrowserCapabilities 对象(由 Request.Browser 属性暴露)来表示的。可以使用 HttpBrowserCapabilities 对象检测当前浏览器是否支持特定版本JavaScript的类型和版本。或者,检测请求是否最初来自于移动设备。

HttpBrowserCapabilities 对象是由一组浏览器定义文件驱动的。这些文件包含了关于特定浏览器能力的信息。在 ASP.NET 4 中,这些浏览器定义文件已经更新为包含最近引入的浏览器和设备(如 Chrome, iPhone)的信息。

下面的列表展示了新的浏览器定义文件:

  • blackberry.browser
  • chrome.browser
  • Default.browser
  • firefox.browser
  • gateway.browser
  • generic.browser
  • ie.browser
  • iemobile.browser
  • iphone.browser
  • opera.browser
  • safari.browser

使用浏览器能力提供程序

在 ASP.NET 3.5 SP1 中,可以以下面的方法定义浏览器拥有的能力:

a. 在计算机级别,创建或更新下面文件夹中的 .browser XML文件:

\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\Browsers

定义后要从 Visual Studio 命令提示行运行下面的命令来重新生成浏览器能力程序集并将其添加到GAC:

aspnet_regbrowsers.exe -I c

b. 对于单独的应用程序,在应用程序的 App_Browsers 文件夹中创建 .browser 文件。

这些方法需要更改 XML 文件,对于计算机级别的更改,还必须在运行 aspnet_regbrowsers.exe 后重新启动应用程序。

ASP.NET 4 包含一个称为浏览器能力提供程序(browser capabilities provider)的特性。正如名称所示,这个会让你征收一个提供程序,从而能使用自己的代码检测浏览器能力。

在实践中,开发人员经常不会定义自定义的浏览器能力。浏览器文件很难更新,更新他们的过程相当复杂,而 .browser 文件的 XML 语法复杂的难以使用和定义。如果有一个通用的浏览器定义语法,或者一个包含即时更新浏览器定义的数据库,会不会让这个过程变得容易呢。新的浏览器能力提供程序特性让这些场景成为可能,对第三方开发人员也实用。

有两个使用新的 ASP.NET 4 浏览器能力提供程序特性的主要方法:扩展 ASP.NET 浏览器能力定义功能,或者干脆完全取代它。下面描述如何取代它,再介绍如何扩展它。

a. 取代 ASP.NET 浏览器能力功能

要完全取代 ASP.NET 浏览器能力,遵循这些步骤:

(1) 创建一个派生自 HttpCapabilitiesProvider 提供程序类重写 GetBrowserCapabilities 方法,例如:

public class CustomProvider : HttpCapabilitiesProvider
{
public override HttpBrowserCapabilities GetBrowserCapabilities(HttpRequest request)
{
HttpBrowserCapabilities browserCaps = new HttpBrowserCapabilities();
Hashtable values = new Hashtable(180, StringComparer.OrdinalIgnoreCase);
values[String.Empty] = request.UserAgent;
values[ "browser"] = "MyCustomBrowser";
browserCaps.Capabilities = values;
return browserCaps;
}
}

代码创建了一个新的 HttpBrowserCapabilities 对象,仅指定了名为 browser 的能力并设置此能力的值为 MyCustomerBrowser。

(2) 注册提供程序

为了在应用程序中使用提供程序,必须在 Web.config 或 machine.config 文件的 browserCaps 节添加 provider 属性。(也可以在 location 元素中为特定目录定义提供程序属性,例如针对特定移动设备的文件夹)。例如:

< system.web >
< browserCaps provider ="ClassLibrary2.CustomProvider, ClassLibrary2,
Version=1.0.0.0, Culture=neutral"
/>
</ system.web >

另一种注册方法是使用代码,例如:

void Application_Start( object sender, EventArgs e)
{
HttpCapabilitiesBase.BrowserCapabilitiesProvider =
new ClassLibrary2.CustomProvider();
// ...
}

这段代码必须运行在 Global.asax 文件的 Application_Start 事件中。

b. 缓存 HttpBrowserCapabilities 对象

可提升性能,步骤如下:

(1) 创建一个派生自 HttpCapabilitiesProvider 的类,例如:

public class CustomProvider : HttpCapabilitiesProvider
{
public override HttpBrowserCapabilities
GetBrowserCapabilities(HttpRequest request)
{
string cacheKey = BuildCacheKey();
int cacheTime = GetCacheTime();
HttpBrowserCapabilities browserCaps =
HttpContext.Current.Cache[cacheKey] as
HttpBrowserCapabilities;
if (browserCaps == null)
{
HttpBrowserCapabilities browserCaps = new
HttpBrowserCapabilities();
Hashtable values = new Hashtable(180,
StringComparer.OrdinalIgnoreCase);
values[String.Empty] = request.UserAgent;
values[ "browser"] = "MyCustomBrowser";
browserCaps.Capabilities = values;
HttpContext.Current.Cache.Insert(cacheKey,
browserCaps, null, DateTime.MaxValue,
TimeSpan.FromSeconds(cacheTime));
}
return browserCaps;
}
}

(2) 注册提供程序(与前面相同)

c. 扩展 ASP.NET 浏览器能力功能

(1) 创建一个派生自 HttpCapabilitiesEvaluator 的类,重写 GetBrowserCapabilities 方法,例如:

public class CustomProvider : HttpCapabilitiesEvaluator
{
public override HttpBrowserCapabilities
GetBrowserCapabilities(HttpRequest request)
{
HttpBrowserCapabilities browserCaps =
base.GetHttpBrowserCapabilities(request);
if (browserCaps.Browser == "Unknown")
{
browserCaps = MyBrowserCapabilitiesEvaulator(request);
}
return browserCaps;
}
}

(2) 注册提供程序(与前面相同)

d. 通过添加新的能力给已存在能力定义来扩展浏览器能力功能

(1) 创建派生自 HttpCapabilitiesEvaluator 的类,重写 GetBrowserCapabilities 方法,例如:

public class CustomProvider : HttpCapabilitiesEvalutor
{
public override HttpBrowserCapabilities
GetBrowserCapabilities(HttpRequest request)
{
HttpBrowserCapabilities browserCaps =
base.GetBrowserCapabilities(request);
browserCaps.Frames = true;
browserCaps.Capabilities[ "MultiTouch"] = "true";
return browserCaps;
}
}

(2) 注册提供程序(与前面相同)

4) ASP.NET 4 中的路由

ASP.NET 4 增加了对 Web Forms 使用路由的内置支持。路由让应用程序可以配置来接受不映射到物理文件的请求URL。

路由从 ASP.NET 3.5 SP1 起就可用了,不过 ASP.NET 4 包含了一些更易于使用的特性:

  • PageRouteHandler 类,是一个简单的 HTTP 处理程序在定义路由时使用,该类传递数据到请求路由的页面。
  • 新的 HttpRequest.RequestContext 属性和 Page.RouteData 属性(这是HttpRequest.RequestContext.RouteData 对象的代理)。用于更方便的访问从路由传递的信息。
  • 新的表达式生成器:System.Web.Compilation.RouteUrlExpressionbuilder 和 System.Web.Compilation.RouteValueExpressionBuilder。
  • RouteUrl,提供了一个在服务器控件中创建路由URL的简单方法。
  • RouteValue,提供了从 RouteContext 对象提取信息的简单方法。
  • RouteParameter 类,使 RouteContext 对象中包含的数据传递到数据源控件查询变得更加容易。

a. 针对 Web Forms 页面路由

下面的例子展示了如何使用 Route 类的新 MapPageRoute 方法定义 Web Forms 路由:

public class Global : System.Web.HttpApplication
{
void Application_Start( object sender, EventArgs e)
{
RouteTable.Routes.MapPageRoute( "SearchRoute",
"search/{searchterm}", "~/search.aspx");
RouteTable.Routes.MapPageRoute( "UserRoute",
"users/{username}", "~/users.aspx");
}
}

ASP.NET 4 引入了 MapPageRoute 方法。下面的例子和前面的例子作用相同,但使用了 PageRouteHandler 类:

RouteTable.Routes.Add( "SearchRoute", new Route( "search/{searchterm}",
new PageRouteHandler( "~/search.aspx")));

代码将路由映射到物理页面。第一个路由定义还指定了名为 searchterm 的参数,它应当从 URL 中提取并传递给页面。

MapPageRoute 方法支持下面的方法重载:

  • MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess)
  • MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults)
  • MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary contraints)

checkPhysicalUrlAccess 参数指示路由是否应当检查被路由的物理页面(search.aspx)的安全许可,和进站URL的许可(search/{searchterm})。如果值为 false,只能进站的URL许可会被检查。这些许可定义在 Web.config 文件中,例如:

< configuration >
< location path ="search.aspx" >
< system.web >
< authorization >
< allow roles ="admin" />
< deny users ="*" />
</ authorization >
</ system.web >
</ location >
< location path ="search" >
< system.web >
< authorization >
< allow users ="*" />
</ authorization >
</ system.web >
</ location >

</ configuration >

在示例中,对物理文件 search.aspx 的访问对除了admin角色的其他所有用户是禁止的。当 checkPhysicalUrlAccess 参数为 true (默认值)时,只有 admin 用户允许访问 URL /search/{searchterm}。如果为 false,所有的授权用户都允许访问 URL /search/{searchterm}。

b. 在 Web Forms 页面中读取路由信息

在 Web Forms 物理页面的代码中,可以访问从 URL 提取的路由信息(或者其他对象添加到 RouteData 对象中的其他信息),这是通过两个属性做到的:HttpRequest.RequestContext 和 Page.RouteData。(Page.RouteData 包装了 HttpRequest.RequestContext.RouteData)例如:

protected void Page_Load(object sender, EventArgs e)
{
string searchterm = Page.RouteData.Values["searchterm"] as string;
label1.Text = searchterm;
}

考虑这个请求URL:http://localhost/search/scott,单词“scott”会在 search.aspx 页面中呈现。

c. 在标记中访问路由信息

可以在标记中使用表达式访问路由数据。表达式生成器是一种和声明式代码一起工作的强大而优雅的方法。(更多信息见 Express Yourself With Custom Expression Builders)。

ASP.NET 4 为 Web Forms 路由包含两个新的表达式生成器。例如:

< asp:HyperLink ID ="HyperLink1" runat ="server"
NavigateUrl ="&lt;%$RouteUrl:SearchTerm=scott%>" &gt;Search for Scott </ asp:HyperLink >

在示例中,RouteUrl 用于定义基于路由参数的 URL。这个标记会生成这样的URL:http://localhost/search/scott。

ASP.NET 基于输入参数自动的解决正确的路由(即产生正确的 URL)。还可以在表达式中包含路由名称,这样可以指定要使用的路由。

下面的例子展示了如何使用 RouteValue 表达式,例如:

< asp:Label ID ="Label1" runat ="server" Text ="&lt;%$RouteValue:SearchTerm%>" /&gt;

当包含这个页面的控件运行时,“scott”会显示在标签上。

RouteValue 表达式使得在标记中使用路由数据变得简单,它避免了在标记中使用更复杂的 Page.RouteData["x"] 语法。

d. 为数据源控件参数使用路由数据

RouteParameter 类可以指定路由数据作为数据源控件中查询的参数值。例如:

< asp:sqldatasource id ="SqlDataSource1" runat ="server"
connectionstring ="&lt;%$ ConnectionStrings:MyNorthwind %>"
selectcommand ="SELECT CompanyName,ShipperID FROM Shippers where
CompanyName=@companyname"

& lt; selectparameters &gt;
< asp:routeparameter name ="companyname" RouteKey ="searchterm" />
</ selectparameters >

</ asp:sqldatasource >
5) 设置客户 ID

新的 ClientIDMode 属性解决了 ASP.NET 的一个长久的问题,即控件如何为呈现的元素创建 id 属性。如果应用程序包含引用这些元素的客户脚本时为呈现的元素生成的 id 属性就很重要。

HTML 中为 Web 服务器控件呈现的的 id 属性是基于控件的 ClientID 属性产生的。直到 ASP.NET 4,从 ClientID 产生 id 属性的算法一直都是使用 ID 连接到命名容器(如果有),这样对于反复控件(repeated controls,即在数据控件中),会添加一个前缀和一系列连续的数字。这就保证了页面中控件的 ID 是唯一的,算法导致控件 ID 是不可预知的,因而在客户脚本中难以引用。

新的 ClientIDMode 属性使得可以更精确地指定如何为控件产生客户 ID。你可以为任何控件设置 ClientIDMode 属性,包括页面。可能的设置有:

  • AutoID —— 这和旧版本的 ASP.NET 中产生 ClientID 属性值的算法等价。
  • Static —— 这表示 ClientID 的值将会和 ID 的值相同,而不会连接父级命名容器的 ID。这对 Web 中的用户控件是有用的。因为一个 Web 用户控件可以放在不同的页面上以及在不同的容器控件中,对使用 AutoID 算法的控件编写客户脚本是很困难的,因为不能预测 ID 的值。
  • Predictable —— 这个选项主要用于使用重复模板的数据控件。它连接了控件的命名容器的 ID 属性,但产生的 ClientID 值不包含类似 "ctlxxx" 的字符串。这个设置和控件的 ClientIDRowSuffix 属性一直工作。设置 ClientIDRowSuffix 属性给数据字段的名称,这个字段的值用作产生的 ClientID 值的后缀。通常使用数据记录的主键作为 ClientIDRowSuffix 值。
  • Inherit —— 这个设置是控件的默认行为,它表示控件的 ID 产生和它的父容器相同。

可以在页面级别设置 ClientIDMode,这样就把当前页面中所有的控件定义了默认的 ClientIDMode 值。

在页面级别的默认的 ClientIDMode 值是 AutoID,控件级别默认的 ClientIDMode 值是 Inherit。

在 @ Page 指令中设置页面级别的值,例如:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs"
Inherits="_Default"
ClientIDMode="Predictable" %>

也可以在配置文件中设置 ClientIDMode 值,或者在计算机(机器)级别,或者在应用程序级别。这会在应用程序中所有页面的所有控件定义默认的 ClientIDMode 设置。如果在计算机级别设置值,就为那台计算机上所有的网站定义了默认的 ClientIDMode 设置。例如:

< system.web >
< pages clientIDMode ="Predictable" ></ pages >
</ system.web >

ClientID 属性的值是从控件父控件的命名容器推断出来的。在某些场景中,例如当你使用母版页时,控件的 ID 可能呈现为下面的 HTML:

< div id ="ctl00_ContentPlaceHolder1_ParentPanel" >
< div id ="ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1" >
< input name ="ctl00$ContentPlaceHolder1$ParentPanel$NamingPanel1$TextBox1"
type ="text" value ="Hello!"
id ="ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1_TextBox1" />
</ div >

尽管标记中显示的 input 元素在页面中只有两个命名容器(嵌套的 ContentPlaceHolder 控件),因为母版页的处理方法,最终结果是控件的ID会像这样:

ctl00_ContentPlaceHolder1_ParentPanel_NamingPanel1_TextBox1

这个 ID 保证了在页面中是唯一的,但在大部分情况下是不必要的。想减少呈现的 ID 的长度,或者想控制 ID 是如何产生的(比如要消除 "ctlxxx" 前缀),最简单的解决方法是设置 ClientIDMode 属性,例如:

< tc:NamingPanel runat ="server" ID ="ParentPanel" ClientIDMode ="Static" >
< tc:NamingPanel runat ="server" ID ="NamingPanel1" ClientIDMode ="Predictable" >
< asp:TextBox ID ="TextBox1" runat ="server" Text ="Hello!" ></ asp:TextBox >
</ tc:NamingPanel >

</ tc:NamingPanel >

在这个示例中,最外层的 NamingPanel 元素的 ClientIDMode 属性被设置为 static,内部的 NamingPanel 元素被设置为 Predictable。这些设置导致下面的标记(页面的其余部分和母版页假设和前面的示例相同):

< div id ="ParentPanel" >
< div id ="ParentPanel_NamingPanel1" >
< input name ="ctl00$ContentPlaceHolder1$ParentPanel$NamingPanel1$TextBox1"
type ="text" value ="Hello!" id ="ParentPanel_NamingPanel1_TextBox1" />
</ div >

Static 的设置会影响最外层 NamingPanel 元素内部任意控件的命名层次的重新设置,从产生的 ID 中消除 ContentPlaceHolder 和 MasterPage 的 ID(呈现元素的 name 属性没有受到影响,因此对事件、视图状态等的标准 ASP.NET 功能依然保留着)。重新设置命名层次的一个副作用是即使你移动 NamingPanel 元素到不同的 ContentPlaceHolder 控件上,呈现的客户 ID 和之前相同。

注意这取决于你来确保呈现的控件 ID 是唯一的。如果不是,会打断需要唯一 ID 的功能,例如客户的 document.getElementById 函数。

a. 在数据绑定控件中创建可预测的客户 ID

在数据绑定的列表控件中通过遗留算法为控件产生的 ClientID 值既长又无法预测。ClientIDMode 功能可以帮助控制这些 ID 如何产生。

在下面的示例中包含一个 ListView 控件:

< asp:ListView ID ="ListView1" runat ="server" DataSourceID ="SqlDataSource1"
OnSelectedIndexChanged ="ListView1_SelectedIndexChanged"
ClientIDMode ="Predictable"
ClientIDRowSuffix ="ProductID" >
</ asp:ListView >

标记中 ClientIDMode 和 ClientIDRowSuffix 属性都被设置了。ClientIDRowSuffix 属性仅能用于数据绑定控件中,它的行为会因使用的控件而不同。区别在于:

  • GridView 控件 —— 可以指定数据源中一个或多个列的名字,它们会在运行时组合起来创建客户 ID。例如,如果你设置 ClientIDRowSuffix 为 "ProductName, ProductId",呈现元素的控件 ID将拥有类似下面的格式:rootPanel_GridView1_ProductNameLabel_Chai_1
  • ListView 控件 —— 可以指定数据源中的单个列追加到客户 ID。例如,如果你设置 ClientIDSuffix 为 "ProductName",呈现的控件 ID 将拥有类似这样的格式:rootPanel_ListView1_ProductNameLabel_1
  • Repeater 控件 —— 这个控件不支持 ClientIDRowSuffix 属性。在 Repeater 控件中,使用的是当前行的索引。当使用 ClientIDMode="Predictable"时,客户 ID的产生会有这样的格式:Repeater1_ProductNameLabel_0

FormView 和 DetailsView 控件不显示多行,所以它们不支持 ClientIDRowSuffix 属性。

6) 在数据控件中持久化行选择

GridView 和 ListView 控件能够让用户选择一行。在旧版本的 ASP.NET 中,选择是基于页面上的行索引。例如,如果你选择了页面 1 上的第三项然后移动到页面 2,这个页面的第三项会选中。

持久化选中开始仅在 .NET Framework 3.5 SP1 的动态数据(Dynamic Data)项目中支持。当这个特性启用时,当前选中的项是基于项的数据键。这意味着如果在页面 1 选中第三行并移到页面 2,页面 2 上什么也不会选中。当移回到页面 1,第三行仍然被选中。通过使用 EnablePersistedSelection 属性,持久化选中现在在所有项目中支持 GridView 和 ListView 控件,如下所示:

< asp:GridView id ="GridView2" runat ="server" EnablePersistedSelection ="true" >
</ asp:GridView >
7) ASP.NET Chart 控件

ASP.NET Chart 控件在 .NET Framework 中扩展了数据可视化提供。使用 Chart 控件,能够为复杂的统计或金融分析创建拥有直觉的和可视化的直观的图表的 ASP.NET 页面。ASP.NET Chart 控件作为 .NET Framework 3.5 SP1 的附加组件引入,现在是 .NET Framework 4 的一部分。

控件包含下列特性:

  • 35 个不同的图表类型
  • 无限数量的图表区域(areas),标题,图例(legends),和标注(annotation)
  • 大量的外观设置
  • 大多数图表类型的3D支持
  • 能自动适应数据点(fit around data points)的智能数据标签
  • 带状线,刻度中断和对数尺度(Strip lines, scale breaks, and logarithmic scaling)
  • 超过 50 个金融和统计公式用于数据分析和转换
  • 图表数据的简单绑定和操纵
  • 支持通用数据格式,如日期,时间,和货币
  • 支持交互性和事件驱动的定制,包括使用 Ajax 的客户单击事件
  • 状态管理
  • 二进制流

下面的图展示了金融图表的示例:

ASPNET4B1-image2.png

对于如何使用 Chart 控件的更多示例,可以下载 Samples Environment for Microsoft Char Controls 页面的示例代码。在 Chart Control Forum 可以找到更多的示例。

a. 添加 Chart 控件到 ASP.NET 页面

下面的例子展示了如何添加 Chart 控件到 ASP.NET 页面,在示例中,Chart 控件会为静态数据点产生一个柱状图。

< asp:Chart ID ="Chart1" runat ="server" >
< Series >
< asp:Series Name ="Series1" ChartType ="Column" >
< Points >
< asp:DataPoint AxisLabel ="Product A" YValues ="345" />
< asp:DataPoint AxisLabel ="Product B" YValues ="456" />
< asp:DataPoint AxisLabel ="Product C" YValues ="125" />
< asp:DataPoint AxisLabel ="Product D" YValues ="957" /> &

lt;/Points &gt;
</ asp:Series >
</ Series >
< ChartAreas >
< asp:ChartArea Name ="ChartArea1" >
< AxisY IsLogarithmic ="True" />
</ asp:ChartArea >
</ ChartAreas >
< Legends >
< asp:Legend Name ="Legend1" Title ="Product Sales" />
</ Legends >

</ asp:Chart >

b. 使用3-D图表

Chart 控件包含 ChartAreas 集合,包含定义图表区域特征的 ChartArea 对象。例如,要为图表区域使用 3-D,可以像下面的例子那样使用 Area3DStyle 属性:

< asp:ChartArea Name ="ChartArea1" >
< area3dstyle
Rotation ="10"
Perspective ="10"
Enable3D ="True"
Inclination ="15"
IsRightAngleAxes ="False"
WallWidth ="0"
IsClustered ="False" />

<% 1: -- Additional markup here -- %>
</ asp:ChartArea >

下图展示了一个3-D图表。

ASPNET4B1-image3.png

c. 使用刻度中断和对数刻度

这两个特性对图表区域中的每个轴是特定的。例如,为了在图表区域的基本的Y轴使用这些特性,要使用ChartArea 对象中的AxisY.IsLogarithmic 和 ScaleBreakStyle 属性。下面的片断展示了如何在基本Y轴使用刻度中断:

< asp:ChartArea Name ="ChartArea1" >

< axisy >

< ScaleBreakStyle
BreakLineStyle ="Wave"
CollapsibleSpaceThreshold ="40"
Enabled ="True" />
</ axisy >
...
</ asp:ChartArea >

下图展示了效果:

ASPNET4B1-image4.png

8) 使用 QueryExtender 控件过滤数据

一个非常常见的任务是过滤数据。这传统上是通过在数据源控件中生成 Where 子句执行的。这个方法可能很复杂,有时 Where 语法并不能充分利用底层数据库的全部功能。

为了使过滤更容易,一个新的 QueryExtender 控件加入到 ASP.NET 4 中。 这个控件能够被添加到 EntityDataSource 或 LinqDataSource 控件中使能够过滤由这些控件返回的数据。因为 QueryExtender 控件依赖于 LINQ,所以在数据发送到页面之前,过滤要应用到数据库服务器上,这会带来非常高效的操作。

QueryExtender 控件支持各种各样的过滤选项。下列几节描述这些选项并提供如何使用它们的示例。

a. 查找(Search)

对于查找选项,QueryExtender 控件在指定的字段执行查找。在下面的例子中,控件使用输入到 TextBoxSearch 控件中的文本并在从 LinqDataSource 控件返回的数据中的 ProductName 和 Supplier.CompanyName 列中查找其内容。

< asp:LinqDataSource ID ="dataSource" runat ="server" > TableName="Products" &gt;
</ asp:LinqDataSource >
< asp:QueryExtender TargetControlID ="dataSource" runat ="server" >
< asp:SearchExpression DataFields ="ProductName, Supplier.CompanyName"
SearchType ="StartsWith" >
< asp:ControlParameter ControlID ="TextBoxSearch" />
</ asp:SearchExpression >
</ asp:QueryExtender >

b. 范围(Range)

范围选项类似于查找选项,但是要指定一对定义范围的值。在下面的示例中,QueryExtender 控件查找 LinqDataSource 控件返回数据中的 UnitPrice 列。范围从 TextBoxFrom 和 TextBoxTo 控件读取。

< asp:LinqDataSource ID ="dataSource" runat ="server" > TableName="Products" &gt;
</ asp:LinqDataSource >
< asp:QueryExtender TargetControlID ="dataSource" runat ="server" >
< asp:RangeExpression DataField ="UnitPrice" MinType ="Inclusive"
MaxType ="Inclusive" >
< asp:ControlParameter ControlID ="TextBoxFrom" />
< asp:ControlParameter ControlID ="TexBoxTo" />
</ asp:RangeExpression >

</ asp:QueryExtender >

c. 属性表达式(PropertyExpression)

属性表达式选项可以定义对属性值的比较。如果表达式演算为 true,那么被检查的数据会返回。下面的示例中,QueryExtender 控件通过比较 Disontinued 列中的数据和来自页面 CheckBoxDiscontinued 控件的值过滤数据。

< asp:LinqDataSource ID ="dataSource" runat ="server" TableName ="Products" >
</ asp:LinqDataSource >
< asp:QueryExtender TargetControlID ="dataSource" runat ="server" >
< asp:PropertyExpression >
< asp:ControlParameter ControlID ="CheckBoxDiscontinued" Name ="Discontinued" />
</ asp:PropertyExpression >
</ asp:QueryExtender >

d. 自定义表达式(CustomExpression)

这个选项会调用页面中的函数,此函数定义了自定义过滤逻辑。下面的示例展示了如何声明式地指定一个自定义表达式。

< asp:LinqDataSource ID ="dataSource" runat ="server" TableName ="Products" >
</ asp:LinqDataSource >
< asp:QueryExtender TargetControlID ="dataSource" runat ="server" >
< asp:CustomExpression OnQuerying ="FilterProducts" />
</ asp:QueryExtender >

下面的示例展示了会被 QueryExtender 控件调用的自定义函数。

protected void FilterProducts( object sender, CustomExpressionEventArgs e)
{
e.Query = from p in e.Query.Cast()
where p.UnitPrice &gt;= 10
select p;
}

这些示例仅展示了在 QueryExtender 控件中一次只使用一种表达式。然而,你能够在 QueryExtender 控件内部包含多种表达式。

9) HTML 编码的代码表达式

一些 ASP.NET 站点(尤其是使用 ASP.NET MVC)严重依赖使用 <%= expression %> 语法(常称作"Code nuggets")来编写一些文本到响应。当使用代码表达式时,很容易就会忘记 HTML 编码这些文本,如果文本来自用户输入,它可能会让页面对跨站点脚本(XSS)***开放。

ASP.NET 4 为代码表达式引入了下面的新语法:

<%: expression %>

当定入到响应时,这种语法默认使用 HTML 编码。这种新表达式实际会转换成下面的:

<%= HttpUtility.HtmlEncode(expression) %>

例如,<%: Request["UserInput"] %> 会在 Request["UserInput"] 的值上面执行 HTML 编码。

这个特性的目标是使得用新语法代替老语法的所有实例成为可能,这样就不用强迫在每个步骤决定要使用哪一个。然而,有一些情况即正在输出的文本哪些是 HTML 还是已经编码过了,这会导致双重编码。

为了解决这些情况,ASP.NET 4 引入了一个新接口,IHtmlString,和具体实现,HtmlString。这些类型的实例表示对要显示为 HTML 的返回值已经恰当地编码了(或另外检查了),因此值不必在被编码了。例如,下面不应当被(也没被)HTML编码:

<% 1: : new HtmlString( "&lt;strong>HTML that is not encoded</strong>") %&gt;

ASP.NET MVC 2 辅助方法已经被更新为和这种新语法一起工作这样就不会被双重编码了,但仅当你在运行 ASP.NET 4 的时候。这种新语法在应用程序使用 ASP.NET 3.5 SP1 运行的时候是不工作的。

记住这不保证来自 XSS ***的保护。例如,使用不包含在引号中的属性值的 HTML 能够包含依然会受影响的用户输入。注意 ASP.NET 控件的输出和 ASP.NET MVC 辅助方法总是把属性值包含在引号中,这是推荐的方法。

同样,这种语法不会执行 JavaScript 编码,例如在基于用户输入创建一个 JavaScript 字符串时。

10) 项目模板变化

在旧版本的 ASP.NET 提供的项目模板(一个空项目模板和一个仅有默认文档的模板)不利于指导初学者。因此,ASP.NET 4 引入了三个新模板,一个为空 Web 应用程序项目,另两个为一个 Web 应用程序和一个为网站项目。

a. 空 Web 应用程序模板

仅有一个 web.config 文件:

ASPNET4B1-image9.png

b. Web 应用程序和网站项目模板

ASPNET4B1-image10.png

项目包含大量文件。另外,Web 应用程序项目使用基本的会员功能进行配置,这可以快速地在安全访问新应用程序中开始起动。所以 web.config 文件包含了用于配置会员、角色和profiles的条目。

ASPNET4B1-image11_b.png

项目还在 Account 目录中包含第二个 Web.config 文件。这第二个配置文件提供了一种对非登录用户安全访问 ChangePassword.aspx 页面的方法。例如:

ASPNET4B1-image12.png

默认创建的页面同样包含很多内容。项目包含一个默认的母版页和 CSS 文件,以及一个配置为默认使用母版页的默认页面(Default.aspx)。结果就是当你第一次运行 Web 应用程序或网站时,默认页面(主页)就已经就绪了。事实上,当启动一个新的 MVC 应用程序时默认页面是相似的。

项目模板的这些改变的意图是提供一个指南,即如何开始构建一个新的 Web 应用程序。使用语义正确的,严格遵从 XHTML 1.0 的标记和使用 CSS 指定布局,模板中的页面为构建 ASP.NET 4 Web应用程序提供了最佳实践。

11) CSS 增强

ASP.NET 4 中的一个主要工作方面是帮助呈现遵从最新HTML标准的HTML。这包括ASP.NET Web服务器控件如何使用 CSS 样式。

a. 针对呈现的兼容性设置

默认地,当 Web 应用程序或网站针对 .NET Framework 4 时,页面元素的 controlRenderingCompatibilityVersion 属性会设置为 "4.0"。这个元素定义在机器级别的 Web.config 文件中,默认应用到所有的 ASP.NET 4 应用程序上:

< system.web >
< pages controlRenderingCompatibilityVersion ="3.5|4.0" />
</ system.web >
controlRenderingCompatibilityVersion 是一个字符串,在当前发布版本中,支持下列的值:
  • "3.5"。这个设置表示遗留的呈现和标记。控件呈现的标记是100%向后兼容的,而 xhtmlConformance 属性是承兑的(honored 怎么翻译?)
  • "4.0"。如果是这个设置,ASP.NET 服务器控件会做下列事情:
    • xhtmlConformance 属性总是被视为 "Strict"。
    • 非输入控件不再呈现无效样式。
    • 隐藏域周围的 div 元素现在是有样式的,这样它们就不会妨碍用户创建的 CSS 规则。
    • Menu 控件呈现的标记是语义正确的而且遵从可访问性指南。
    • 验证控件不再呈现内联样式。
    • 以前会呈现 border="0" 的控件(即从ASP.NET Table 控件和 Image 控件派生的控件)不再呈现这个属性。

b. 禁用控件

在 ASP.NET 3.5 SP1 和更早版本中,框架会为任何 Enabled 属性设置为 false 的控件在 HTML 中呈现 disabled 属性。然而根据 HTML 4.01 规范,只有 input 元素才应当有这个属性。

在 ASP.NET 4 中,可以把 controlRenderingCompatibilityVersion 属性设置为 "3.5",例如:

< system.web >
< pages controlRenderingCompatibilityVersion ="3.5" />
</ system.web >

这时创建一个 Label 控件并禁用此控件,例如:

< asp:Label id ="Label" runat ="server" Text ="Test" Enabled ="false" >

Label 控件会呈现如下的 HTML:

< span id ="Label1" disabled ="disabled" >Test </ span >

在 ASP.NET 4 中,可以把 controlRenderingCompatibilityVersion 为 "4.0",这时,当控件的 Enabled 属性设置为 false 时,只有呈现 input 元素的控件才会呈现 disabled 属性。不呈现为 HTML input 元素的控件则会呈现一个 class 属性引用一个 CSS 类,可以用来定义控件的禁用外观。例如,前面示例中展示的 Label 控件会产生下面的标记:

< span id ="Span1" class ="aspnetdisabled" >Test </ span >

为这个控件指定的类的默认值是 "aspnetdisabled"。通过设置 WebControl 类的静态属性 DisabledCssClass 可以更改这个默认值。对于控件开发人员,对特定控件使用的行为也可以使用 SupportsDisabledAttribute 属性定义。

12) 隐藏围绕隐藏域的 DIV 元素

ASP.NET 2.0 及以后的版本会把系统指定的隐藏域(例如用于存储视图状态信息的 hidden 元素)呈现在 div 元素中,这是为了遵从 XHTML 标准。然而,当 CSS 规则影响 div 元素时会导致问题。例如,它可能在页面隐藏的 div 元素上显示 1 像素的线。在 ASP.NET 4 中,包装了隐藏域的 div 元素会添加一个 CSS 类引用,如下所示:

< div class ="aspNetHidden" >... </ div >

接着可以定义一个 CSS 类只应用到隐藏元素。如下面所示:

< style type ="text/css" >
DIV# aspNetHidden {border:0;}

</ style >
13) 为模板控件呈现外部表格

默认地,下面支持模板的 ASP.NET Web 服务器控件都会自动地包围在外部表格的里面并应用内联样式:

  • FormView
  • Login
  • PasswordRecovery
  • ChangePassword
  • Wizard
  • CreateUserWizard

一个新的名为 RenderOuterTable 的属性添加到这些控件上来允许外部表格从标记中移除。例如,考虑下面的 FormView 控件的例子:

< asp:FormView ID ="FormView1" runat ="server" >
< ItemTemplate >
Content
</ ItemTemplate >

</ asp:FormView >

这个标记会呈现下面的输出到页面,其中包含一个 HTML 表格:

< table cellspacing ="0" border ="0" id ="Table1" style ="border-collapse:collapse;" >
< tr >
< td colspan ="2" >
Content
</ td >
</ tr >

</ table >

为了阻止表格被呈现,可以设置 FormView 控件的 RenderOuterTable 属性为 false,如下面所示:

< asp:FormView ID ="FormView1" runat ="server" RenderOuterTable ="false" >

这时输出就不会有 table, tr, 和 td 元素了,只有模板中的内容。

这个增强使得使用 CSS 对内容设置样式变得更加容易,因为没有未料到的标签被呈现。

注意这个变化禁止了对 Visual Studio 2010 设计器中的自动格式化功能的支持。

14) ListView 控件增强

ListView 控件在 ASP.NET 4 中变得更容易使用了。旧版本的这个控件需要指定一个布局模板,其中包含一个规定 ID 的服务器控件。下面的例子展示了在 ASP.NET 3.5 中 ListView 的典型示例:

< asp:ListView ID ="ListView1" runat ="server" >
< LayoutTemplate >
< asp:PlaceHolder ID ="ItemPlaceHolder" runat ="server" ></ asp:PlaceHolder >
</ LayoutTemplate >
< ItemTemplate >
<% 1: Eval( "LastName") %>
</ ItemTemplate >
</ asp:ListView >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

在ASP.NET 4中,ListView 控件不再需要布局模板,前面的例子可以由下面的标记取代:

< asp:ListView ID ="ListView1" runat ="server" >
< ItemTemplate >
<% 1: Eval( "LastName") %>
</ ItemTemplate >
</ asp:ListView >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } 15) CheckBoxList 和 RadioButtonList 控件增强

在 ASP.NET 3.5 中,使用两个值(Flow 和 Table)可以为 CheckBoxList 和 RadioButtonList 指定布局。例如:

< asp:CheckBoxList ID ="CheckBoxList1" runat ="server" RepeatLayout ="Flow" >
< asp:ListItem Text ="CheckBoxList" Value ="cbl" />
</ asp:CheckBoxList >

< asp:RadioButtonList runat ="server" RepeatLayout ="Table" >
< asp:ListItem Text ="RadioButtonList" Value ="rbl" />
</ asp:RadioButtonList >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

默认地,控件会呈现如下类似的 HTML:

< span id ="Span2" >< input id ="CheckBoxList1_0" type ="checkbox"
name ="CheckBoxList1$0" />< label for ="CheckBoxList1_0" >CheckBoxList </ label ></ span >

< table id ="RadioButtonList1" border ="0" >
< tr >
< td >< input id ="RadioButtonList1_0" type ="radio" name ="RadioButtonList1" value ="rbl" />< label for ="RadioButtonList1_0" >RadioButtonList </ label ></ td >
</ tr >
</ table >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

因为这些控件包含项的列表,要呈现语义正确的 HTML,应当使用 HTML 列表(li)呈现内容。

在 ASP.NET 4 中,CheckBoxList 和 RadioButtonList 控件的 RepeatLayout 属性支持两个新值:OrderedList 和 UnorderedList。

下面的例子展示了如何使用这些新值:

< asp:CheckBoxList ID ="CheckBoxList1" runat ="server"
RepeatLayout ="OrderedList" >
< asp:ListItem Text ="CheckBoxList" Value ="cbl" />
</ asp:CheckBoxList >

< asp:RadioButtonList ID ="RadioButtonList1" runat ="server"
RepeatLayout ="UnorderedList" >
< asp:ListItem Text ="RadioButtonList" Value ="rbl" />
</ asp:RadioButtonList >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

产生的HTML:

< ol id ="CheckBoxList1" >
< li >< input id ="CheckBoxList1_0" type ="checkbox" name ="CheckBoxList1$0" value ="cbl" />< label for ="CheckBoxList1_0" >CheckBoxList </ label ></ li >
</ ol >

< ul id ="RadioButtonList1" >
< li >< input id ="RadioButtonList1_0" type ="radio" name ="RadioButtonList1" value ="rbl" />< label for ="RadioButtonList1_0" >RadioButtonList </ label ></ li >
</ ul >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

注意,如果把 RepeatLayout 设置为新值,RepeatDirection 属性就不能再使用,否则会在运行时抛出异常。这些控件的视觉布局可使用 CSS 定义。

16) Menu 控件增强

在 ASP.NET 4 之前,Menu 控件呈现为一系列 HTML 表格。这使得应用 CSS 样式变得很困难,而且也不遵从可访问性标准。

在 ASP.NET 4 中,这个控件现在使用语义标记呈现,即由一个无序列表和列表元素组成。下面的例子展示了 Menu 控件在页面中的标记:

< asp:Menu ID ="Menu1" runat ="server" >
< Items > < asp:MenuItem Text ="Home" Value ="Home" />
< asp:MenuItem Text ="About" Value ="About" />
</ Items >
</ asp:Menu >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

当页面呈现时,控件会产生下列 HTML:

< div id ="Menu1" >
< ul >
< li >< a href ="#" onclick ="..." >Home </ a ></ li >
< li >< a href ="#" onclick ="..." >About </ a ></ li >
</ ul >
</ div >
< script type ="text/javascript" > 1: 2: new Sys.WebForms.Menu( 'Menu1'); </ script >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

除了呈现的增强,菜单的键盘导航也使用焦点管理改进了。当 Menu 控件获得焦点时,可以使用箭头键导航元素。Menu 控件现在还能附加可访问富互联网应用程序(ARIA)角色和属性,以增强可访问能力。(见 Menu ARIA guidelines

为菜单控件的样式呈现在页面顶部的样式块中,而不是内联在呈现的 HTML 元素中。如果想完全控制对控件的样式,可以把新的 IncludeStyleBlock 属性设置为 false,这会让样式块不被发出。另外,可以先运行页面,把呈现的样式块复制到外部 CSS 文件,再把控件的 IncludeStyleBlock 属性设置为 false。这样菜单外观就被定义到外部样式表中了。

17) Wizard 和 CreateUserWizard 控件

ASP.NET Wizard 和 CreateUserWizard 控件支持模板,用来定义呈现的 HTML。(CreateUserWizard 派生自 Wizard)下面的示例展示了一个完全模板化的 CreateUserWizard 控件的标记:

< asp:CreateUserWizard ID ="CreateUserWizard1" runat ="server" ActiveStepIndex ="0" >
< HeaderTemplate >
</ HeaderTemplate >

< SideBarTemplate >
</ SideBarTemplate >

< StepNavigationTemplate >
</ StepNavigationTemplate >

< StartNavigationTemplate >
</ StartNavigationTemplate >

< FinishNavigationTemplate >
</ FinishNavigationTemplate >

< WizardSteps >
< asp:CreateUserWizardStep ID ="CreateUserWizardStep1" runat ="server" >
< ContentTemplate >
</ ContentTemplate >

< CustomNavigationTemplate >
</ CustomNavigationTemplate >

</ asp:CreateUserWizardStep >

< asp:CompleteWizardStep ID ="CompleteWizardStep1" runat ="server" >
< ContentTemplate >
</ ContentTemplate >
</ asp:CompleteWizardStep >

</ WizardSteps >
</ asp:CreateUserWizard >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

控件呈现类似下面的 HTML:

< table cellspacing ="0" cellpadding ="0" border ="0" id ="CreateUserWizard1" style ="border-collapse:collapse;" >
< tr >
< td >Header </ td >
</ tr >
< tr style ="height:100%;" >
< td >
< table cellspacing ="0" cellpadding ="0" border ="0" style ="height:100%;;border-collapse:collapse;" >
< tr >
< td style ="height:100%;;" ></ td >
</ tr >
</ table >
</ td >
</ tr >
< tr >
< td align ="right" ></ td >
</ tr >
</ table >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

在 ASP.NET 3.5 SP1 中,尽管可以改变模板的内容,但仍然对控制 Wizard 控件的输出有限制。在ASP.NET 4中,可以创建一个 LayoutTemplate 模板并插入 PlaceHolder 控件(使用保留的名称)来指示想让 Wizard 控件如何呈现。下面的示例展示了这个过程:

< asp:CreateUserWizard ID ="CreateUserWizard1" runat ="server" ActiveStepIndex ="1" >
< LayoutTemplate >
< asp:PlaceHolder ID ="headerPlaceholder" runat ="server" />
< asp:PlaceHolder ID ="sideBarPlaceholder" runat ="server" />
< asp:PlaceHolder id ="wizardStepPlaceholder" runat ="server" />
< asp:PlaceHolder id ="navigationPlaceholder" runat ="server" />
</ LayoutTemplate >
< HeaderTemplate >
Header
</ HeaderTemplate >
< WizardSteps >
< asp:CreateUserWizardStep runat ="server" >
< ContentTemplate >
</ ContentTemplate >
</ asp:CreateUserWizardStep >
< asp:CompleteWizardStep runat ="server" >
< ContentTemplate >
</ ContentTemplate >
</ asp:CreateUserWizardStep >
</ WizardSteps >
</ asp:CreateUserWizard >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

示例包含了下列在 LayoutTemplate 元素中的命名占位符:

  • headerPlaceHolder——在运行时,这会被 HeaderTemplate 元素中的内容取代
  • sideBarPlaceHolder——在运行时,这会被 SideBarTemplate 元素的内容取代
  • wizardStepPlaceHolder——在运行时,这会被 WizardStepTemplate 元素的内容取代
  • navigationPlaceHolder——在运行时,这会被定义的导航模板所取代

示例中的标记使用占位符呈现了下列的 HTML(定义的模板中没有实际定义的内容):

< span >
</ span >
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

唯一的不是用户定义的 HTML 是 span 元素。(我们期望在未来的发布中,甚至连 span 元素也不会呈现。)