本教程结合实例演示NBear.Web提供的Page/MasterPage/UserControl扩展基类。您将看到,使用这些基类能大大提高ASP.NET 2.0网页开发速度,轻松获得Ajax和多语言支持等功能。对应的源码包含在从sf.net下载的zip包中的tutorials\Web_Tutorial目录中。
内容目录
1、Helper Methods - 辅助Web常用方法
2、ClientScriptFactory - 辅助生成客户端JS脚本
3、LoadResources()/GetString() - 多语言支持
4、AjaxHelper - 基于UserControl的AjaxHelper集成
从SF.NET下载NBear最新版本程序集、源码及使用教程
0、重要说明
本文讨论的内容只涉及NBear.Web这一个程序集,如果要在您自己的Web项目中使用本文所讨论的功能,只需要添加到NBear.Web.dll的引用。NBear是一个基于.Net Framework 2.0/C# 2.0/ASP.NET 2.0的框架,因此,本文讨论的内容只能用于ASP.NET 2.0开发。
本文只讨论了NBear.Web中由NBear.Web.UI命名空间下的Page/MasterPage/UserControl这组扩展基类中定义的功能,因此,
要在网页中使用这些组件的功能,必须将这些类设为您的WebForm页面,MasterPage页面或User Control的基类。
由于为Page,MasterPage和UserControl这三个基类所作的扩展基本上是等价的,所以,本文下面的内容,主要演示对于Page这个基类的扩展功能,对于MasterPage和UserControl使用方法类似,只需保证使MasterPage和UserControl继承相应的基类即可。
1、Helper Methods
示例代码中的HelperMethodsDemo.aspx页面演示了一组NBear.Web.UI.Page基类的扩展辅助方法。
1) GetIntParam()/GetStringParam()/GetDateTimeParam()。这组方法用于方便地获取从QueryString和Form传递而来的参数。如果网页包含相同键值的Form参数和QueryString参数,则优先返回Form参数。
下面的代码简单的使用这组函数,并输出参数的值:
Response.Write(
string
.Format(
"
intParam = {0}<br />
"
, GetIntParam(
"
intParam
"
,
0
)));
Response.Write(
string
.Format(
"
strParam = {0}<br />
"
, GetStringParam(
"
strParam
"
,
string
.Empty)));
Response.Write(
string
.Format(
"
dtParam = {0}<br />
"
, GetDateTimeParam(
"
dtParam
"
, DateTime.MinValue)));
注意,这组函数包含两个参数,第二个参数指定默认的错误返回值,如果指定的键值不存在,或类型转换失败,则返回该错误返回值。
2) StrongTyped<IEntityType>()。该方法用于将一个任意类型的对象转换为指定的强类型对象,从而简化形如(ReturnType)obj这样的语法。该方法尤其适合于用于页面的数据绑定表达式中,代替基于反射的eval语法。
下面的示例将一个弱类型的Session值转换为一个强类型的对象:
Session[
"
hello time
"
]
=
DateTime.Now;
Response.Write(
string
.Format(
"
Test StrongTyped<T>(), current time ticks = {0}<br />
"
, StrongTyped
<
DateTime
>
(Session[
"
hello time
"
]).Ticks));
3) TextToHtml()/ToXXXString()。这组方法帮助进行常见的字符串转换。执行示例页面可以看到其中两个方法的演示,这里就不列举了。
2、ClientScriptFactory
可以通过 Page.ClientScriptFactory访问一组用于生成客户端JS脚本的服务端方法,包含了常见的一些JS代码的构造封装。在服务端构造客户端JS的好处可以使得构造客户端脚本的过程获得编译期的错误检测,避免手写JS代码的一些不小心的不那么容易调试的错误。
示例代码中的ClientScriptFactoryDemo.aspx页面演示了使用ClientScriptFactory的两种典型场景。
一种是在aspx页面中直接输出脚本。例如:
<%
=
ClientScriptFactory.WrapScriptTag(ClientScriptFactory.PopAlert(
"
Page Loaded
"
))
%>
该语句将直接向页面输出一个<script>块,包含一个alert效果。注意,如果不使用WrapScriptTag的话,自然生成的脚本就不会包含在一个script中,当在某一段script块中插入部分JS片段时,不需包含WrapScriptTag。
第二种也是非常常见的使用方法,则是在页面的某个Postback方法执行完毕后,直接在事件处理函数中Response一段脚本,给出一个客户端提示。例如,下面的代码在一个事件处理函数中输出了一段相对复杂的客户端JS脚本:
Response.Write(ClientScriptFactory.WrapScriptTag(
string
.Format(
"
var result = {0}; if (result) {1} else {2}
"
, ClientScriptFactory.PopConfirm(
"
Are you sure?
"
), ClientScriptFactory.PopAlert(
"
Yes
"
), ClientScriptFactory.PopAlert(
"
No
"
)), ClientScriptFactory.PopAlert(
"
Done
"
)));
3、LoadResources()/GetString()
这组方法提供了基于.resx和.resources资源文件,支持多语言网站构架的功能。Global.asax和GlobalizationDemo.aspx页面演示了这组方法的使用。
注:ASP.NET 2.0本身就提供了很不错的多语言支持方案,本组件中提供的可以作为另一个选择,但是一般还是推荐使用ASP.NET 2.0默认提供的方案。
在Global.asax中的Application_Start事件中,初始化资源为一个默认语言:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
if (NBear.Web.UI.WebHelper.DefaultCulture.ToString().ToLower() != "en-us")
{
NBear.Web.UI.WebHelper.DefaultCulture = new System.Globalization.CultureInfo("zh-CN");
}
NBear.Web.UI.WebHelper.LoadResources("Strings");
}
在GlobalizationDemo.aspx页面中,包含了一个DropDownList用于更换指定语言的资源,同时使用GetString()输出一个定义在资源文件中的Hello键值的文本资源。改变DropDownList中的语言选择,将可以看到,页面会显示取自不同的资源文件的文本资源。
下面是页面的code hehind,页面载入时,设置当前的默认语言,并且在DropDownList的DropDownList1_SelectedIndexChanged事件处理中,在选择的语言变化时,修改载入的资源:
public
partial
class
GlobalizationDemo : NBear.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
for (int i = 0; i < DropDownList1.Items.Count; i++)
{
ListItem item = DropDownList1.Items[i];
if (item.Value.ToLower() == NBear.Web.UI.WebHelper.DefaultCulture.ToString().ToLower())
{
DropDownList1.SelectedIndex = i;
}
}
}
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
NBear.Web.UI.WebHelper.DefaultCulture = new CultureInfo(DropDownList1.SelectedValue);
NBear.Web.UI.WebHelper.LoadResources("Strings");
}
}
那么这些资源在哪里定义的呢?
在ASP.NET 2.0下,资源文件一般需要定义在App_GlobalResources特别目录中,当然实际上定义在别处也没关系。我们的示例中,在这个目录下定义了两个resx文件Strings.en-US.resx和Strings.zh-CN.resx。分别对应中英文两个语言。这两个文件中都只包含了一个Hello键值及对应的文本内容。
另外,App_GlobalResources目录中还包含了一个ResGen.cmd文件,这是一个批处理脚本,它用于简化将resx编译为.resources文件到bin目录。必须将resx文件编译为bin目录下的.resources文件,才能被框架访问到。ResGen.cmd文件的内容如下:
@echo off
"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\resgen.exe" Strings.en-US.resx ..\bin\Strings.en-US.resources
"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\resgen.exe" Strings.zh-CN.resx ..\bin\Strings.zh-CN.resources
注意,如果要修改或添加资源项目,只需要修改resx文件,并重新点击ResGen.cmd编译资源文件即可,不需重新编译整个网站程序。
4、AjaxHelper
AjaxHelper源自Teddy以前的一个同名Ajax框架。原来的实现基于对prototype这个开源Ajax实现的封装,实现了ASP.NET下基于UserControl实现各种Ajax效果的功能。自动ASP.NET 2.0发布以后,Teddy已经将AjaxHelper完美集成到NBear中的扩展页面基类中,并且使用ASP.NET 2.0内置的Callback机制代替原来的prototype依赖。
集成到NBear使得基于这个集成本版的AjaxHelper的使用更简单优雅。
示例代码中的AjaxDemo.aspx和AjaxDemo2.aspx这两个页面演示了集成到Page基类的Ajax功能的使用。
NBear中目前这个AjaxHelper支持两中使用模式。
模式一,不支持浏览器刷新模式。
所谓不支持浏览器刷新指的是,在打开页面,并以该模式执行了某些Ajax效果后,如果点击浏览器的刷新按钮,或者按F5,页面状态会回复到初始载入状态。
AjaxDemo.aspx页面演示了该模式的Ajax回调的使用。
首先,只要使用AjaxHelper效果的页面,必须重载EnableAjaxCallback属性,并返回true,否则,页面生成的代码不会带有用于Ajax回调的支持脚本,所有的Ajax效果都不会生效。可以在code behind中,象下面这样重载EnableAjaxCallback属性:
public
partial
class
AjaxDemo : NBear.Web.UI.Page
{
protected override bool EnableAjaxCallback
{
get
{
return true;
}
}
}
再来看AjaxDemo.aspx页面的内容:
<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="AjaxDemo.aspx.cs" Inherits="AjaxDemo"
%>
<!
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
>
Untitled Page
</
title
>
<
script
language
="javascript"
type
="text/javascript"
>
function customFunc(data)
{
alert(data);
}
</
script
>
</
head
>
<
body
>
<
form
id
="form1"
runat
="server"
>
<
div
>
<
input
type
="button"
value
="Test Callback"
onclick
="<%= Ajax.Callback("
AjaxTemplates/Simple", null, "customFunc") %
>
" />
<
input
type
="button"
value
="Test Update"
onclick
="<%= Ajax.Update("
AjaxTemplates/DataBinding", "outputElementId", "num
=5",
"customFunc") %
>
" />
<
br
/>
<
br
/>
<
div
id
="outputElementId"
></
div
>
</
div
>
</
form
>
</
body
>
</
html
>
注意,页面中定义了两个按钮,它们的onclick事件处理包含了Ajax调用代码。
Ajax.Callback(string ajaxTemplate, string parms, string onComplete)
- 该方法将会执行一个对指定的ajaxTemplate的回调,可以通过parms传递形如"paramName1=1¶mName2=2"这样的类似QueryString的参数,第三个参数onComplete是一个用于对回调返回的内容进行处理的客户端脚本函数的名称。
Ajax.Update(string ajaxTemplate, string outputElementId, string parms, string onComplete)
- 该方法和Ajax.Callback的区别是,它多了一个参数outputElementId,可以指定一个包含innerHTML属性的标签(如div,span等)的Id,回调成功的内容在交给onComlpete前,会被自动替换到outputElementId指定的标签的innerHTML属性内容。当然,也可以不指定onComplete参数,传递一个null就行。
注意,页面中Ajax.Callback和Ajax.Update分别调用了两个AjaxTemplate:Simple和DataBinding。这两个AjaxTemplate实际上是两个特殊的UserControl,它们定于AjaxTemplates目录中。
所有的用作AjaxTemplate的User Control必须从NBear.Web.UI.AjaxTemplate类继承。并且,如果要对页面面内容进行code behind的处理,如进行数据绑定,不能在Page_Load中进行处理,而必须重载OnAjaxTemplatePreRender()方法进行处理。Page_Load中的代码永远不会被执行。
下面简单列举Simple和DataBinding的代码。
注意Simple只在包含ascx页面包含了一个简单文本,不包含任何有效code behind代码:
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="Simple.ascx.cs" Inherits="AjaxTemplates_Simple"
%>
Hello Simple!
DataBinding在ascx页面包含了一个空的GridView控件如下:
<%
@ Control Language="C#" AutoEventWireup="true" CodeFile="DataBinding.ascx.cs" Inherits="AjaxTemplates_DataBinding"
%>
<
asp:GridView
ID
="GridView1"
runat
="server"
>
</
asp:GridView
>
同时DataBinding.ascx.cs这个code behind文件中,对GridView1绑定了一些数据,注意,我们重载了OnAjaxTemplatePreRender函数来执行绑定。另外,你可以看到,我们从OnAjaxTemplatePreRender的callbackParams参数中获得了从AjaxDemo.aspx页面的Button2传递过来的num参数。
public
partial
class
AjaxTemplates_DataBinding : NBear.Web.UI.AjaxTemplate
{
private SampleEntity[] objs = null;
private void LoadSampleEntities(int num)
{
objs = new SampleEntity[num];
for (int i = 0; i < num; i++)
{
objs[i] = EntityFactory<SampleEntity>.CreateObject();
objs[i].ID = i + 1;
objs[i].Name = Guid.NewGuid().ToString();
}
}
public override void OnAjaxTemplatePreRender(System.Collections.Generic.Dictionary<string, string> callbackParams)
{
LoadSampleEntities(int.Parse(callbackParams["num"]));
GridView1.DataSource = objs;
GridView1.DataBind();
}
}
模式二,支持浏览器刷新模式。
相对于模式一,采用模式二时,浏览器刷新时,之前的最后一次Ajax回调能够被记忆,并在页面载入后自动重新执行。
AjaxDemo2.aspx演示了采用模式二使用AjaxHelper的功能。
注意,AjaxDemo2.aspx和AjaxDemo.aspx的唯一不同只在于aspx文件,参见下面的AjaxDemo2.aspx的代码:
<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="AjaxDemo2.aspx.cs" Inherits="AjaxDemo2"
%>
<!
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
id
="Head1"
runat
="server"
>
<
title
>
Untitled Page
</
title
>
<
script
language
="javascript"
type
="text/javascript"
>
function customFunc(data)
{
alert(data);
}
</
script
>
</
head
>
<
body
onload="<%= Ajax.OnPageLoad() %>"
>
<
form
id
="form1"
runat
="server"
>
<
div
>
<
input
type
="button"
value
="Test Callback"
onclick
="<%= Ajax.Callback2("
AjaxTemplates/Simple", null, "customFunc") %
>
" />
<
input
type
="button"
value
="Test Update"
onclick
="<%= Ajax.Update2("
AjaxTemplates/DataBinding", "outputElementId", "num
=5",
"customFunc") %
>
" />
<
br
/>
<
br
/>
<
div
id
="outputElementId"
></
div
>
</
div
>
</
form
>
</
body
>
</
html
>
和AjaxDemo.aspx页面比较,您可以发现两点细微的区别,请注意粗体的代码。
第一点不同是,页面的body标签多了一个onload事件处理,这个事件处理的代码的作用就是在页面loaded之后,自动重新执行上一次模式二方式执行的Ajax回调。
第二点不同是,此时使用Ajax.Callback2和代替了模式一中的AjaxUpdate2,他们的参数列表和含义完全相同。
如果运行AjaxDemo2.aspx页面,并点击按钮,您就会发现,在点击按钮的同时,页面的地址栏部分会被附加一个#...的代码,这里就包含了上一次Ajax.Callback2或Ajax.Update2执行时的所有参数。Ajax.OnPageLoad()就是根据这些参数来重新执行上一次Ajax回调的。
实际上,模式一和模式二完全可以在同一个页面混合使用,只需记住,只有Ajax.Callback2或Ajax.Update2执行的上一次(仅仅一次,在之前的Ajax回调是不记忆的)Ajax回调可以在页面刷新时被自动重复执行。
//本文结束