2004-10-16+ 自定义控件(内部内容+状态管理

<cc1:Txt id="Txt1" runat="server">hello world !</cc1:Txt>
这个自定义控件和label差不多,都是加一个span后输出,标记里面的内容是怎么被读取的呢?
如果你打开一个.aspx页面的跟踪,就会在控件树里发现一些叫LiteralControl的控件,这些控件其实就是那些没有runat=server的html标记,比如<table><tr>什么的,在处理的时候.net会把它们当成LiteralControl。很明显,我们在控件标记内的文本也是一个LiteralControl,这就相当于在控件内加了一个子控件。
sdk文档关于LiteralControl的介绍:
ASP.NET 将所有不需要服务器端处理的 HTML 元素和可读文本编译为该类的实例。例如,开始标记中不包含 runat="server" 属性/值对的 HTML 元素被编译为 LiteralControl 对象。
文本控件的行为与文本容纳器一样,这意味着可以从文本控件提取文本,并通过父服务器控件的 Controls 属性从父服务器控件的 ControlCollection 中移除文本控件。因此,当开发从 LiteralControl 类导出的自定义控件时,确保由控件自己执行任何所需的预处理步骤,而不是使用对 LiteralControl.Render 方法调用的调用来实现它们。一般都会这样做以提高 Web 应用程序的响应时间。
可以以编程方式分别使用 ControlCollection.Add 或 ControlCollection.Remove 方法,从页或服务器控件添加或移除文本控件。

因此我们要实现这种效果,就是先检查控件有没有子控件,如果有,那就应该是它的内部内容生成的LiteralControl,可以提取它的值并做进一步处理。注意这是对没有人为加子控件的简单自定义控件来说的,Controls[0]应该就是那个LiteralControl。至于复合控件是不是这样,现在还不得而知(是我没看到那里而已,实际上,按照书上写的,Controls[0]总是那个内部文本的LiteralControl。)
下面是实现的代码,这个是模仿label的控件,当然,和复杂的label相比,这个的定义太太简单了,感兴趣的可以用反射器看看label的定义 :)
public class Txt :Control
{
public Txt()
{
//一开始就对viewstate赋值
if(ViewState["text"]==null)
ViewState["text"]=string.Empty;
}

//使用viewstate来实现属性
public string Text
{
get { return (string)ViewState["text"]; }

set { ViewState["text"]=value; }
}

#region render
protected override void Render(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Name,this.UniqueID);
writer.RenderBeginTag(HtmlTextWriterTag.Span);
//判断是否含有子控件
if(this.HasControls())
{
//获得LiteralControl
LiteralControl lc=(LiteralControl)this.Controls[0];
//这里得到内部内容的值
writer.Write(lc.Text);
}
else
//如果没有内部内容,那就是直接用属性指定了
writer.Write(this.Text);
writer.RenderEndTag();
}
#endregion

}

上面这个控件的状态保持通过viewstate来完成,以前我们定义属性,都是用一个private变量来完成,但是那样做就没有办法保存控件的状态,现在用viewstate,可以很容易的完成这个。
状态的保持还可以通过实现IPostBackDataHandler接口实现,在那里的LoadPostData方法就传递了一个System.Collections.Specialized.NameValueCollection,可以用来获得控件的状态。
但并不是说所有控件只要实现了IPostBackDataHandler,就可以使用它来控制自身的值,只有内容是做为post请求的一部分发送的控件才可以(比如文本框),像上面的这个<span>,即使实现了IPostBackDataHandler,也完全没有作用,不仅需要的值不可能得到传递,用来实现IPostBackDataHandler的那两个方法也根本不会被调用!
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
this.msg=postCollection[postDataKey];
return true;
}

对于该方法,可以直接使用 postDataKey做为索引从 postCollection得到值。关于更详细的内容,可以去看上一篇笔记。
因此,对于内容不是post请求一部分的控件,可以使用viewstate来保持状态,而剩下的控件,内是ost请求一部分的控件,可以用实现IPostBackDataHandler+viewstate的混合方法来保持状态。

你可能感兴趣的:(自定义控件)