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

水平树控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (7投票s)

2008年1月12日

CPOL

5分钟阅读

viewsIcon

81067

downloadIcon

1247

一个类似 Vista Explorer 的控件实现,用于表示分层数据。

引言

这是一个自定义的树控件,旨在解决分层数据的垂直显示问题。

背景

我使用 Vista,尽管我不喜欢它的许多方面,但我却钟爱 Windows Explorer 中用于解析磁盘文件夹结构的那个控件。

当用户想要从隐藏的树中选择某项时,会出现一个自定义列表框,如下所示:

如您所见,对于 Explorer 实现,该控件表示文件夹或特殊文件夹(如“我的文档”),每个都有其自定义图标。还使用了额外的图形,我试图模仿它们,但由于我缺乏艺术技能,未能完全复制;所以如果有人有任何建议,我会非常感兴趣。

控件解析

内部数据表示

分层数据的内部表示是类似树的 NodeBase 对象集合。NodeBase 是抽象的,以下是它的有效实现:

Node 用于数据。它包含 DisplayedText、相关的 Image 和一个 Tag 属性,该属性保存节点实际对象的引用。此对象用于操作节点的激活事件。目前,只提供了一个特殊的节点类:NodeSeperator。特殊节点类永远不会出现在工具栏上。它们只出现在显示每个节点子节点的选择器中。

视觉表示

上述数据的视觉表示是通过显示一系列控件来完成的。每个控件基本上都派生自 NodeBaseControl,它是抽象的,并有两个独立的实现。NodeBaseControl 最终持有一个 NodeBase 对象。

NodeLinksControl 在工具栏中只使用一次,即树,它是最左边可见的对象。它有一个显示当前活动节点图像的图像,并且可以显示各种节点的选择,这些节点可能不是分层数据的一部分,例如链接。

NodeControl 用于表示所有其他节点。根节点是一个 NodeControl。它显示文本和图像以显示选择器。如果没有子节点,则不需要选择器,因此不显示图像。

当添加许多节点时,水平树会像 Vista 的 Explorer 一样隐藏,最靠近根部,并将它们添加到 NodeLinksControl 的选择中。它还会更改图像以显示节点已隐藏的事实。

在任何单一点,当没有节点被选中时,只需点击它或从选择器中选中它,该节点就会成为最后一个可见节点,被激活,并且任何子节点都会从视觉树中移除。Selector 是一个自定义的 ListBox,用于显示节点的子节点。分隔符节点不能被选中或高亮显示。当请求选择器时,树处于

SelectionMode==true

直到选中一个节点,或在选择器或 NodeBaseControl 外部单击鼠标。为了实现这一点,选择器被添加到 ParentFormControls 中并置于最前面。它还会捕获鼠标并重定向所需的鼠标消息。

NodeBaseControl 解析

每个 NodeBaseControl 由两个并排的 PartBase 控件显示。PartBase 是抽象的,其子类是:

每个部分有效地了解另一个部分,并且基本上具有一个驱动其外观的 State。这些部分控件中的每一个都处理来自鼠标的所有输入功能,并考虑到树的状态或其其他部分的 State

Using the Code

您可以将控件拖放到表单中(在我的演示中,它被命名为 horizontalTreeControl1)。并未实现完整的设计时支持,因为我没有时间创建它,而且我对学习如何实现不感兴趣,因为我相信未来在于 WPF。在演示项目中,构建了一个类型化的数据集来模拟类似于磁盘中找到的文件夹结构。要填充 LinksControl,您可以使用类似以下的代码:

this.horizontalTreeControl1.LinksNode.Suspend();
this.horizontalTreeControl1.LinksNode.AddNode("Link1", 
                            Properties.Resources.Link1, null);
this.horizontalTreeControl1.LinksNode.AddSeperator();
this.horizontalTreeControl1.LinksNode.AddNode("Link2", 
                            Properties.Resources.Link2, null);
Node n = new Node();
this.horizontalTreeControl1.LinksNode.Resume();

SuspendResume 用于抑制 Paint 事件。每个节点都有一个

  • 显示的文本
  • Image
  • 对象引用,它基本上用作 Windows Forms 控件中的 Tag 属性

要实现工具栏的 RootNode,请使用类似以下的代码:

n = new Node();
n.DisplayText = ds.Folder[0].Name;
n.Tag = ds.Folder[0];
n.Image = Properties.Resources.Root;
this.horizontalTreeControl1.RootNode = n;

现在,有两种方法可以在每个节点中添加子节点。您可以使用上面为 LinksNode 所述的每个节点上的 AddNode,或者捕获 NodeActivated 事件以在指定时间动态添加节点。当节点被激活时,就会调用此事件。当没有理由将所有数据添加到树中,或者数据随时间变化时,这非常有用。像这样捕获事件:

this.horizontalTreeControl1.NodeActivated += 
    new Sarafian.Framework.ClientSide.UI.Resources.HorizontalTree.
    DelegateEnum.DelegateNodeActivated(horizontalTreeControl1_NodeActivated);

并像这样实现它:

void horizontalTreeControl1_NodeActivated(Node node)
{
    if (node.Tag is DataRow)
    {
        node.ClearNodes();
        DsTest.FolderRow row = (DsTest.FolderRow)node.Tag;
        node.Suspend();
        foreach (DsTest.FolderRow subRow in 
                 row.GetChildRows(this.ds.Folder.ChildRelations[0]))
        {
            node.AddNode(subRow.Name, Properties.Resources.Folder, subRow);
        }
        node.Resume();
    }
    else
    {
        //MessageBox.Show(node.DisplayText);
    }
}

如您所见,再次使用了 AddNode。此事件也用于了解何时激活了节点。例如,如果该节点是一个文件夹,而您想要它的文件。Tag 属性保存节点的实际数据,可以是任何对象。

关注点

此控件并未完全实现 Windows Vista Explorer 的路径选择器。需要另一个带有 TextBox 和自动填充功能的控件,该控件将在水平树控件的前面激活。此控件可以显示任何类型的分层数据。

在开发过程中,我犯了许多错误,导致进行了几次重构尝试,因此代码并不完美。有一些文件是从其他类库添加到类库中的,我在这里导入它们是为了使解决方案更简单。这些文件是:

  • Percent.cs
  • Rectangle.cs
  • Point.cs

有关更多信息,请参阅我在我的 博客 上的 帖子

历史

  • 版本 1.0 创建。
© . All rights reserved.