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





5.00/5 (8投票s)
如何使用 Access 表和窗体创建树形视图控件。
引言
Microsoft Access 没有原生的树形视图或列表视图控件——我们必须依赖一个名为 MSCOMCTL.OCX 的 Windows 控件来提供这些服务。有时,该控件会更新,这有时会导致应用程序崩溃。本文旨在解决构建一个“原生”树形视图控件的需求,该控件由 Access 表、窗体和代码构建,以绕过该问题。
背景
上次控件崩溃让我认识到,从现在开始避免该问题的最佳方法是自己编写。顺便说一句,一个很好的解决该问题的方法可以在这里找到。
控件看起来怎么样?
该控件由一个 Access 窗体和一个底层的本地表构建而成……
该控件设计用于允许用户通过单击复选框或使用箭头键来展开、折叠或上下移动焦点来选择单个节点。
右侧的数字表示每个节点所属的子节点——这些是可选的。
它是如何工作的?
在创建了一个 Access 数据库后,我合并了 Northwind Traders 数据库中的一些表。数据模型如下所示……
约定
以z为前缀的表表示它们是本地的,通常其内容是临时的。
以hqry为前缀的查询表示这通常是一个隐藏的查询。使用带有后缀数字的查询名称是我将相关功能按使用顺序分组的方式——在大系统中,很难想出有意义的名称!
节点的级别表示其在 X 轴上的逻辑位置。在上方的树形视图截图中,零级节点是客户名称,一级节点是订单日期,二级节点是订单日期上订购的产品。
底层 Z 表
窗体加载时,会触发 OnOpen
事件,并加载底层表;选择后,它看起来像这样……
这是一个自引用的表。我总是回避任何不符合范式的模型,但在这种情况下,将所有内容放在一个表中更容易显示、管理和调试。
字段包含以下信息……
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 符号上,IsGroup
和 IsExpanded
值就提供了下一步的线索……
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 问题