ASP.NET:就是喜欢Web Form,就是喜欢拖控件(五)
ASP.NET:就是喜欢Web Form,就是喜欢拖控件(一)
ASP.NET:就是喜欢Web Form,就是喜欢拖控件(二)
ASP.NET:就是喜欢Web Form,就是喜欢拖控件(三)
ASP.NET:就是喜欢Web Form,就是喜欢拖控件(四)
话说拖控件这种极端懒惰和对自己技术前途极其不负责任的行为受到了一些同学的热情批评。如果一个程序员的技能仅仅依靠着IDE的功能的话,一旦遇到人家不提供的功能,轮到我们自己写的时候就不知道怎么办了。怎么办?
相信之前的几篇中,大家已经对ASP.NET原生控件的强大扩展能力有了深刻印象,然而实际开发中,即使是这些如此优秀的控件,也不可能完全覆盖千变万化的用户需求。而对我而言,这种情况下我不会放弃拖控件,而是——自己写需要的控件。
然而在自己动手之前,我还是要先推荐大家看看微软官方的扩展控件库http://ajaxcontroltoolkit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33804
也许你需要的控件就在其中。
这一篇里,我将用一个闲暇时在搞的Web应用中的一个需求做为例子。先简单介绍一下背景,这是一个黑白棋(什么是黑白棋?)棋谱管理的网站。它的数据库保存了1977-2009年黑白棋的重要比赛对局。数据库设计如下:
其中Contest表保存比赛信息,Contestant表保存选手信息。Game表保存所有对局,ContestID是指向比赛的外键,BlackContestantID和WhiteContestantID分别是执白执黑两个棋手的ID。当然于是我们有个按这些外键关系连接好的视图,数据看起来像下面这样:
通过GridView,我们可以很容易的把比赛名称、棋手名称、黑色最终得分等信息显示出来。然而做为一个棋谱管理网站,用户显然希望看到这些对局的具体过程:
我想就算ASP.NET类库再全面,也做不出能自定义成这样的控件。所以我们必须自己动手了。显然我们这个控件在客户端有着复杂的逻辑,必须使用JavaScript来支持,为了方便我们选择建立AJAX Control(其实并不是一定要AJAX才用AJAX Control,AJAX Control的优点之一是可以使用ScriptManager来管理脚本文件。)
为ASP.NET控件建立一个新项目。
首先这个控件名字需要作一些修改,记得改变JS文件名后,同步更新AssemblyInfo.cs。
ASP.NET控件建立之后,在控件中重载Render方法就可以控制控件产生的HTML文本:
实际上,如果不是想要完全控制产生的HTML,可以重载其它一些方法(见上图中RenderBeginTag等)只改变其中一部分。ScriptControl默认会产生一个带id的span元素,一般不推荐完全重写Render方法,如果要重写,也应该保证ClientID的正确性。
ScriptControl会自动向页面注册需要的JScript文件,且多个控件同时存在时不会导致重复加载JS文件,默认生成的GetScriptReferences方法就是用来处理JScript文件的依赖关系的。默认情况下只有一个脚本依赖,但因为GetScriptReferences返回一个IEnumerable<ScriptReference>,所以如果你需要依赖多个脚本文件,也可以做到。
控件客户端JS在建立的时候已经生成了一个示例。
但还有一些需要注意的地方,大部分时候,我们手里可能有了一些纯客户端的控件,对于已经存在的一些JS控件,我们需要把它重构到ASP.NET客户端要求的格式。
第一步:注册命名空间
在控件JS代码中,我们可以使用微软的客户端AJAX框架提供的所有扩展方法。所以可以使用Type.registerNamespace注册一个命名空间。
第二步:注册类
在原始的客户端控件中,应该已经有写好的控件类,我们需要用ClassName.registerClass注册以及指定基类。registerClass已经由框架扩展到Function.prototype上,任何写好的类都可以直接调用了。例子:
Type.registerNamespace("Othello");
Othello.OthelloEditor.registerClass('Othello.OthelloEditor', Sys.UI.Control);
注意,如果是跟服务端控件对应的客户端控件类,必须用registerClass继承Sys.UI.Control,而假如我们有些纯客户端逻辑类(不需要继承基类),我们可以直接扩展到注册好的命名空间上,无需使用registerClass,如:
Othello.OthelloGame = OthelloGame;
第三步:在文件最后加入if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
接下来我们控件的服务端和客户端都有了之后,就可以添加为我们的控件添加属性了。下面是一个例子:
[Category("Data")]
[DefaultValue("")]
[Localizable(true)]
public byte[] BinMoves
{
set
{
for (var i = 0; i < 60; i++)
{
var v = value[i];
Moves +=((Char)('a'+(v%10)-1)).ToString() + (int)v / 10;
}
}
get
{
return new byte[60];
}
}
[Bindable(true)]是非常重要的,这决定了控件属性是可绑定的。显然,Render中可以根据这些属性来控制生成的HTML代码,而当数据绑定到这些属性时,它们的值可以根据数据库中的对应值变化。byte[]类型可以绑定到数据库的bin字段,这里的BinMoves就是用来绑定上面数据库中的steps的。
实际上,这时候我们的控件用起来已经和原生控件没什么分别了,我们可以对控件属性进行数据绑定:
最后附上一个简单的示例项目: