大家都知道在写一个自定义控件的时候,如果需要使用到回发数据处理方法时,需要继承IPostBackDataHandler接口,该接口有两个方法,一个是LoadPostData(),用来接收回发的数据,一个是RaisePostDataChangedEvent(),它是用来通知回发数据的控件该控件的状态(如输入到TextBox中的值)已更改,如果控件状态更改后你需要执行某些操作,可以在该方法中执行。
是否执行RaisePostDataChangedEvent()方法是由
LoadPostData()方法的返回值来决定的,如果
LoadPostData()返回true就执行RaisePostDataChangedEvent(),否则不引发。
实现这个接口最需要注意的一点,UniqueID就是必须把其中一个事件回发元素的name属性值设为这个控件的UniqueID,这样做很容易理解,就象上面那个LinkButton传递的参数一样,这个发回服务器端的UniqueID值使页框架能够确认这些数据是哪个控件发回来的。这个方法是标准的form回发,就象我们用asp时,一个表单中有一大堆的文本框、下拉框或者单选复选框,点击Submit按钮就将所有的这些表单元素的name属性值和它们的数据或状态发回服务器端,在asp.net中,这些回发的数据就是一个NameValueCollection对象,即一系列键、值对的集合。前面已经说过了,这其中必须有一个键名是这个引发回发的控件的UniqueID,否则页框架就分不清是哪个控件引发了回发事件以及回发的数据应该交给哪个控件去处理,页框架在接收到的回发数据中如果发现某个键名是页面上某个控件的UniqueID,就知道这些数据是这个控件回发的,然后检查该控件是否需要自己处理回发的数据(即是否实现IPostBackDataHandler接口),如果需要自己处理,页框架就会把该控件的标识和回发的数据作为参数传递给IPostBackDataHandler接口的LoadPostData()方法去处理.
下面来看一个例子
[DefaultEvent(
"
TextChanged
"
), ToolboxData(
"
<{0}:TextBoxInput runat=server></{0}:TextBoxInput>
"
)]
public
class
TextBoxInput : WebControl, IPostBackDataHandler, INamingContainer
{
// Fields
private HtmlInputText pDataBox;
private static readonly object TextChangedEvent;
static TextBoxInput()
{
TextChangedEvent = new object();
}
public TextBoxInput()
{
this.pDataBox = new HtmlInputText();
}
void IPostBackDataHandler.RaisePostDataChangedEvent()
{
this.OnTextChanged(EventArgs.Empty);
}
bool IPostBackDataHandler.LoadPostData(string ControlDataKey, NameValueCollection PostBackDataCollection)
{
string text1 = PostBackDataCollection[ControlDataKey];
if (this.Value != text1)
{
this.Value = text1;
return true;
}
return false;
}
protected override void CreateChildControls()
{
this.pDataBox.ID = this.UniqueID;
base.ChildControlsCreated = true;
}
protected void OnTextChanged(EventArgs e)
{
if (base.Events != null)
{
EventHandler handler1 = (EventHandler)base.Events[TextChangedEvent];
if (handler1 != null)
{
handler1(this, e);
}
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter pOutPut)
{
if (this.Visible)
{
System.Web.UI.WebControls.Table tbDate = new Table();
System.Web.UI.WebControls.TableRow tbDateRow = new TableRow();
System.Web.UI.WebControls.TableCell tbDateCell1 = new TableCell();
tbDateCell1.Controls.Add(this.pDataBox);
tbDateRow.Cells.Add(tbDateCell1);
tbDate.Rows.Add(tbDateRow);
this.Controls.Add(tbDate);
base.Render(pOutPut);
}
}
public string Value
{
get
{
if (this.ViewState["Value"] != null)
{
return (string)this.ViewState["Value"];
}
return string.Empty;
}
set
{
this.ViewState["Value"] = value;
}
}
public event EventHandler TextChanged
{
add
{
base.Events.AddHandler(TextChangedEvent, value);
}
remove
{
base.Events.RemoveHandler(TextChangedEvent, value);
}
}
}
我们重点要看的是CreateChildControls()
和Render () 这两个函数
1先看Render 的最后两句
this.Controls.Add(tbDate);
base.Render(pOutPut);
这两句render 到页面的
<span id="TextBoxInput1">
<table border="0">
<tr>
<td><input name="TextBoxInput1:TextBoxInput1" type="text" id="TextBoxInput1_TextBoxInput1" /></td>
</tr>
</table>
</span>
我们发现使用 base.Render(pOutPut); 会在外面再加一层span ,并且它的id 为 UniqueID,
而 pDataBox 的id 变成了 TextBoxInput1:TextBoxInput1. 这样就会导致
不会触发 IPostBackDataHandler.LoadPostData 方法.
原因上面已经说明.
这时候,如果在 OnPreRender 中加入这句
this.Page.RegisterRequiresPostBack(this);
还是可以触发IPostBackDataHandler.LoadPostData方法的.
但是里面的方法不能这么写了.
string text1 = PostBackDataCollection[ControlDataKey];
不会有值的,因为 这时候ControlDataKey就是 TextBoxInput1,为空的.
而我们可以这样来写
string text1 = PostBackDataCollection[this.pDataBox.UniqueID] ;
这样可以得到 postback 后我们所需要的控件值.
2
还有一种方法就是
把
this.Controls.Add(tbDate);
base.Render(pOutPut);
换成
tbDate.RenderControl(pOutPut)
不去调用 base.Render(pOutPut); 这样也可以.
3
而引发LoadPostData的条件是在自定义控件呈现的html中必须有一个input标签的name属性为整个自定义控件的UniqueID。当自定义控件为复合控件,即里面包含标准的ASP.net子控件时,要有一个子控件的ID为该自定义复合控件的UniqueID,这时你需要继承System.Web.UI.Control,不要实现INamingContainer接口,否则子控件的ID会按名称空间来定义,即子控件的ClientID会自动加上父控件的ID。还有一种办法,就是在控件组中加入一个Hidden控件!
pOutPut.Write("<INPUT type=\"hidden\" name=" + this.UniqueID + " value=" + this.Value + " >");
总之要想调用 IPostBackDataHandler.LoadPostData方法,
必须具备
一个name 为 UniqueID 的控件
或者调用this.Page.RegisterRequiresPostBack(this);方法
如果在IPostBackDataHandler.LoadPostData方法 想获取回传的数据时,必须取正确的PostDataKey,有时候
PostBackDataCollection[PostDataKey]不一定有值.因为由于控件的 UniqueID 名字的混乱而导致的.
PostBackDataCollection [PostDataKey]这里面记录的值是 name 为 UniqueID 的控件的值.PostDataKey也就等于
UniqueID. 如果 PostBackDataCollection[PostDataKey] 为"" 的话那么 就需要调用this.Page.RegisterRequiresPostBack(this);方法 来触发LoadPostData了
如果使用
this.Controls.Add(control);
在CreateChildControls 添加子控件的时候.在子控件的命名中加上this.UniqueID.表示当前控件的子控件.
这时候如果给那些 input 赋值指望 触发LoadPostData,因为 它的Name 变成了"name:name",所以就达不到预期的效果了.