这是我学控件开发以来第一次写了一个自己的控件,不过看起来傻傻的,功能很少,只是能提供一个可编辑的下拉列表服务器控件,虽然功能简单,但总算是自己实践的,留下学习笔记:
在基类的选择上我用的是CompositeControl,应为我用的是.Net的服务器控件(dropdownList,TextBox)以搭积木的方式结合起来的(这样就不用自己写绑定了)
属性:只有四个重要的属性,Text(编辑框的值),DataSource(用于绑定的数据源),DataTextField(绑定源对应的文本域),DataValueField(绑定源对应的值域)
其中Text要用ViewState用来保证数据的持久性
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? String.Empty : s);
}
set
{
ViewState["Text"] = value;
}
}
DataSource则保存在了一个私有成员(object类)里
private object dataSource;
public object DataSource
{
get
{
return this.dataSource;
}
set
{
if (value == null)
{
throw new ArgumentException("unvalid data source.", this.ID);
}
this.dataSource = value;
}
}
DataTextField与DataValueField则同text属性一样保存在视图里面这里就不放代码了
行为:这个控件最重要的部分就是绑定数据了,模仿了dropdownlist的绑定方式
protected override void OnDataBinding(EventArgs e)事件,是在UI页面里调用了TestList1.DataBind();后要触发的事件,我们就在这里面将dropdownlist用传入的DataSource绑定好
1 protected override void OnDataBinding(EventArgs e)
2 {
3 base.OnDataBinding(e);
4 _select.DataSource = DataSource;
5 _select.DataTextField = DataTextField;
6 _select.DataValueField = DataValueField;
7 _select.DataBind();
8 }
之后貌似就只剩呈现了(其实远不如此),在CSS和JS上我是参考了网上别人将一个select 和 Input结合一起的模板模仿的(代码呈上)
1 <input type="text" name="re_name" style="width: 100px; height: 22px; font-size: 10pt;" />
2 <span style="width: 18px; border: 0px solid red;">
3 <select name="r00" style="margin-left: -100px; width: 118px; background-color: #FFEEEE;"
4 onchange="re_name.value=this.value;" onclick="this.selectedIndex=-1;">
5 <option value="111">111</option>
6 <option value="222">222</option>
7 <option value="333">333</option>
8 </select></span>
模仿着就把Render写出来了
1protected override void CreateChildControls()//加入子控件
2 {
3 Controls.Clear();
4
5 this.Controls.Add(_select);
6 this.Controls.Add(_input);
7
8 ChildControlsCreated = true;
9 }
10
11 protected override void RenderContents(HtmlTextWriter output)
12 {
13 EnsureChildControls();
14 int iWidth = Convert.ToInt32(base.Width.Value);
15 if (iWidth == 0)
16 {
17 iWidth = 102;
18 _input.Width = Unit.Parse("102px");
19 }
20
21 int sWidth = iWidth + 16;
22 int spanWidth = sWidth - 18;
23
24 int Height = Convert.ToInt32(base.Height.Value);
25
26 if (Page != null)
27 {
28 Page.VerifyRenderingInServerForm(this);
29 }
30 output.Write(@"<div style='POSITION:relative'>");
31 output.Write(@"<span style='MARGIN-LEFT:" + spanWidth.ToString() + "px;OVERFLOW:hidden;WIDTH:18px'>");
32
33 _select.Width = Unit.Parse(sWidth.ToString() + "px");
34 _select.Height = Unit.Parse(Height.ToString() + "px");
35 _select.Style.Add("MARGIN-LEFT", "-" + spanWidth.ToString() + "px");
36 _select.ID = base.ID + "_Select";
37 _select.Attributes.Add("onchange", "this.parentNode.nextSibling.value=this.value");
38 _select.Attributes.Add("onfocus", "" + this.getFocusScript() + "");
39 _select.RenderControl(output);
40 output.Write("</span>");
41
42 _input.Style.Clear();
43 _input.Width = Unit.Parse(iWidth.ToString() + "px");
44 _input.Height = Unit.Parse(Height.ToString() + "px");
45 _input.Style.Add("left", "0px");
46 _input.Style.Add("POSITION", "absolute");
47 _input.ID = this.UniqueID;
48 _input.Text = Text;
49 _input.RenderControl(output);
50
51 output.Write("</div>");
52 }
53
54 private string getFocusScript()//对应的脚本
55 {
56 string strScript = " ";
57 strScript += "var isExist = -2; ";
58 strScript += "var obj = event.srcElement; ";
59 strScript += "var str = this.parentNode.nextSibling.value; ";
60 strScript += "var ary = obj.options; ";
61 strScript += "for(var i=0;i<ary.length;i++){ ";
62 strScript += " if(str == ary[i].text){ ";
63 strScript += " isExist = i; ";
64 strScript += " break; ";
65 strScript += " } ";
66 strScript += "} ";
67 strScript += "if(isExist != -2){ ";
68 strScript += " obj.selectedIndex = isExist; ";
69 strScript += "} ";
70 strScript += "else{ ";
71 strScript += " obj.selectedIndex = -1; ";
72 strScript += "} ";
73 return strScript;
74 }
问题可不是到此就结束了,如果就这样我怎么也无法获取我输入的文本,改变的,选择的文本的值,为什么?因为每次当我点击button后会首先触发整个页面的刷新,我改变的值就会随之被重新设置(也就同我们写页面代码时忘记写!ispostback()而丢失交互信息一样),所以我们需要在页面刷新之前来保存更新的值。
这就用到了IPostBackDataHandler里面的LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection) 以下引用两位高人的话
当页面处理一个Http Post请求的时候,它会把form传回来数据进行解码,存入一个NameValueCollection的对象里面,我们可以通过 Request.Form来观察,这个存储结构比较类似于Hashtable,传入form field的name得到它的值。有了收集回来的post数据,就可以进行处理了。
实现这个接口最需要注意的一点,UniqueID就是必须把其中一个事件回发元素的name属性值设为这个控件的UniqueID,这样做很容易理解,就象上面那个LinkButton传递的参数一样,这个发回服务器端的UniqueID值使页框架能够确认这些数据是哪个控件发回来的。这个方法是标准的form回发,就象我们用asp时,一个表单中有一大堆的文本框、下拉框或者单选复选框,点击Submit按钮就将所有的这些表单元素的name属性值和它们的数据或状态发回服务器端,在asp.net中,这些回发的数据就是一个NameValueCollection对象,即一系列键、值对的集合。前面已经说过了,这其中必须有一个键名是这个引发回发的控件的UniqueID,否则页框架就分不清是哪个控件引发了回发事件以及回发的数据应该交给哪个控件去处理,页框架在接收到的回发数据中如果发现某个键名是页面上某个控件的UniqueID,就知道这些数据是这个控件回发的,然后检查该控件是否需要自己处理回发的数据(即是否实现IPostBackDataHandler接口),如果需要自己处理,页框架就会把该控件的标识和回发的数据作为参数传递给IPostBackDataHandler接口的LoadPostData()方法去处理.
这时候,如果在 OnPreRender 中加入这句 this.Page.RegisterRequiresPostBack(this); 还是可以触发IPostBackDataHandler.LoadPostData方法的.
但是里面的方法不能这么写了.
string text1 = PostBackDataCollection[ControlDataKey];
不会有值的,因为 这时候ControlDataKey就是 TextBoxInput1,为空的.而我们可以这样来写
string text1 = PostBackDataCollection[this.pDataBox.UniqueID] ;
这样可以得到 postback 后我们所需要的控件值.(即那个input的name值)
接下来的就是补全代码,补写OnPreRender this.Page.RegisterRequiresPostBack(this);
{
base .OnPreRender(e);
this .Page.RegisterRequiresPostBack( this );
}
和IPostBackDataHandler.LoadPostData方法
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
string newtext = postCollection[this.UniqueID + "$" + this.UniqueID];
this.Text = newtext;
return false;
}
最后还要注意一点的是获取服务器控件所生成的html代码中所对应的name和id的值 这里再引用另一个牛人的文章:
ID,
这个肯定黑熟悉了,用于server端编程引用控件,没有对应的client值,即不呈现到html中
UniqueID,
asp.net引擎按控件树层次生成的分层形式限定的标志符,连接符默认为 $ (美元符号)【注:MSDN说默认为 : (冒号),实际是 $ ,可能文档有误吧】,此连接符在asp.net 2.0 中由属性 IdSeparator 指定,在client中呈现为html元素的name属性
此属性主要用来提交(PostBack)客户端数据,如Request.Form[someControl.UnqiueID]
ClientID,
由父控件的UnqiueID连接本身ID而成,但是连接符不一样,默认为 _ (下划线),此连接符在asp.net 2.0 中由属性 ClientIDSeparator 指定,在client中呈现为html元素的id属性
我上面loadpostData中在获得那个textbox对应的input的name值时用的是this.UniqueID + "$" + this.UniqueID,这是迫于无赖,因为我始终只能得到name一个形如TextList1$c01s什么的东东,而在另一个地方确实完好的TextList1¥TextList1,哎,不管了这样也可以就好了,那个位牛人能帮帮小弟这个忙的话我不甚感激;
终于大功告成了,(说这句话我都惭愧)其实远远未达到实用要求,比如外观的控制,不过且当做勉励自己的方式,勉励自己坚持下去~~~~O(∩_∩)O~
附上完整的源代码/Files/SoYoung/MyFirstControl.rar