今天突然想把某一功能封装成服务器控件,但这一功能又需要连接数据库,如果直接在控件里连接数据库,那么跟在UI层直接操作数据库是一样的,也就破坏了三层架构。
突然想到了在自定义控件里调用页面方法,但在控件里只能调用基类 System.Web.UI.Page 已声明的几个方法,比如:DataBind()。一个简单方法就是在自定义控件里调用基类已声明的方法,然后在具体页面重写此方法,达到功能的实现。这里简单介绍一下:
在自定义控件里可以这样调用
this
.Page.DataBind();
在具体页面重写此方法:
public
override
void
DataBind()
{
base
.DataBind();
//
Do something
}
但很明显我今天要介绍的不是此方法。以下进入正题。
想用反射调用页面方法,如果有规范的方法名和参数列表,那就简单多了。这里自然想到用一接口来规范化。
一、创建新网站(ServerControlTest)
二、添加自定义服务器控件项目
三、定义并在页面实现接口
为了方便,直接在 Default.aspx.cs 文件中声明接口。注意:这里的接口是不包含在任何命名空间中的。
此时Default.aspx.cs 文件中的代码如下:
using
System;
using
System.Web;
using
System.Web.UI;
using
System.Web.UI.WebControls;
public
partial
class
_Default : System.Web.UI.Page , ITestInterface
{
protected
void
Page_Load(
object
sender, EventArgs e)
{
}
//
接口实现
#region
TestInterface 成员
public
string
TestMethod(
string
str)
{
return
"
已调用接口:TestInterface ——
"
+
str;
}
#endregion
}
//
接口定义
public
interface
ITestInterface
{
string
TestMethod(
string
str);
}
四、自定义控件的实现
我们将重载控件的 OnInit 事件,在此事件中调用页面方法 TestMethod。如果此页面未实现 ITestInterface 接口,我们将抛出异常。
控件的默认代码不变,添加以下代码:
protected
override
void
OnInit(EventArgs e)
{
base
.OnInit(e);
Type myType
=
this
.Page.GetType();
System.Reflection.TypeFilter myFilter
=
new
System.Reflection.TypeFilter(MyInterfaceFilter);
//
返回页面名称为 ITestInterface 的接口
Type[] myInterfaces
=
myType.FindInterfaces(myFilter,
"
ITestInterface
"
);
//
已实现接口
if
(myInterfaces.Length
>
0
)
{
//
我们将调用方法得到的结果赋于控件的 Text 属性
//
第一个参数是调用此TestMethod的对象,在这里自然是页面实例
//
第二个参数是方法TestMethod的参数列表,类型必须一致,无参数时可传null
this
.Text
=
myInterfaces[
0
].GetMethod(
"
TestMethod
"
).Invoke(
this
.Page,
new
object
[] {
"
输入参数
"
}).ToString();
}
//
未实现接口
else
{
throw
new
Exception(
"
The page doesn't implement the interface 'ITestInterface' !
"
);
}
}
public
bool
MyInterfaceFilter(Type typeObj, Object criteriaObj)
{
if
(typeObj.ToString()
==
criteriaObj.ToString())
return
true
;
else
return
false
;
}
五、为项目 ServerControlTest 添加引用

六、在 Default.aspx 中引用控件
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
CodeFile
=
"
Default.aspx.cs
"
Inherits
=
"
_Default
"
%>
<%
@ Register Assembly
=
"
ServerControl
"
Namespace
=
"
ServerControl
"
TagPrefix
=
"
cc1
"
%>
<!
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
></
title
>
</
head
>
<
body
>
<
form
id
="form1"
runat
="server"
>
<
div
>
<
cc1:ServerControl1
ID
="ServerControl1"
runat
="server"
/>
</
div
>
</
form
>
</
body
>
</
html
>
七、运行应用程序
页面将输出: 已调用接口:TestInterface —— 输入参数
至此已介绍完毕。不过其中有些注意的地方提一下。
在查找接口时 Type[] myInterfaces = myType.FindInterfaces(myFilter, "ITestInterface"); 接口名必须是完全限定名,如果接口是存放在某一命名空间中就必须注意了。如果你觉得这样太过于局限的话,那么你可以为控件增多一个属性,用来输入接口的完全限定名。
大家如果有什么更好的方法或建议,不妨提出来,大家一起学习学习。