Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]

时间:2022-06-03 17:10:48

         继上篇文章之后(链接),大家给了一些反馈和意见,有些我已动手进行了部分修改,将会在2.0版本中提供给大家。希望大家能
继续支持我们这个开源项目。

         好了,开始今天的话题,今天就说一下 Tab 控件。

        先贴一张运行效果图让大家看一下:
    
        Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]

    Discuz!NT控件剖析 之 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                  Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]
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( " 创建新的属性页Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码] " 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 这个窗体运行时的效果如下:

Discuz!NT控件剖析 之 Tab 属性页 [原创: 附源码]
   
            里面的代码很好理解,这里就不多说什么了。
   
            其余的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