可快速绑定到关系表或单表的树

类别:.NET开发 点击:0 评论:0 推荐:

using System;

using System.Collections;

using System.ComponentModel;

using System.Drawing;

using System.Data;

using System.Windows.Forms;

using System.Diagnostics;

 

namespace upControls

{

  /// <summary>

  /// 可快速绑定到关系表或单表的树,树自动按照主表及其子表的PrimaryKey列值来绑定

  /// 只遍历一次Rows中的所有行,所以加载速度非常快

  /// 结点内容可以只显示值,也可以显示列名称以作说明

  /// 关系型的数据源要求具有:子列必需具唯一约束

  /// 附加列必需是关系表中的最底层表所拥有的列

  /// 适用于ParentID,ID,Text式之外的所有表的树填充

  /// </summary>

  public class DataTreeView : TreeView

  {

    private System.ComponentModel.Container components = null;

    private DataTable _mainDatatable;

    private string[] _appendColumnNames=null;

    private System.Windows.Forms.ContextMenu cntMenu;

    private bool _columnNameOnText;

    private TreeNode _parentNode;

 

    public DataTreeView ()

    {

      InitializeComponent();

      MenuItem mnu;

      mnu=cntMenu.MenuItems.Add ("显示列名");

      mnu.Click +=new EventHandler(mnu_Click);

      mnu=cntMenu.MenuItems.Add ("-");

      mnu=cntMenu.MenuItems.Add ("展开");

      mnu.Click +=new EventHandler(mnu_Click);

      mnu=cntMenu.MenuItems.Add ("折叠");

      mnu.Click +=new EventHandler(mnu_Click);

      mnu=cntMenu.MenuItems.Add ("-");

      mnu=cntMenu.MenuItems.Add ("全部展开");

      mnu.Click +=new EventHandler(mnu_Click);

      mnu=cntMenu.MenuItems.Add ("全部折叠");

      mnu.Click +=new EventHandler(mnu_Click);

    }

 

    /// <summary>

    /// 清理所有正在使用的资源。

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if(components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    #region 组件设计器生成的代码

    /// <summary>

    /// 设计器支持所需的方法 - 不要使用代码编辑器

    /// 修改此方法的内容。

    /// </summary>

    private void InitializeComponent()

    {

      this.cntMenu = new System.Windows.Forms.ContextMenu();

      this.cntMenu.Popup += new System.EventHandler(this.cntMenu_Popup);

      this.CheckBoxes = true;

      this.ContextMenu = this.cntMenu;

    }

    #endregion

 

///////////////////////////////////////////////////////////////////////////////

 

 

    /// <summary>

    /// 主表,主表的第一个primarykey值将添加在树的顶层

    /// </summary>

    public DataTable MainTable

    {

      get{return this._mainDatatable  ; }

    }

 

    /// <summary>

    /// 除primarykey列之外的列,可以附加在最后一个primarykey列的结点之下的列

    /// </summary>

    public string[] AppendColumnNames

    {

      get{return _appendColumnNames  ; }

    }

 

    /// <summary>

    /// 结点的文本要否包含列名

    /// </summary>

    public bool ColumnNameOnText

    {

      get{return _columnNameOnText  ; }

    }

 

    

    /// <summary>

    /// 填充一个表及其子表到树,结点显示的数据是每一个键列的内容

    /// </summary>

    /// <param name="dataTable">要填充到树的表</param>

    /// <param name="parentNode">要填充到哪一个现有的结点之下</param>

    /// <param name="appendColumnnames">附加列(非键列),格式是:表名.列名,或只有列名</param>

    /// <param name="ColumnnameOnText">列名要不要显示在结点的文本之中</param>

    /// <param name="clearNodes">要不要清除现存的结点再填充</param>

public void Fill ( DataTable dataTable,TreeNode parentNode,

string[] appendColumnnames,bool ColumnnameOnText,bool clearNodes)

    {

      _mainDatatable =dataTable;

      _appendColumnNames=appendColumnnames;

      _columnNameOnText=ColumnnameOnText;

      _parentNode=parentNode;

      TryBinding(clearNodes);

    }

 

    /// <summary>

    /// 尝试填充树,如果各个必需属性都设置好了

    /// </summary>

    /// <param name="clearNodes"></param>

    public void TryBinding(bool clearNodes)

    {

      if (clearNodes)

      {

        this.Nodes.Clear() ;

        TryBinding(_mainDatatable,null,_appendColumnNames);

      }

      else

        TryBinding(_mainDatatable,_parentNode,_appendColumnNames);

    }

 

private void TryBinding(DataTable dataTable,TreeNode parentNode,

string[] appendColumnNames)

    {

      if (dataTable==null) return ;

 

      //先加入主表名结点

      if (parentNode!=null)                      

        parentNode=parentNode.Nodes.Add (dataTable.TableName );

      else

        parentNode=this.Nodes.Add (dataTable.TableName );

      

      //返回包含了一个表中所有键列的数组,但是如果表是子表的话,则作为关系的键列不包含在内
      //因为父表中存在相同的列值,不需要加载重复内容的结点

      DataColumn[] PrimaryKey=AddThesePrimaryKey(dataTable);      

      TreeNode[]  priNodes=new TreeNode[PrimaryKey.Length ];

      string sort=string.Empty;

      //作排序准备

      for (int i=0 ; i < PrimaryKey.Length ; i ++ )          

        sort=sort + "," + PrimaryKey[i].ColumnName ;

 

//在下边的数据行遍历中需要确保是按序排列的,快速加载全靠它

sort=sort.Trim (',');                      

      //已删除的行当然不要加到树

      DataRow[] allRows=dataTable.Select (string.Empty,sort,DataViewRowState.CurrentRows ); 

      foreach (DataRow dr in allRows)

      {

        //下面的for设置priNodes数组,保证priNodes内有n个Node对应于当前行的每一个键列

        for (int i=0 ; i < PrimaryKey.Length  ; i ++  )        

        {

          string colName=PrimaryKey [i].ColumnName ;

          //内容为null,则这一行中这个键列之后的列内容都都不会加到树了

          if (dr[colName]==null) break;              

          TreeNode nod=new TreeNode ();

          //格式化结点的文本

          this.FormatNodeText (nod,dr,PrimaryKey [i]);      

          if (priNodes[i]!=null)

          {

            //是否已经存在,前面的键列一般是允许重复内容的

            if ( priNodes[i].Text !=nod.Text )          

              priNodes[i]=nod;

          }

          else

            priNodes[i]=nod;

        }

 

        int r =0 ;

        TreeNode pnod=null;

        //判断priNodes中的node是否要加到树以及要加到哪里(不能用foreach,顺序不同了)

        for (int i=0 ; i < priNodes.Length ; i ++  )        

        {

          if (priNodes[i] ==null) break;

          TreeNode nod=priNodes[i];

          if (r==0 )

          {

            if (!parentNode.Nodes.Contains (nod))

              parentNode.Nodes.Add (nod);

          }

          else if (!pnod.Nodes .Contains (nod))

            pnod.Nodes .Add (nod);

          pnod=nod;

          ++r;

        }

        //在上面的循环中没有被设置,表示每一个键列都是null,虽然不太可能

        if (pnod==null) continue;                  

        

        //附加列必需是关系表中的最底层表

        if (dataTable.ChildRelations.Count ==0 && appendColumnNames!=null)    

        {

          foreach (string fullColName in appendColumnNames)

          {

            //appendColumnNames中的列名可以是:表名.列名,或只有列名

//因为一个表可能有多个关系,而表名起导航作用

string[] fullName=fullColName.Split ('.');            

            

            string tabName=string.Empty,colName=string.Empty;        

            if (fullName.Length >1)

            {

              tabName=fullName[0];

              colName=fullName[1];

            }

            else

            {

              tabName=dataTable.TableName ;

              colName=fullName[0];

            }

            if (tabName==dataTable.TableName && dataTable.Columns .Contains (colName))

            {

              TreeNode nod=new TreeNode ();

              this.FormatNodeText (nod,dr,dataTable.Columns [colName]);

              pnod.Nodes.Add (nod);             

            }

          }

        }

        ///再填充子表的内容到树,每一个表都只历遍一次Rows

        foreach (DataRelation drl in dataTable.ChildRelations )

          TryBinding(drl.ChildTable ,pnod,appendColumnNames);

      }

    }

 

    /// <summary>

    /// 返回包含了一个表中所有键列的数组,但是如果表是子表的话,则作为关系的键列不包含在内(因为父表中存在相同的列值,不需要加载重复内容的结点)

    /// </summary>

    /// <param name="dt"></param>

    /// <returns></returns>

    private DataColumn[] AddThesePrimaryKey(DataTable dt)

    {

      DataColumn[] keys=null;

      ArrayList list=new ArrayList ();

      foreach (DataColumn dc in dt.PrimaryKey)

        list.Add (dc);

 

      if (dt!=this.MainTable )              

        foreach (DataRelation drl in dt.ParentRelations )

          foreach (DataColumn dc in drl.ChildColumns )

            list.Remove (dc);

 

      if (list.Count >0 )

      {

        keys=new DataColumn [list.Count ];

        list.CopyTo (keys,0);

      }

      return keys;

    }

 

 

    /// <summary>

    /// 将结点文本格式化

    /// </summary>

    /// <param name="node"></param>

    /// <param name="dataRow"></param>

    /// <param name="dc"></param>

    private void FormatNodeText(TreeNode node,DataRow dataRow,DataColumn dc)

    {

      string nodeText=string.Empty,nameCol=string.Empty,caption=string.Empty;

      ///Column_AutoID uca=null;

      ///Column_AutoID包含了一个ID列的编码规则的信息,

      ///if (dc.ExtendedProperties.ContainsKey ("Ext_AutoID"))

      ///uca=dc.ExtendedProperties["Ext_AutoID"] as Column_AutoID ;

 

      if (dataRow.Table.Columns.Contains (dc.ColumnName ))

      {

        nodeText=dataRow[dc].ToString ().Trim ();

        ///Column_AutoID.IDNameColumn: 保存一个ID列的名称列名,如果有此项,结点文本就可用名称来说明ID了,例如用姓名说明人员编号

        ///if (uca!=null && uca.IDNameColumn !=null)        

        ///{

        ///nameCol=uca.IDNameColumn ;

        ///if (dataRow.Table.Columns .Contains (nameCol))

        ///nameCol=  dataRow[nameCol].ToString ();      //名称列的值

        ///}

 

        caption=dc.Caption ;

        if (dc.DataType ==typeof( Boolean) )

        {

          nodeText=caption;

          node.Checked = System.Convert.ToBoolean (dataRow[dc]);

        }

        else

        {

          nameCol=nameCol==string.Empty?nameCol:"\t[" + nameCol +"]";

          if (this.ColumnNameOnText )

            nodeText=caption + ":" + nodeText + nameCol;

          else

            nodeText= nodeText + nameCol;

        }

        node.Text =nodeText;

      }

    }

 

    private void cntMenu_Popup(object sender, System.EventArgs e)

    {

    

    }

 

    private void mnu_Click(object sender, EventArgs e)

    {

      MenuItem mnu=sender as MenuItem ;

      switch (mnu.Index )

      {

        case 0:

          mnu.Checked=!mnu.Checked;

          _columnNameOnText=mnu.Checked ;

          this.TryBinding (true);

          break;

        case 2:

          if (this.SelectedNode !=null)

            this.SelectedNode.ExpandAll ();

          break;

        case 3:

          if (this.SelectedNode !=null)

            this.SelectedNode.Collapse  ();

          break;

        case 5:

          this.ExpandAll ();

          break;

        case 6:

          this.CollapseAll ();

          break;

      }

    }

  }

}



///作者: CSDN网名alias88,邮件:[email protected],QQ:63343

本文地址:http://com.8s8s.com/it/it42133.htm