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

使用 MS Access 表和窗体创建的树状视图控件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8投票s)

2013 年 5 月 27 日

CPOL

7分钟阅读

viewsIcon

125824

downloadIcon

4781

如何使用 Access 表和窗体创建树形视图控件。

引言

Microsoft Access 没有原生的树形视图或列表视图控件——我们必须依赖一个名为 MSCOMCTL.OCX 的 Windows 控件来提供这些服务。有时,该控件会更新,这有时会导致应用程序崩溃。本文旨在解决构建一个“原生”树形视图控件的需求,该控件由 Access 表、窗体和代码构建,以绕过该问题。

背景

上次控件崩溃让我认识到,从现在开始避免该问题的最佳方法是自己编写。顺便说一句,一个很好的解决该问题的方法可以在这里找到。

控件看起来怎么样?

该控件由一个 Access 窗体和一个底层的本地表构建而成……

控件设计用于允许用户通过单击复选框或使用箭头键来展开、折叠或上下移动焦点来选择单个节点。

右侧的数字表示每个节点所属的子节点——这些是可选的。

它是如何工作的?

在创建了一个 Access 数据库后,我合并了 Northwind Traders 数据库中的一些表。数据模型如下所示……

Tree-View data model

约定

z为前缀的表表示它们是本地的,通常其内容是临时的。

hqry为前缀的查询表示这通常是一个隐藏的查询。使用带有后缀数字的查询名称是我将相关功能按使用顺序分组的方式——在大系统中,很难想出有意义的名称!

节点的级别表示其在 X 轴上的逻辑位置。在上方的树形视图截图中,零级节点是客户名称,一级节点是订单日期,二级节点是订单日期上订购的产品。

底层 Z 表

窗体加载时,会触发 OnOpen 事件,并加载底层表;选择后,它看起来像这样……

Z Table showing specimen data

这是一个自引用的表。我总是回避任何不符合范式的模型,但在这种情况下,将所有内容放在一个表中更容易显示、管理和调试。

字段包含以下信息……

NodeKey

该字段有两个功能,第一个是主键,第二个是因为它是文本字段,所以它也充当排序键。在大多数情况下,您将使用关联表的主键来创建复合节点键。

您可以看到,在添加了二级节点的情况下,我使用了管道符 (|) 来分隔键。需要在设计阶段对该键进行一些预先考虑,以确保其始终唯一。

如果子节点具有数字键,您可能会发现无法依赖此连接字符串的唯一性,因此您可能需要使用后缀字母,例如 < 字母 O 表示订单或 < 字母 OI 表示订单项。

使用管道符可以将键拆解如下……

Dim strKeys() As String...
strKeys = Split(strKey, "|") 

如果您使用了后缀字符来使数字键唯一,请记住 Val 函数非常有用,可以提取数字,因为它会在遇到非数字字符时停止读取。因此,如果您的部分键是:strKeys(N),值为 1234 OL,那么 Val(strKeys(N)) 将返回 1234。有关详细信息,请参见此处

NodeSelected

这是树形视图中每个项目左侧的复选框。它允许用户选择多个节点,然后采取一些集体行动。

NodeCaption

这是树形视图中可见的主要字段。请注意,它包含指示节点是折叠还是展开的 Unicode 字符 (4,6)。另请注意用于标记每个树级别的缩进。

IsExpanded

指示节点是折叠还是展开。

IsGroup

指示节点有子节点。

ChildrenCount

显示父节点的子节点数量。

窗体

树形视图窗体是一个简单的 Access 窗体,设置为连续窗体模式……

您可以看到节点键文本框字段位于给出用户一些说明的标签后面。右侧是 IsGroup 复选框。IsGroup 和节点键控件在窗体显示时是隐藏的。节点标题和子节点计数文本框具有条件格式设置以进行高亮显示。当窗体加载时,我们使用一套查询和 Unicode 字符来填充零级节点,以显示折叠符号 (4)。

Unicode 符号被声明为 string 并按如下方式实例化……

Private mstrRightArrow As String
Private mstrDownArrow As String…
 
mstrRightArrow = ChrW(&H25BA)
mstrDownArrow = ChrW(&H25BC)

由于这些字符是 Unicode,我们必须使用 ChrW 函数;这阻止我们将它们声明为常量。

一个名为黑色圆圈字符 (=) 的第三方 Unicode 字符用于指示没有子节点的节点。您可以在这里找到这些符号的有趣列表。

展开和折叠节点 - 信号上下文

当用户单击每行的文本区域时,会引发一个鼠标按下事件。此事件的代码是更改树外观的主要开关板。

挑战在于确定用户是想展开或折叠节点,还是仅仅想选择它。

鼠标按下事件识别出单击发生在 X 轴上的哪个位置。这告诉我们子树的哪个级别正在发出事件。一旦我们知道单击发生在 Unicode 符号上,IsGroupIsExpanded 值就提供了下一步的线索……

Private Sub NodeCaption_MouseDown(Button As Integer, _
        Shift As Integer, X As Single, Y As Single)
    Const cstrTitle As String = "NodeCaption_MouseDown"
    Dim strKeys() As String
    
    On Error GoTo ErrHandler
    
    'If there are no child nodes then just exit
    If Me.ChildrenCount = 0 Then GoTo NormalExit
    
    'Determine the node level and make sure that the click is in
    'the target position on the X axis (over the symbol)
    If InStr(Me.NodeKey, "|") Then
        strKeys = Split(Me.NodeKey, "|")
    Else
        ReDim strKeys(0 To 0) As String
    End If
    
    Select Case UBound(strKeys)
        Case 0
            If (X >= 30 And X <= 180) Then
                'Carry on
            Else
                GoTo NormalExit
            End If
        Case 1
            If (X >= 600 And X <= 750) Then
                'Carry on
            Else
                GoTo NormalExit
            End If
        Case 2
            If (X >= 1125 And X <= 1275) Then
                'Carry on
            Else
                GoTo NormalExit
            End If
        Case Else
            GoTo NormalExit
        End Select
        
    If Me.IsGroup Then
        If Me.IsExpanded Then
            CollapseNode Me.NodeKey
        Else
            'Need to signal the state of the checkbox
            'via NodeSelected so child nodes are ticked or not
            ExpandNode Me.NodeKey, Me.NodeSelected
        End If
    End If
NormalExit:
    Exit Sub
    
ErrHandler:
    MsgBox Err.Description & vbCrLf & "Error number: " & _
                  Err.Number, vbExclamation, cstrTitle
    Resume NormalExit
End Sub 

展开节点

Expand Node 子例程使用一套查询来替换 Unicode 符号,然后根据节点的级别添加记录。

首先,我们确定有多少个级别,然后设置相应的查询……

strKeys = Split(strKey, "|")
Select Case UBound(strKeys)
    
    Case 0 'Add level 1 subtree
        Set qd = db.QueryDefs("hqryTreeView02")
        qd.Parameters!prmSpacer = Space$(6)…
    Case N
        Set qd = db.QueryDefs("hqryTreeViewNN")
        qd.Parameters!prmSpacer = Space$(N) 

Space$ 命令用于为每个子树信号群组结构。

折叠节点

当用户单击一个已展开的节点符号时,程序会识别出来,移除子节点,并更改符号。这就是使用良好的主键发挥作用的地方,这样我们就可以删除子节点但保留表中的父节点。

删除查询使用当前记录的 NodeKey 如下……

PARAMETERS prmNodeKey Text ( 255 );
DELETE ztblTreeView01.*
FROM ztblTreeView01
WHERE (((ztblTreeView01.NodeKey) Like [prmNodeKey] & "|*")); 

通过向 WHERE 子句添加管道符,我们告诉查询只删除子节点,因此父节点不会被删除。

最后,在添加记录后,重新查询窗体并将当前记录设置为父节点。

选择节点

通过单击节点,您将在主 Access 屏幕的左下角看到所选行的标题。这由节点标题的单击事件驱动,也是 VBA 代码中采取其他适当操作的地方。

缺点

此设计有两个缺点,第一,当鼠标经过 Unicode 字符时,无法将光标从 I 形状更改为箭头;第二,没有拖放功能。

关注点

当您使用一组具有自然主键的表时,您可能会遇到排序问题,因为在节点键中留有空格。我发现,在这种情况下创建节点需要使用 Replace 函数来消除空格,然后一切都会按正确的排序顺序显示。

可以将此设计开发成一个树形视图表,这在某些应用程序中会很有用。

之前尝试制作此控件

我找不到很多尝试制作原生 Access 树形视图控件的例子,但在 2005 年,以下内容已发布……

  • SmartTree
  • 这是由 Lauren Quantrell 在博客中发布的,并由 Gord Thompson 和 ‘Bri’ 转换为 Access 97 数据库。此解决方案使用命令按钮作为展开和折叠事件的目标区域——非常巧妙。
  • Listview Tree:David Fenton 展示了一个列表视图控件,改编后看起来像树形视图——不错。仅图片请参见此处

历史

  • 2013 年 7 月 9 日,已替换下载的 zip 文件 - 已报告 CRC 问题
© . All rights reserved.