研究ANF的源码,让我获益良多。其中很多思想,都是非常值得学习的。其中换肤的方式,宝玉已经介绍过了,《Asp.Net Forums2.0深入分析》之 Asp.Net Forums是如何实现代码分离和换皮肤的。不过,当一个自定义控件中服务器端控件比较多的时候,InitializeSkin方法的实现代码就有点烦人了,比如看一下AdminSiteSettings的代码。实在是非常之烦人。模式都一样,如TextBox DisableSiteReason=skin.FindControl("DisableSiteReason") as TextBox之类。所以今天想利用Attribute来简化一下。
首先我们要添加一个Attribute类,暂且就叫做BindControlAttribute:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace AspNetForums.Controls
{
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,AllowMultiple=false)]
class BindControlAttribute:Attribute
{
string _ctrlID;
public BindControlAttribute(string ctrlID)
{
_ctrlID = ctrlID;
}
public string ControlID
{
get { return _ctrlID; }
}
}
}
这个类Attribute功能比较简单,就是让这个Attribute记录字段要绑定到的控件的ID。
第二步就是修改SkinnedForumWebControl了,主要是添加一个方法:
private void InitializeFields(Control skin)
{
FieldInfo[] fields = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (FieldInfo fi in fields)
{
if (fi.IsDefined(typeof(BindControlAttribute), false))
{
BindControlAttribute bind = fi.GetCustomAttributes(typeof(BindControlAttribute), false)[0] as BindControlAttribute;
object ctrl = skin.FindControl(bind.ControlID);
fi.SetValue(this, ctrl);
}
}
}
其实在fi.IsDefined(typeof(BindControlAttribute), false)这里我本想再加一个条件的,就是限制字段的类型是System.Web.UI.Control或者它的子类,可是试了几种方法,都没能成功。请知道的指点一下...
记得要引用System.Reflection名称空间,还要改一下CreateChildControls方法的实现:
protected override void CreateChildControls()
{
Control skin = null;
if (inlineSkin != null)
{
inlineSkin.InstantiateIn(this);
InitializeSkin(this);
}
else
{
// Load the skin
skin = LoadSkin();
//Initialize the fields
InitializeFields(skin);//就加这一行
// Initialize the skin
InitializeSkin(skin);
Controls.Add(skin);
}
}
到这里,实现的任务就完成了。下面就是应用了。应用是比较简单的,只需要在定义字段的时候,加上这个BindControlAttribute就行了。如:
uing System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using AspNetForums.Components;
using AspNetForums.Enumerations;
namespace AspNetForums.Controls
{
/// <summary>
/// 论坛组列表服务器控件
/// </summary>
public class ForumGroupView : SkinnedForumWebControl
{
#region 成员字段
private ForumContext forumContext = ForumContext.Current;
private string skinFilename = "View-ForumGroupView.ascx";
[BindControl("forumGroupRepeater")]//定义字段时加这么一行
private Repeater repeater;
#endregion
public ForumGroupView()
{
// Assign a default template name
if (SkinFilename == null)
SkinFilename = skinFilename;
}
#endregion
#region 控件初始化
// *********************************************************************
// Initializeskin
//
/// <summary>
/// Initializes the user control loaded in CreateChildControls. Initialization
/// consists of finding well known control names and wiring up any necessary events.
/// </summary>
///
// ********************************************************************/
protected override void InitializeSkin(Control skin)
{
//repeater = (Repeater) skin.FindControl("forumGroupRepeater");//这一行就没有用了。
DataBind();
}
感觉是不是好点呢?
第一次用反射,着实费了我不小的功夫(主要是看书不认真:-))。我要获取私有字段的时候,试了好多次才试出来,前面2个BindingFlags貌似都得加上。另外,我发现,Attribute构造函数是在调用GetCustomAttributes函数时才调用的。不知道是否有二班的情况呢?