这两天正在建树。
目标有几个:
1. 信息在数据库中,程序自动装载。
2. 表要少,特别是数据信息只在一个表中为好。因为目前的程序分三种:
1) 演示程序,信息在xml 文件中为好。随时都能展示给其它人看。
2)小客户端程序,不需要连远端数据库,用本地的access数据库就可以工作。
3)客户端工作在连接远端数据库的情况下。
也就是说,基础信息,有可能从远端同步到本地。
所以,表要少。
而且,树的层级关系,目前来看,不同的被管对象是不对的,是动态变化的,我们不能说哪一层,具有什么特定的含义,所以,要建一个子从属于父的关联树。
然后,展示到界面上。
小团队开发,或是一个人开发,象我这样,最好先考虑界面。因为界面是工作量最大的,内存结构做得好,可不一定界面上就好显示。
一开始调研了一个小程序: HierarchyTree
http://www.codeproject.com/script/articles/download.aspx?file=/KB/aspnet/HierarchyTree/HierarchyTree.zip&rp=http://www.codeproject.com/Articles/140827/Dynamic-Binding-Of-Hierarchy-Data-Structure-To-Tre
这个例子,展示了一种通常的作法。
但从各个方面,我不喜欢。看来在C#里面处理树不是很方便。当然,也是作者没有写好。
foreach (HierarchyTrees.HTree hTree in hierarchyTrees) { HierarchyTrees.HTree parentNode = hierarchyTrees.Find(delegate(HierarchyTrees.HTree emp) { return emp.NodeID == hTree.UnderParent; }); if (parentNode != null) { foreach (TreeNode tn in tvHierarchyView.Nodes) { if (tn.Value == parentNode.NodeID.ToString()) { tn.ChildNodes.Add(new TreeNode(hTree.NodeDescription.ToString(), hTree.NodeID.ToString())); } if (tn.ChildNodes.Count > 0) { foreach (TreeNode ctn in tn.ChildNodes) { //RecursiveChild(ctn, parentNode.NodeID.ToString(), hTree); } } } } else { tvHierarchyView.Nodes.Add(new TreeNode(hTree.NodeDescription, hTree.NodeID.ToString())); } } tvHierarchyView.ExpandAll(); }
这里我们看到,作者进行了两次迭代。比较糟的做法。
通常来讲,应当几步:
1) 在内存中建树。
2) 插入到界面。
插入界面的过程,有两个功能,需要注意:一个是在内存树中,如何遍历,如何快速定位。
一个是在界面树中,如何快速定位。
所以,在建内存数,和插入界面的时候,应当伴随建一个hash 表,或是快速map 之类的,两棵树都要有。
否则,代码就这成这位兄台这样:看起来很短,但究其实质,很糟。
为什么两步呢?以前我很喜欢一步到位,这样好处是,不用自己建树了,直接用界面控件的树就好了。不能说不好,因为这样有一个很重要的好处:不需要进行内存与界面的同步工作。对于动态树,这的确是要小心处理的,如果内存树还有界面树又分别建了索引,同步的工作量,还是有的。
但,编程,一味图快,图简单,问题也多。因为程序和人一样,是要腐烂的。总有一天,作者本人也不想闻一闻,究其原因,就是语义不清。
有了良好的内存结构,意味着复用性好:一方面,本程序内,其它模块好利用,另一方面这些代码,也便于复用到其它的项目中。
=============
本着这样的意图。我再次开始了脑子。
一般而言,大公司做的东西相对好一些。所以,我找到devExpress中的例子,花了大半天时间,建环境。
因为要跟代码。
看的例子是这个:用的是DX2011.2.5_src
XtraTreeList
刚接个电话,闲话少说吧,
这个例子,是从一个xml中读取信息,放到界面上。
可以猜测,一定有两个固有的字段:
ID
ParentID
这里,我再强调一下关键是你要有原码。看原码比一切理论都有实际意义。
另外,一定要建好调试环境。因为我手头的devExpress原码,也搞不清楚是从哪拿的,没有注释,可能这也是devexpress公司的策略,他们不会发布有注释的原码。
但也足够了。
至于,如何建这种环境,以后我来讲解。接了两个电话,已超时了。
<?xml version="1.0" standalone="yes"?> <NewDataSet> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="Table"> <xs:complexType> <xs:sequence> <xs:element name="Id" type="xs:double" minOccurs="0" /> <xs:element name="ParentId" type="xs:double" minOccurs="0" /> <xs:element name="FirstName" type="xs:string" minOccurs="0" /> <xs:element name="LastName" type="xs:string" minOccurs="0" /> <xs:element name="JobTitle" type="xs:string" minOccurs="0" /> <xs:element name="Phone" type="xs:string" minOccurs="0" /> <xs:element name="EmailAddress" type="xs:string" minOccurs="0" /> <xs:element name="AddressLine1" type="xs:string" minOccurs="0" /> <xs:element name="City" type="xs:string" minOccurs="0" /> <xs:element name="PostalCode" type="xs:string" minOccurs="0" /> <xs:element name="CountryRegionName" type="xs:string" minOccurs="0" /> <xs:element name="StateProvinceName" type="xs:string" minOccurs="0" /> <xs:element name="GroupName" type="xs:string" minOccurs="0" /> <xs:element name="BirthDate" type="xs:dateTime" minOccurs="0" /> <xs:element name="HireDate" type="xs:dateTime" minOccurs="0" /> <xs:element name="Gender" type="xs:string" minOccurs="0" /> <xs:element name="MaritalStatus" type="xs:string" minOccurs="0" /> <xs:element name="Title" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <Table> <Id>109</Id> <ParentId>0</ParentId> <FirstName>Bruce</FirstName> <LastName>Cambell</LastName> <JobTitle>Chief Executive Officer</JobTitle> <Phone>(417) 166-3268</Phone> <EmailAddress>[email protected]</EmailAddress> <AddressLine1>4228 S National Ave</AddressLine1> <City>Oaks</City> <PostalCode>65809</PostalCode> <StateProvinceName>California</StateProvinceName> <CountryRegionName>United States</CountryRegionName> <GroupName>Executive General and Administration</GroupName> <BirthDate>1957-09-06T00:00:00</BirthDate> <HireDate>2000-07-02T00:00:00</HireDate> <Gender>M</Gender> <MaritalStatus>M</MaritalStatus> <Title>Mr.</Title> </Table> <Table> <Id>42</Id> <ParentId>109</ParentId> <FirstName>Cindy</FirstName> <LastName>Haneline</LastName> <JobTitle>Information Services Manager</JobTitle> <Phone>(918) 161-3649</Phone> <EmailAddress>[email protected]</EmailAddress> <AddressLine1>2429 E. 15th Street</AddressLine1> <City>Vista</City> <PostalCode>74014</PostalCode> <StateProvinceName>California</StateProvinceName> <CountryRegionName>United States</CountryRegionName> <GroupName>Executive General and Administration</GroupName> <BirthDate>1973-12-23T00:00:00</BirthDate> <HireDate>1996-11-06T00:00:00</HireDate> <Gender>F</Gender> <MaritalStatus>S</MaritalStatus> </Table>
============
好了,开始。
DX2011.2.5_src\Sources\DevExpress.XtraTreeList\DevExpress.XtraTreeList\TreeList.cs(692):
protected const string defKeyFieldName = "ID",
defParentFieldName = "ParentID",
defImageIndexFieldName = "ImageIndex";
public TreeList() : this(null) {
}
这是最关键的几句话,我一开始跟了半天,才找到,原来是写死的。
初始化:
namespace DevExpress.XtraTreeList.Demos {
public partial class NodesFiltering : DevExpress.XtraTreeList.Demos.TutorialControl {
public NodesFiltering() {
InitializeComponent();
InitData();
InitEditors();
//InitFilter();
treeList1.Columns["pkgName"].AllNodesSummary = true;
treeList1.Columns["pkgName"].SummaryFooter = SummaryItemType.Count;
treeList1.Columns["pkgName"].OptionsFilter.FilterPopupMode = FilterPopupMode.CheckedList;
treeList1.OptionsView.ShowSummaryFooter = true;
}
这里,我们改一下,不从xml,改从sqlserver中读。
private void InitData() {
string DBFileName = DevExpress.Utils.FilesHelper.FindingFileName(Application.StartupPath, "Data\\Employees.xml");
if(DBFileName != "") {
DataSet dataSet = new DataSet();
//dataSet.ReadXml(DBFileName);
using (SqlConnection connection = new SqlConnection(@"Data Source=127.0.0.1;Initial Catalog=AutoPack;Persist Security Info=True;User ID=sa;Password=12345"))
{
connection.Open();//SSP_GET_HIERARCHY
using (SqlCommand command = new SqlCommand("select * from TBL_TREE_HIERARCHY", connection))
{
//command.CommandType = System.Data.CommandType.StoredProcedure;
//SqlDataReader reader = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
SqlDataAdapter da = new SqlDataAdapter(command);
//DataSet ds = new DataSet();
//fill the DataSet using default values for DataTable names, etc.
da.Fill(dataSet);
}
}
treeList1.DataSource = dataSet.Tables[0].DefaultView;
treeList1.ForceInitialize();
treeList1.ExpandAll();
treeList1.BestFitColumns();
}
}
//这段代码,要是不处理,则确保表里有相关的字段:GroupName
string currentGroupName;
private void treeList1_GetStateImage(object sender, GetStateImageEventArgs e) {
string[] groupNames = new string[] { "Administration", "Inventory", "Manufacturing", "Quality", "Research", "Sales" };
currentGroupName = (string)e.Node.GetValue("GroupName");
e.NodeImageIndex = Array.FindIndex(groupNames, new Predicate<string>(IsCurrentGroupName));
}
private bool IsCurrentGroupName(string groupName) { return currentGroupName.Contains(groupName); }
数据库表如下:
USE [AutoPack]
GO
/****** Object: Table [dbo].[TBL_TREE_HIERARCHY] Script Date: 02/25/2014 17:45:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBL_TREE_HIERARCHY](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ParentID] [int] NULL,
[pkgName] [nvarchar](511) NULL,
[NODE_DESCRIPTION] [nvarchar](511) NULL,
[ImageIndex] [int] NULL,
[UVERSION] [nvarchar](511) NULL,
[VER] [nvarchar](511) NULL,
[Title] [nvarchar](511) NULL,
[GroupName] [nvarchar](511) NULL,
[pkgFilename] [nvarchar](511) NULL
) ON [PRIMARY]
GO
内容,我随意写的
ID ParentID pkgName NODE_DESCRIPTION ImageIndex UVERSION VER Title GroupName pkgFilename
1 0 a Mathematics 0 A 1 wert Administration a.out
2 1 b Algebra NULL A 2 fgg Inventory b.out
3 1 c Geometry 0 A 3 s Research c.out
4 3 d Triangle 0 A 1 ert Research d.out
5 4 e By Relative Length 0 A NULL f Research e.out
6 4 f By Internal Angle 0 A 232 s Research f.out
7 5 g Equilateral Triangle 0 A 124 dfgwr Research g.out
8 5 h Scalene Triangle 0 A 3 NULL Quality h.out
9 5 i Isosceles Triangle 0 A 5 sfdg Quality i.out
10 6 j Oblique > 90 Degree:Obtuse Angled Traingle 0 A 5 s Quality j.out
11 6 k Oblique < 90 Degree:Acute Angled Traingle 0 A 5 f Quality k.out
12 6 l Right Angled Triangle 0 A 4 sfg Quality l.out
13 2 m Elementary Algebra 0 A 4 sf Quality m.out
14 2 n Abstract Algebra 0 A 6 gsfgh Manufacturing n.out
15 2 o Linear Algebra 0 A 345 fgj Manufacturing o.out
16 7 p All Sides are Equal 0 A 3 fghj Manufacturing p.out
下一步,把这里改了在namespace DevExpress.XtraTreeList.Demos {
partial class NodesFiltering {
...
this.treeListColumn1.Caption = "包名称";
this.treeListColumn1.FieldName = "pkgName";
this.treeListColumn1.MinWidth = 33;
this.treeListColumn1.Name = "treeListColumn1";
this.treeListColumn1.Visible = true;
this.treeListColumn1.VisibleIndex = 0;
this.treeListColumn1.Width = 105;
//
// treeListColumn2
//
this.treeListColumn2.Caption = "文件名";
this.treeListColumn2.FieldName = "pkgFilename";
this.treeListColumn2.Name = "treeListColumn2";
this.treeListColumn2.Visible = true;
this.treeListColumn2.VisibleIndex = 1;
this.treeListColumn2.Width = 106;
其它列删除
好了,跑起来,