65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 3.0 和 SQL 2005 通过修改后的先序遍历技术表示的层次树

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (26投票s)

2008年12月10日

CPOL

6分钟阅读

viewsIcon

118519

downloadIcon

3281

将各种算法收集到一个库中,用于在不同格式之间转换层次树,并允许它们表示为 SQL 2005,支持的格式有 TreeView、文本、表格、修改后的先序遍历树和图形。

Tree_Traversal/demosmall.JPG

引言

在我从事 (CMS) 内容管理系统的工作中,并且在内容映射模块(一个 .NET 桌面应用程序)中,我需要将我的内容分类到一系列的类别中,例如层次树,但我也需要将该树表示成多种格式,例如文本、表格、节点和数据库。

我搜索了网络,寻找哪个 .NET 库可以将我的树表示到 SQL2005 数据库中。我找不到那个库,但我找到了一篇(Gijs Van Tulder)题为 Storing Hierarchical Data in a Database 的文章,其中解释了 MPTT (修改后的先序遍历树) 的概念。不幸的是,它是为 PHP 开发人员编写的,所以我将该概念翻译成了 C# 和 SQL2005 代码。此外,我还编写了一些算法,将树表示为文本、表格、树视图节点和图形格式。我还使用提供者模型技术作为支持 SQLite 等各种数据库的步骤。

最后,我将所有这些东西收集到一个项目中,但是,整个类可以轻松地重用并分离成类库,即使是我编写的创新算法也可以单独重用,就像用于渲染树的图形表示的算法一样。

现在我认为我有一个高效的方法来添加、插入、删除和检索树元素,可以使用多种方法,包括一种用于 SQL2005 数据库的方法,该方法使用非常有效的架构,即 MPTT,它通过每次活动只需一次查询来最小化数据库查询的数量。

什么是层次树?

简单地说,它是一种数据结构,看起来像一棵真实的树,尽管层次树通常与真实树相比是颠倒显示的;也就是说,根在顶部,叶子在底部,该结构中的元素通过父子关系相互关联,这些关系由元素之间的连接线表示,称为“分支”,此外,没有上级的元素称为“”,没有子元素的元素称为“叶子”。

以下示例演示了动物王国树的文本表示

Animal Kingdom
#Backbones
##Mammal
##Lungs
##Reptile
##Bird
##Gills
###Fish
###Amphibian
#No Backbones
##Starfish
##Mollusk
###Snail
###Clam
##Jointed Legs
###Insect
###Spider
###Crustacean
图 1:层次树,文本表示。

所以我们有 1 个根(动物王国)和 12 个叶子(哺乳动物爬行动物鸟类鱼类两栖动物海星蜗牛蛤蜊昆虫蜘蛛甲壳类动物),我们有 18 行,每行包含一个树元素/节点,节点标题前的 (#) 数量代表元素在树中的深度级别,具有相同级别且具有相同父级的元素称为“兄弟姐妹”。

这种树的图形表示存在于图 2 中,其中矩形是节点的符号表示,并且这种树的表格表示也存在于图 3 中。

Tree_Traversal/treesmall.JPG

图 2:层次树图形表示。
 
ID 父节点 ID 标题 左侧 右侧
1 0 动物王国 1 36
2 1 脊椎动物 2 17
3 2 哺乳动物 3 4
4 2 5 6
5 2 爬行动物 7 8
6 2 Bird 9 10
7 2 11 16
8 7 鱼类 12 13
9 7 两栖动物 14 15
10 1 无脊椎动物 18 35
11 10 海星 19 20
12 10 软体动物 21 26
13 12 蜗牛 22 23
14 12 蛤蜊 24 25
15 10 有关节腿 27 34
16 15 昆虫 28 29
17 15 蜘蛛 30 31
18 15 甲壳类动物 32 33
 
图 3:层次树表格表示。

使用演示项目

回到上一节,复制图 1 的文本,并将其粘贴到演示项目界面右手边的TextBox中,然后,按标题为“<<”的按钮,将文本转换为左手边TreeView控件中的传统树表示。

通过单击名为“表格”的单选按钮来显示树的表格表示,并使用名为“文本”的另一个单选按钮在文本和表格之间切换。

要将您刚刚转换的树保存到 SQL2005 数据库,您应该首先选择一个现有数据库,但您不必创建任何表,只需通过单击名为“连接字符串”的按钮来构建一个有效的连接字符串,在组合连接字符串后,只需单击名为“保存”的按钮即可将树保存到名为“tblTree”的表中,如果该表不存在,它将自动创建,如果存在,它将被截断。

当然,如果您提供了正确的连接字符串,您可以随时再次加载此树,只需组合连接字符串并单击“加载”按钮即可。但是,您不必每次打开演示项目时都输入连接字符串,它会自动将连接字符串保存到名为 constring.txt 的文件中。

现在,您可以通过单击“绘制”按钮来享受树的图形表示。

Using the Code

您只需创建一个 MpttCoreEngine 类的对象,然后调用其接口中的任何方法。

MpttCoreEngine engine = new MpttCoreEngine();

此类包含编写用于以各种格式表示层次树的所有算法,以下列表包含此类中最重要的方法。

SetConnectionString 使用此函数向引擎提供连接字符串
ConvertMpttIntoTree 使用此函数从数据库加载树节点
ConvertTreeIntoMptt 使用此函数将树节点保存到数据库
ConvertTextIntoTree 将文本树转换为适合TreeViewTreeNode
ConvertTextTableIntoTree 将表格树转换为适合TreeViewTreeNode
ConvertTreeIntoText TreeNode转换回文本表示
ConvertTreeIntoTable TreeNode转换回表格表示
DrawTree TreeNode绘制到Image上并返回Image以供以后使用

以下代码是演示项目中用于演示如何使用这些方法的一个快照。

	private void LoadDb()
	{
	    try
	    {
	        treeView.Nodes.Clear();
	        TreeNode node = null;
	        engine.SetConnectionString(_connectionString);
	        node = engine.ConvertMpttIntoTree();
	        if (node != null)
	        {
	            treeView.Nodes.Add(node);
	            treeView.ExpandAll();
	        }
	    }
	    catch (Exception e)
	    {
	        MessageBox.Show(e.Message +
                     "\r\nThe Connection string may not be correct");
	    }
	}
	
	private void SaveDb()
	{
	    if (treeView.Nodes.Count <= 0)
	        return;
	    try
	    {
	        engine.SetConnectionString(_connectionString);
	        engine.ConvertTreeIntoMptt(treeView.Nodes[0]);
	    }
	    catch (Exception e)
	    {
	        MessageBox.Show(e.Message +
                     "\r\nThe Connection string may not be correct");
	    }
	}
	
	private void TreeReflection()
	{
	    char t = (delimiter.Text.Length > 0 ? delimiter.Text[0] : '#');
	    engine.LevelDelimiter = t;
	    //
	    treeView.Nodes.Clear();
	    TreeNode node = null;
	    if (textual.Checked)
	        node = engine.ConvertTextIntoTree(textView.Text, _delimiter, '*');
	    else//tabular
	        node = engine.ConvertTextTableIntoTree(textView.Text, true);
	    if (node != null)
	    {
	        treeView.Nodes.Add(node);
	        treeView.ExpandAll();
	    }
	}
	
	private void TextReflection()
	{
	    if (treeView.Nodes.Count <= 0)
	        return;
	    char t = (delimiter.Text.Length > 0 ? delimiter.Text[0] : '#');
	    engine.LevelDelimiter = t;
	    if (textual.Checked)
	    {
	        textView.Text = engine.ConvertTreeIntoText(treeView.Nodes[0], false);
	    }
	    else
	    {
	        textView.Text = engine.ConvertTreeIntoTable(treeView.Nodes[0], false);
	    }
	}

	private void btnDraw_Click(object sender, EventArgs e)
	{
	    if (treeView.Nodes.Count <= 0)
	        return;
	    Bitmap bmp = engine.DrawTree(treeView.Nodes[0]);
	    TreePreview preview = new TreePreview(bmp);
	    preview.Show();
	}

关注点

代码充满了有趣的要点,其中许多可能需要单独的文章来解释。以下是最重要的要点

  1. 绘制树节点的算法非常复杂,我使用了匿名函数、递归和回调来计算节点的确切位置。您可以在 DrawTree 函数中找到该算法。
  2. MPTT 的概念在网络上的几个地方都有解释,但我将 MPTT 的概念与一个称为“提供者模型”的设计模式结合起来,其中创建了一个名为 TreeProvider抽象类来实现和支持任何数据库类型,如 SQL2005、MySQL 或 SQLite。
  3. 您可以通过仅复制名为 Core 的文件夹到任何您想要的项目中,来轻松地将演示项目的核心分离成一个类库。

历史

版本 1.0

  1. TreeView 表示
  2. 文本表示
  3. 表格表示
  4. MPTT 表示
  5. 图形表示
  6. SQL2005 支持
© . All rights reserved.