导航

自定义服务器控件通过反射调用页面方法

Posted on 2009-04-06 17:27  LucasYang  阅读(976)  评论(0编辑  收藏  举报

  今天突然想把某一功能封装成服务器控件,但这一功能又需要连接数据库,如果直接在控件里连接数据库,那么跟在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"); 接口名必须是完全限定名,如果接口是存放在某一命名空间中就必须注意了。如果你觉得这样太过于局限的话,那么你可以为控件增多一个属性,用来输入接口的完全限定名。

大家如果有什么更好的方法或建议,不妨提出来,大家一起学习学习。