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过程处理如下
    
   

 1  private static readonly object TabSelectedIndexChangedEvent;
 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 

    
         其余主要属性参见注释代码:
    
    

 1 //当选定某一属性页索引值时
 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 文件的源码如下(请看一下注释即可):
    
    

 1 public class TabControlDesigner : ControlDesigner
 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设计页面。
     相关主要代码如下:
    

 1  internal class TabEditor : WindowsFormsComponentEditor
 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, nullnullnull);
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贴上,以便大家进行对照: 

 

  1 function tabpage_mouseover(e)
  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

 

 

posted @ 2007-08-22 09:24  代震军  阅读(7733)  评论(50编辑  收藏  举报