zoukankan      html  css  js  c++  java
  • Discuz!NT控件剖析 之 DataGrid(数据列表) [原创: 附源码]

        自从 9月未开始写关于"ICONIX方法"的系列文章以来,到今天已有两个多月了,当初因为兴趣点的转移才划一
    段落的Discuz!NT 系列文章,从今天开始又要开始续写了。这写这个系列以来,大家对我写的内容也是包贬不一,
    其实这也是众口难调所致,我会在接下来的几篇随笔中尽力顾及大家的感受和阅读口味。最后还是希望大家能支持我们

    的这个开源项目。

        好了,开始今天的话题!

        先请大家看一下这个控件运行时的效果图:

        效果图1: datagrid_1.JPG
     

        效果图2: datagrid_2.JPG

      

       
        需要说明的是写这个控件(继承自.net DataGrid 控件)的动机:
       
        其实在产品早期,为了提高开发速度。我们最先使用的是Component Art 控件库中的DataGrid控件,相信园子
    里肯定有人用过这个商业控件库,甚至研究过它。从我个人感受看,我只能是“NB”来形容它了,首先是它的使用习惯
    和方法名称非常接近(甚至完全相同)于我们所熟悉的Microsoft .NET DataGrid 控件,另外就是它的功能比微软
    的DataGrid强大得不是一点半点,是那种“很好很强大”的东东。我在这里要向那些致力于控件开发的朋友推荐这个
    控件库,相信大家会从它的源码中学到很多有益的东西。

        如果这里有些朋友还不知道它的话,建议大家看看 CS(这里可不是"反恐精英"呀),这个开源社区软件里的控件
    库就用的是Component Art。

        言归正传吧,因为Component Art是商业控件库,如果使用的话会有许多问题(如授权等)。所以在Discuz!NT
    1.0 正式版推出前, 我们的相应代码基本上都又再次回到了 Microsoft .NET DataGrid上了,同时考虑到所开发的
    代码要适用于.net1.0-.net2.0,所以没有使用.net2.0中的DataView控件。
       
        即然决定使用Microsoft .NET DataGrid,那么就要看看它到底适不适合我们这个项目了:) ,因为之前已完成
    了后台的编码工作,而剩下的就是在确保少修改代码,甚至减少代码量的基础上使用这个控件来摆脱Component Art。

        在减少代码量的方面,这里对“原始”的DataGrid进行了不少的封装, 如下:

        1.绑定数据部分,为了实现只给一条 SQL语句,就完成数据绑定的设计,我在继承自DataGrid的类中写了如下
    方法:

      

     1//添加表类型对象
     2        public void BindData(string sqlstring)
     3        {
     4            this.SqlText = sqlstring;
     5
     6            //DbHelper类我在之前的关于数据库链接一文中已介绍过,大家可以参考一下      
     7            DataTable dt = DbHelper.ExecuteDataset(CommandType.Text, sqlstring).Tables[0];
     8
     9            //用于标识记录数
    10            this.VirtualItemCount = dt.Rows.Count;
    11
    12            //下面两行代码就不用多说了吧:)
    13            this.DataSource = dt;
    14            this.DataBind();
    15        }

    16        
    17 public void BindData()
    18        {
    19            if ((this.SqlText != null&& (this.SqlText != ""))
    20            {
    21                BindData(this.SqlText);
    22            }

    23            
    24        }

    25
    26

        2.加载编辑列和删除列按钮的方法如下:

            

     1public void LoadEditColumn()
     2        {
     3            EditCommandColumn ecc = new EditCommandColumn();//更新按钮列 
     4            ecc.SortExpression = "desc";
     5            ecc.ButtonType = ButtonColumnType.LinkButton;//链接按钮 
     6            ecc.EditText = "编辑";
     7            ecc.UpdateText = "更新";
     8            ecc.CancelText = "取消";
     9            ecc.ItemStyle.Width = 70;
    10            this.Columns.AddAt(0, ecc);//增加按钮列 
    11        }

    12
    13        public void LoadDeleteColumn()
    14        {
    15            ButtonColumn bc = new ButtonColumn();
    16            bc.SortExpression = "desc";
    17            bc.CommandName = "Delete";
    18            bc.Text = "删除";
    19            bc.ItemStyle.Width = 70;
    20            this.Columns.AddAt(1, bc);//增加按钮列 
    21        }

    22
    23

        3.在点击编辑,取消和跳转指定分页上也进行了封装如下:
           

     1 public void EditByItemIndex(int itemindex)
     2        {
     3            this.EditItemIndex = itemindex;
     4            BindData();
     5        }

     6
     7        public void Cancel()
     8        {
     9            this.EditItemIndex = -1;
    10            BindData();
    11        }

    12
    13        public void LoadCurrentPageIndex(int value)
    14        {
    15            this.CurrentPageIndex = (value < 0? 0 : value;
    16            BindData();
    17        }

    18
    19

        4.点击表头的排序任务通过下面的属性来完成:

     

     1private string sort;
     2
     3        [Bindable(true), Category("Appearance"), DefaultValue("")]
     4        public string Sort
     5        {
     6            get
     7            {
     8                return sort;
     9            }

    10            set
    11            {
    12                sort = value;
    13                SortTable(sort, (DataTable)null);
    14            }

    15        }

    16
    17

     
            而SortTable函数的声明如下:

     

     1public void SortTable(string SortExpression, DataTable dt)
     2        {
     3            DataView dv = new DataView();
     4            if (dt != null && dt.Rows.Count > 0)
     5            {
     6                dv = new DataView(dt);
     7            }

     8            else
     9            {
    10                if (this.SqlText != null && this.SqlText != "")
    11                {
    12                    dv = new DataView(DbHelper.ExecuteDataset(CommandType.Text, this.SqlText).
    13   Tables[0]);
    14                }

    15                else
    16                {
    17                    return;
    18                }

    19            }

    20            dv.Sort = SortExpression.Replace("<img""~").Split('~')[0+ " " + 
    21   this.DataGridSortType;
    22            //dv.Sort= SortExpression+" "+this.DataGridSortType;
    23            this.DataSource = dv;
    24            this.DataBind();
    25        }

    26
    27
    28        public void SortTable(string SortExpression, string sqlstring)
    29        {
    30            DataView dv = new DataView();
    31            if (sqlstring != null && sqlstring != "")
    32            {
    33                dv = new DataView(DbHelper.ExecuteDataset(CommandType.Text, sqlstring).
    34   Tables[0]);
    35            }

    36            else
    37            {
    38                return;
    39            }

    40
    41            dv.Sort = SortExpression.Replace("<img""~").Split('~')[0+ " " + 
    42   this.DataGridSortType;
    43            this.DataSource = dv;
    44            this.DataBind();
    45        }

    46
    47

            而里面的DataGridSortType属性, 即标识当前是升序还是降序排列当前表字段:

           

     1 [Description("表头的名称。"), Category("Appearance"), DefaultValue("ASC")]
     2        public string DataGridSortType
     3        {
     4            get
     5            {
     6
     7                object obj = ViewState["DataGridSortType"];
     8                string ascordesc = obj == null ? "ASC" : (string)obj;
     9                if (ascordesc == "ASC")
    10                {
    11                    ViewState["DataGridSortType"= "DESC";
    12                    return "DESC";
    13                }

    14                else
    15                {
    16                    ViewState["DataGridSortType"= "ASC";
    17                    return "ASC";
    18                }

    19
    20            }

    21            set
    22            {
    23                ViewState["DataGridSortType"= value;
    24            }

    25        }

    26
    27

        5.当按表头某字段进行排序时,在字段名称后添加“向下”或“向上”箭头icon。这里为了实现和使用方便,在
    继承自datagrid的基础上绑定(详见构造函数)并实现了SortGrid方法(下面代码比较简单,就不做说明了):

     

     1protected void SortGrid(Object sender, DataGridSortCommandEventArgs e)
     2        {
     3
     4            SortTable(e.SortExpression, (DataTable)null);
     5
     6            foreach (System.Web.UI.WebControls.DataGridColumn dc in this.Columns)
     7            {
     8                if (dc.SortExpression == e.SortExpression)
     9                {
    10                    if (dc.HeaderText.IndexOf("<img src=">= 0)
    11                    {
    12                        if (this.DataGridSortType == "ASC")
    13                        {
    14                            dc.HeaderText = dc.HeaderText.Replace("<img src=" + this.ImagePath + "asc.
    15   gif height=13>""<img src=" + this.ImagePath + "desc.gif height=13>");
    16                        }

    17                        else
    18                        {
    19                            dc.HeaderText = dc.HeaderText.Replace("<img src=" + this.ImagePath + "desc.
    20   gif height=13>""<img src=" + this.ImagePath + "asc.gif height=13>");
    21                        }

    22                    }

    23                    else
    24                    {
    25                        if (this.DataGridSortType == "ASC")
    26                        {
    27                            dc.HeaderText = dc.HeaderText + "<img src=" + this.ImagePath + "desc.gif 
    28      height=13>";
    29                        }

    30                        else
    31                        {
    32                            dc.HeaderText = dc.HeaderText + "<img src=" + this.ImagePath + "asc.gif 
    33      height=13>";
    34                        }

    35                    }

    36                }

    37                else
    38                {
    39                    dc.HeaderText = dc.HeaderText.Replace("<img""~").Split('~')[0];
    40                }

    41            }

    42        }

    43            
    44
    45

        6.当鼠标在数据行间移动时的背景颜色变化的效果也设置在了控件中,相应的代码段如下:

       

     1  public void DataGrid_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
     2     {
     3     if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
     4            {
     5                e.Item.Attributes.Add("onmouseover""this.className='mouseoverstyle'");
     6                e.Item.Attributes.Add("onmouseout""this.className='mouseoutstyle'");
     7                e.Item.Style["cursor"= "hand";
     8            }

     9           
    10

        
        通过上面的一番"折腾",使用原来很好用的datagrid在使用上进一步“代码瘦身”,相应的CS代码也相应的变成了
    下面的样子(数据表通过运行下载包中的SQL脚本创建即可):

     

     1protected void Page_Load(object sender, EventArgs e)
     2 {
     3  if (!Page.IsPostBack)
     4  {
     5   BindData();
     6  }

     7 }

     8
     9 public void BindData()
    10 {
    11                //是否允许自定义分页(继承自.net datagrid)
    12  DataGrid1.AllowCustomPaging = false;
    13                //定义列表名称
    14  DataGrid1.TableHeaderName = "过滤词列表";
    15  DataGrid1.BindData("SELECT * FROM [dnt_smilies]");
    16 }

    17
    18 protected void Sort_Grid(Object sender, DataGridSortCommandEventArgs e)
    19 {
    20  DataGrid1.Sort = e.SortExpression.ToString();
    21 }

    22
    23  
    24 protected void DataGrid_PageIndexChanged(object source, DataGridPageChangedEventArgs e)
    25 {
    26  DataGrid1.LoadCurrentPageIndex(e.NewPageIndex);
    27 }

    28
    29 protected void DataGrid_Edit(Object sender, DataGridCommandEventArgs E)
    30 {
    31  DataGrid1.EditByItemIndex(E.Item.ItemIndex);
    32 }

    33
    34 protected void DataGrid_Cancel(Object sender, DataGridCommandEventArgs E)
    35 {
    36  DataGrid1.Cancel();
    37 }

    38
    39


        当然,如果您觉得还是以前的.net datagrid 使用方便,这个控件也是兼容的。

        上面的改进只是为了少写代码,是一种“偷懒”的行径。而下面的代码就是在microsoft datagrid基础上的订制
    改进了。


        1.因为分样的方式要与论坛前台的"分页样式"相类似,所以要在分页页码位置之后添加诸如: 当前页码,总页数,
    总记录数,跳转到指定页面文本框等,所以下面的代码被开发出来。

          

     1public void DataGrid_ItemCreated(Object sender, DataGridItemEventArgs e)
     2      {
     3            ListItemType elemType = e.Item.ItemType;
     4
     5            if (elemType == ListItemType.Pager)
     6            {
     7                TableCell cell1 = (TableCell)e.Item.Controls[0];
     8                cell1.HorizontalAlign = HorizontalAlign.Left;
     9                cell1.VerticalAlign = VerticalAlign.Bottom;
    10                cell1.CssClass = "datagridPager";
    11
    12                LiteralControl splittable = new LiteralControl("splittable");
    13                splittable.Text = "</td></tr></table><table class=\"datagridpage\"><tr><td height=\"2\">
    14     </td></tr><tr><td>";
    15                cell1.Controls.AddAt(0, splittable);
    16
    17                LiteralControl PageNumber = new LiteralControl("PageNumber");
    18                PageNumber.Text = " ";
    19                if (this.PageCount <= 1)
    20                {
    21                    try
    22                    {
    23                        cell1.Controls.RemoveAt(1); //当页数为1时, 则不显示页码
    24                    }

    25                    catch { ; }
    26                }

    27                else
    28                {
    29                    PageNumber.Text = "&nbsp;&nbsp;";
    30                }

    31                PageNumber.Text += "<font color=black>共 " + this.PageCount + " 页, 当前第 " + 
    32     (this.CurrentPageIndex + 1+ " 页";
    33
    34                if (this.VirtualItemCount > 0)
    35                {
    36                    PageNumber.Text += ", 共 " + this.VirtualItemCount + " 条记录";
    37                }

    38
    39
    40                PageNumber.Text += "    &nbsp;&nbsp;" + ((this.PageCount > 1? "跳转到:" : "");
    41                cell1.Controls.Add(PageNumber);
    42
    43
    44                //当大于1时显示跳转按钮
    45                if (this.PageCount > 1)
    46                {
    47                    //加载跳转文件框
    48                    GoToPagerInputText.ID = "GoToPagerInputText";
    49                    GoToPagerInputText.Attributes.Add("runat""server");
    50                    GoToPagerInputText.Attributes.Add("onkeydown""if(event.keyCode==13) 
    51    {
    52       var gotoPageID=this.name.replace('InputText','Button'); 
    53    return(document.getElementById(gotoPageID)).focus();}

    54     ");
    55
    56                    GoToPagerInputText.Size = 6;
    57                    GoToPagerInputText.Value = (this.CurrentPageIndex == 0? "1" :
    58       (this.CurrentPageIndex + 1).ToString();
    59                    cell1.Controls.Add(GoToPagerInputText);
    60
    61                    PageNumber = new LiteralControl("PageNumber");
    62                    PageNumber.Text = "页&nbsp;&nbsp;";
    63                    cell1.Controls.Add(PageNumber);
    64
    65                    //加载跳转按钮 
    66                    GoToPagerButton.ID = "GoToPagerButton";
    67                    GoToPagerButton.Text = " Go ";
    68                    cell1.Controls.Add(GoToPagerButton);
    69                }

    70
    71                e.Item.Controls.Add(cell1);
    72
    73

        2. 相信用过.net datagrid 的用户对下面代码的用法会比较熟悉:
                 (控件名).PagerStyle.Mode=PagerMode.NumericPages;

        其实在一开始使用这种分类样式时,还觉得不错,但时间一长,数据一多起来就会在“上一页”和“下一页”
    的显示位置上出现"..."这样的链接,我问过许多用户,他们中不少人一开始都搞不清“...”都底是什么东西。于
    是我就想把这个表示“上一页”或“下一页”的符号用真正的汉字进行替换。所以就在这个控件中出现了下面的代
    码段了(接上面代码段):
         

     1    
     2 //上面的代码段 
     3 TableCell pager = (TableCell)e.Item.Controls[0];
     4
     5        for (int i = 1; i < pager.Controls.Count; i += 2)
     6        {
     7             Object o = pager.Controls[i];
     8
     9             if (o is LinkButton)
    10             {
    11                 LinkButton h = (LinkButton)o;
    12                 if (h.Text == "" && i == 1)//pager.Controls[i].ID == "_ctl0")
    13                 {
    14                     h.Text = "上一页";
    15                     continue;
    16                 }

    17                 if (i > 1 && h.Text == "")
    18                 {
    19                     h.Text = "下一页";
    20                     continue;
    21                 }

    22
    23   //下面JS用于当跳转页面时显示“正在加载数据”层。
    24                 h.Attributes.Add("onclick""javascript:document.getElementById('Layer5').innerHTML
    25      ='<br /><table><tr><td valign=top><img border=\"0\" src=\"../images/loading.gif\"
    26    /></td><td valign=middle style=\"font-size: 14px;\" >正在加载数据<BR /></td></tr>
    27    </table><BR />';document.getElementById('success').style.display ='block';");
    28             }

    29             if (o is Label)
    30             {
    31                 Label l = (Label)o;
    32                 if (l.Text == "" && i == 1)//l.ID == "_ctl0") 
    33                 {
    34                     l.Text = "上一页";
    35                 }

    36
    37                 if (i > 1 && l.Text == "")
    38                 {
    39                     l.Text = "下一页";
    40                 }

    41            }

    42        }

    43        
    44
    45

            
       3.为了减少因为使用控件而生成过多冗长的viewstate代码,添加了如下的属性:
            

     1[Bindable(true), Category("Appearance"), DefaultValue("")]
     2        public bool SaveDSViewState
     3        {
     4            get
     5            {
     6                object obj = ViewState["SaveDSViewState"];
     7                if (obj == nullreturn false;
     8                else
     9                {
    10                    if (obj.ToString().ToLower() == "true")
    11                        return true;
    12                    else
    13                        return false;
    14                }

    15            }

    16            set
    17            {
    18                ViewState["SaveDSViewState"= value;
    19            }

    20        }

    21

         通过上面的属性设置来判断是否EnableViewState,如下:
            

    1if (!this.SaveDSViewState)
    2        {
    3            this.Controls[0].EnableViewState = false;
    4        }

    5

      
        4.为了让多行编辑记录并提交更方便,还添加了IsFixConlumnControls属性,当它的值为TRUE时,则当前分页下的
    所有记录都以文本框的形式进行显示,也就是在文章一开始的效果图2 中显示的效果(里面的下拉列表框和复选框除外)。
    而相应实现代码如下所示:

       

     1 public void DataGrid_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
     2    {
     3        
     4
     5        if (this.IsFixConlumnControls)
     6        {
     7            if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
     8            {
     9                for (int i = 0; i < e.Item.Cells.Count; i++)
    10                {
    11                    if ((!e.Item.Cells[i].HasControls()))
    12                    {
    13           //判断是否存在只读属性
    14                         if (GetBoundColumnFieldReadOnly()[i].ToString().ToLower() == "false"
    15                         {
    16                                Discuz.Control.TextBox t = new Discuz.Control.TextBox();
    17                                t.ID = GetBoundColumnField()[i].ToString();
    18                                t.Text = e.Item.Cells[i].Text.Trim().Replace("&nbsp;""");
    19
    20                                //设置宽度
    21                                if (this.Columns[i].ItemStyle.Width.Value > 0)
    22                                {
    23                                    t.Width = (int)this.Columns[i].ItemStyle.Width.Value;
    24                                }

    25                                else
    26                                {
    27                                    t.Width = 100;
    28                                }

    29
    30                                e.Item.Cells[i].Controls.Add(t);
    31                         }

    32
    33                    }

    34                    else
    35                    {
    36                        foreach (System.Web.UI.Control c in e.Item.Cells[i].Controls)
    37                        {
    38                            //加载discuz!nt下拉列表框控件
    39                            if (c is Discuz.Control.DropDownList)
    40                            {
    41                               Discuz.Control.DropDownList __dropdownlist = (Discuz.Control.DropDownList)c;
    42                               if (__dropdownlist.SqlText != "")
    43                               {
    44                                   __dropdownlist.AddTableData(__dropdownlist.SqlText);
    45                               }

    46
    47                               try
    48                               {
    49                                   __dropdownlist.SelectedValue = Convert.ToString(DataBinder.Eval(e.Item.
    50      DataItem, __dropdownlist.DataValueField));
    51                               }

    52                               catch
    53                               { ;}
    54                            }

    55
    56                            //加载普通下拉控件
    57                            if (c is System.Web.UI.WebControls.DropDownList)
    58                            {
    59                               System.Web.UI.WebControls.DropDownList __dropdownlist = (System.Web.UI.
    60      WebControls.DropDownList)c;
    61                               try
    62                               {
    63                                    __dropdownlist.SelectedValue = Convert.ToString(DataBinder.Eval(e.Item.
    64      DataItem, __dropdownlist.DataValueField));
    65                               }

    66                               catch
    67                               { ;}
    68                            }

    69
    70                       }

    71                  }

    72
    73      
    74
    75

          同时为便于得到指定行的控件的属性值,还添加了下面两个方法:
          

    1//得到指定行的控件字段的值
    2      public string GetControlValue(int controlnumber, string fieldname)
    3      {
    4          return DNTRequest.GetFormString(this.ClientID.Replace("_"":"+ ":_ctl" + (controlnumber + 3+ 
    5  ":" + fieldname);
    6      }

    7


          

     1//得到指定行的CheckBox控件字段的值
     2      public bool GetCheckBoxValue(int controlnumber, string fieldname)
     3      {
     4          string selectcontrolvalue = GetControlValue(controlnumber, fieldname);
     5          if (selectcontrolvalue == "on")
     6            {
     7                return true;
     8            }

     9            else
    10            {
    11                return false;
    12            }

    13        }

    14

        
        当然为了设置和使用的方便和提高开发效率,还有一些属性和方法要么被重写(如DataSource等),要么被添加了进来
    (如TableHeaderName等)。大家可以详细看一下包中的源码即可, 这里就不再多说了:)


    说句心里话:

        对于这个控件,从公司产品开发的角度来说,基本上满足需要了:)

        但从我个人角度来看却不那么让人满意,原因就是看到了Component Art 里面的Datagrid源码(当然它里面的其它控
    件也同样优秀),让我感到“无地自容”。甚至相当长的一段时间里我都不想(或者说不敢)再写任何东西,因为写出来的代
    码和功能跟人家的东西一对比,就感到自己写的就是“垃圾”。这段时间大约持约了一个多月。相信园子里的朋友中有些人会
    有类以的经历吧!

        但过后慢慢自信心就恢复了过来,因为如果放弃不写代码,水平就会停止下来,而以前所积攒的问题也永远得不到解决,
    所以还是逆着头皮“上路”了。现在回过头来看,一年前的这种心态真是很害人,我甚至想起以前温瑞安的一本武侠小说时中
    的人物,那位大侠自出师以来就是每战必败,江湖人称“逢打必败”冯无极(具体名字记不清了)。人家在那种情况下居然都
    能无所谓,而自己这些年来自认成熟了不少,但还有这种心理,现在回想起来实在是可笑。

      
        好了,主要是东西就先交待到这里了。如果大家有什么问题或建议,欢迎与我交流。
        我的邮件是daizhj@discuz.com, daizhj617595@126.com
      
        
       关键字: .net, 控件, datagrid, component art, control, discuz, discuz!nt, discuznt, 代震军, daizhj
     
       下载链接:/Files/daizhj/datagrid_Controls_Test.rar

     


     

  • 相关阅读:
    hive_学习_00_资源帖
    大数据_学习_02_目录贴_大数据学习总结
    hadoop_异常_02_ExitCodeException exitCode=1: chmod: changing permissions of `/ray/hadoop/dfs/data': Operation not permitted
    hbase_异常_05_End of File Exception between local host is: "rayner/127.0.1.1"; destination host is: "localhost":9000;
    hbase_异常_04_util.FSUtils: Waiting for dfs to exit safe mode...
    hbase_异常_03_java.io.EOFException: Premature EOF: no length prefix available
    hbase_异常_02_hbase无法访问16010端口
    hbase_异常_01_Hbase: Failed to become active master
    【HDU】2147 kiki's game
    【HDU】1517 A Multiplication Game
  • 原文地址:https://www.cnblogs.com/daizhj/p/972623.html
Copyright © 2011-2022 走看看