Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]
继上篇文章之后(链接),大家给了一些反馈和意见,有些我已动手进行了部分修改,将会在2.0版本中提供给大家。希望大家能
继续支持我们这个开源项目。
好了,开始今天的话题,今天就说一下 Tab 控件。
先贴一张运行效果图让大家看一下:
开发动机:在去年开发后台功能时,最早使用的是ComponentArt控件库。相信园子里有不少人都用过这个商业控件库。在beta1版
正式发布后,才有时间将这个库中的控件一个一个的替除出来,其中就有tabs 控件。因为必定是商业控件,所以还是自己设计开发的
用着踏实。
今天这个下载包中的控件代码可是全新,这些代码如果不出意外的话,将会随同2.0版本一起发布出去。同时为了使用方便,我将
一些样式部分的代码单拿出来(因为2.0版本中样式表采用继承的方式进行设计)。现在就按下载包中的文件逐一给大家做一下说明:
在Discuz.Controls项目中的admin/tab/目录下有下面一些文件
TabControl.cs : tab控件的主体类,主要负责前端UI代码的生成,事件订制,子控件生成等
TabControlDesigner.cs :顾名思义,这里对TabControl控件进行设计时支持的类
TabEditorForm.cs : 对tab控件中的属性页进行添加,修改,删除进行可视化支持的窗体类
TabEditor.cs : 对在TabEditorForm窗体中操作的数据保存到设计时页面进行支持
TabPage.cs : 属性页控件类,作为TabControl的子控件进行显示其中的内容并进行相关属性绑定
TabPageCollection.cs : 将TabPage类实例以数据集合形式提供给TabControl的ITEM属性
现在大家就应该对整个控件有一个大概了解了吧!
那么现在就对这几个文件中的关键代码作一下概述:
TabControl.cs 中的服务器端事件处理,定义及其postback过程处理如下
2
3 public event EventHandler TabSelectedIndexChanged
4 {
5 add
6 {
7 base.Events.AddHandler(TabControl.TabSelectedIndexChangedEvent, value);
8 }
9 remove
10 {
11 base.Events.RemoveHandler(TabControl.TabSelectedIndexChangedEvent, value);
12 }
13 }
14
15 protected void OnTabSelectedIndexChanged(EventArgs e)
16 {
17 if (base.Events != null)
18 {
19 EventHandler handler1 = (EventHandler)base.Events[TabSelectedIndexChangedEvent];
20 if (handler1 != null)
21 {
22 handler1(this, e);
23 }
24 }
25 }
26
27 void IPostBackDataHandler.RaisePostDataChangedEvent()
28 {
29 this.OnTabSelectedIndexChanged(EventArgs.Empty);
30 }
31
32
其余主要属性参见注释代码:
2 public int SelectedIndex
3 {
4 get
5 {
6 if (this.Items.Count <= 0)
7 {
8 return (this._SelectedIndex = -1);
9 }
10 if (this._SelectedIndex == -1)
11 {
12 for (int i = 0; i < this.Items.Count; i++)
13 {
14
15
16 return this._SelectedIndex;
17 }
18 set
19 {
20 if ((value < -1) || (value >= this.Items.Count))
21 {
22 throw new ArgumentOutOfRangeException("选项页必须小于" + this.Items.Count.ToString());
23 }
24 this._SelectedIndex = value;
25 }
26 }
27
28
29 [Description("顶部属性页标题距左边偏移量"), DefaultValue(0)]
30 public int LeftOffSetX
31 {
32 get
33 {
34 object obj = ViewState["LeftOffSetX"];
35 return obj == null ? 0 : Convert.ToInt32(obj.ToString());
36 }
37 set
38 {
39 ViewState["LeftOffSetX"] = value;
40 }
41 }
42
这个控件的render函数实现逻辑很清楚,这里就不多说了,大家可以去看一下相关代码就行了。
TabControlDesigner.cs 文件的源码如下(请看一下注释即可):
2 {
3 protected override string GetEmptyDesignTimeHtml()
4 {
5 return CreatePlaceHolderDesignTimeHtml("右击选择创建新的属性页");
6 }
7
8 private DesignerVerbCollection _verbs;
9
10 //定义处理的动作
11 public override DesignerVerbCollection Verbs
12 {
13 get
14 {
15 if (_verbs == null)
16 {
17 _verbs = new DesignerVerbCollection(new DesignerVerb[] { new DesignerVerb("创建新的属性页", new EventHandler(this.OnBuildTabStrip)) });
18 }
19
20 return _verbs;
21 }
22 }
23
24
25 //处理动作要运行的操作,这里是显示TabEditorForm窗体
26 private void OnBuildTabStrip(object sender, EventArgs e)
27 {
28 TabEditor oEditor = new TabEditor();
29 oEditor.EditComponent(this.Component);
30 }
31
32 //得到设计时Html代码
33 public override string GetDesignTimeHtml()
34 {
35
36 try
37 {
38 TabControl oTabStrip = ((TabControl)Component);
39
40 if (oTabStrip.Items == null || oTabStrip.Items.Count == 0)
41 {
42 return GetEmptyDesignTimeHtml();
43 }
44
45 System.Text.StringBuilder oSB = new System.Text.StringBuilder();
46 StringWriter oStringWriter = new StringWriter(oSB);
47 HtmlTextWriter oWriter = new HtmlTextWriter(oStringWriter);
48
49 oTabStrip.RenderDownLevelContent(oWriter);
50 oWriter.Flush();
51 oStringWriter.Flush();
52
53 return oSB.ToString();
54 }
55 catch (Exception ex)
56 {
57 return CreatePlaceHolderDesignTimeHtml("生成设计时代码错误:\n\n" + ex.ToString());
58 }
59 }
60 }
61
62
63
TabEditor.cs文件的内容主要是用于对操作数据绑定到相关的web设计页面。
相关主要代码如下:
2 {
3 public override bool EditComponent(ITypeDescriptorContext context, object component, IWin32Window owner)
4 {
5 TabControl oControl = (TabControl)component;
6 IServiceProvider site = oControl.Site;
7 IComponentChangeService changeService = null;
8
9 DesignerTransaction transaction = null;
10 bool changed = false;
11
12 try
13 {
14 if (site != null)
15 {
16 IDesignerHost designerHost = (IDesignerHost)site.GetService(typeof(IDesignerHost));
17 transaction = designerHost.CreateTransaction("BuildTabStrip");
18
19 changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
20 if (changeService != null)
21 {
22 try
23 {
24 changeService.OnComponentChanging(component, null);
25 }
26 catch (CheckoutException ex)
27 {
28 if (ex == CheckoutException.Canceled)
29 return false;
30 throw ex;
31 }
32 }
33 }
34
35 try
36 {
37 TabEditorForm oEditorForm = new TabEditorForm(oControl);
38 if (oEditorForm.ShowDialog(owner) == DialogResult.OK)
39 {
40 changed = true;
41 }
42 }
43 finally
44 {
45 if (changed && changeService != null)
46 {
47 changeService.OnComponentChanged(oControl, null, null, null);
48 }
49 }
50 }
51 finally
52 {
53 if (transaction != null)
54 {
55 if (changed)
56 {
57 transaction.Commit();
58 }
59 else
60 {
61 transaction.Cancel();
62 }
63 }
64 }
65
66 return changed;
67 }
68 }
69
70
这块功能的注释因为开发时间问题,以后会加进去,但这些代码因为存在很大的通用性,所以大家可以用在自己的控件设计中:)
TabEditorForm.cs 这个窗体运行时的效果如下:
里面的代码很好理解,这里就不多说什么了。
其余的TabPage.cs,和TabPageCollection.cs文件都是相对简单的设计,其中主要看一下TabPage类中的Render函数即可
下面再将相关的JS贴上,以便大家进行对照:
2 {
3 if(e.className == "CurrentTabSelect")
4 {
5 return ;
6 }
7
8 if(e.className != "OnTabSelect")
9 {
10 e.className = "OnTabSelect";
11 }
12 }
13
14 function tabpage_mouseout(e)
15 {
16 if(e.className == "CurrentTabSelect")
17 {
18 return ;
19 }
20 if(e.className != "TabSelect")
21 {
22 e.className = "TabSelect";
23 }
24 }
25
26 function tabpage_selectonserver(e,tabpageid)
27 {
28 e.parentNode.parentNode.childNodes[0].value = tabpageid;
29 }
30
31 function tabpage_selectonclient(e,tabpageid)
32 {
33 tabdiv = e.parentNode;
34
35 var tabpagediv = getElementsByClassName('tab-page','div',document);
36 var tabareas = getElementsByClassName('tabarea','div',document);
37
38 for(i=0;i<tabdiv.childNodes.length;i++)
39 {
40 tabdiv.childNodes[i].className = "TabSelect";
41 tabdiv.childNodes[i].childNodes[0].className = "";
42 }
43
44 for(i=0;i<tabpagediv.length;i++)
45 {
46 if(tabpagediv[i].id.indexOf(e.id.split(':')[0])>=0)
47 {
48 tabpagediv[i].style.display = "none";
49 }
50 }
51
52 //对当前结点的子结点(所有)设置属性
53 for(i=0;i<tabareas.length;i++)
54 {
55 if(tabareas[i].id.indexOf(e.id.replace('_li',''))>=0)
56 {
57 tabareas[i].style.display = "block";
58 tabareas[i].childNodes[0].style.display = 'block';
59 }
60 }
61
62 //对当前结点的父节点(所有)设置属性
63 var parentnode = document.getElementById(tabpageid);
64 while(true)
65 {
66 parentnode = parentnode.parentNode;
67
68 if(parentnode == null)
69 {
70 break;
71 }
72
73 if((parentnode.className =="tab-page")||(parentnode.className =="tabarea"))
74 {
75 parentnode.style.display = 'block';
76 }
77 }
78
79 document.getElementById(tabpageid).style.display = 'block';
80 document.getElementById(tabpageid+"_li").className = 'CurrentTabSelect';
81 document.getElementById(tabpageid+"_li").childNodes[0].className="current";
82 }
83
84
85 function getElementsByClassName(strClassName, strTagName, oElm)
86 {
87 var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);
88 var arrReturnElements = new Array();
89 strClassName = strClassName.replace(/\-/g, "\\-");
90 var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
91 var oElement;
92 for(var i=0; i<arrElements.length; i++){
93 oElement = arrElements[i];
94 if(oRegExp.test(oElement.className)){
95 arrReturnElements.push(oElement);
96 }
97 }
98 return (arrReturnElements)
99 }
100
101
好了,主要是东西就先交待到这里了。如果大家有什么问题或建议,欢迎与我交流,我的邮箱是:
daizhj@gmail.com, daizhj617595@126.com
源码:点击下载
本系列上一篇文件链接:
Discuz!NT控件剖析 之 TextBox [原创: 附源码] :http://www.cnblogs.com/daizhj/archive/2007/08/09/849041.html