分享
 
 
 

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

王朝c#·作者佚名  2006-12-17
窄屏简体版  字體: |||超大  

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,邮件:alias88@163.com,QQ:63343

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有