通过 vba 访问更改导航窗格组

Posted

技术标签:

【中文标题】通过 vba 访问更改导航窗格组【英文标题】:Change Navigation pane group in access through vba 【发布时间】:2014-12-08 20:10:36 【问题描述】:

我有一个 VBA 代码模块在访问中创建 4 个新表并将它们添加到数据库中。我想在最后添加一个部分,通过自定义组在导航窗格中组织它们,以便它们都被组织起来。这可以通过 vba 实现吗?

编辑:

我不希望这些表位于未分配对象组中。我想通过 VBA 更改该组的名称。

【问题讨论】:

“在导航窗格中组织”?你要什么? 所以我有 4 个从宏创建的表,我希望将它们放在导航窗格中的新自定义组中。查看编辑。 我仍在寻找 VBA 解决方案,但根据我的阅读,您无法重命名“未分配”组 - 您所能做的就是隐藏它。您想要的是移动项目(通过 VBA),但我所看到的只是如何手动执行此操作。我会继续寻找...... 好的,谢谢。我找到了这个:social.msdn.microsoft.com/Forums/en-US/…,但我不确定它的含义或如何使用它。 我想我已经找到了解决方案,很快就会发布。您是否已经定义了组? 【参考方案1】:

编辑:添加了更多代码以将其他对象类型添加到自定义导航组。

以下代码会将表格分配给您的自定义导航组。

警告!!我仍在尝试解决表“MSysNavPaneObjectIDs”的“刷新”问题。如果您创建一个新表,然后尝试添加到您的组 - 有时它在第一次尝试时可以工作,有时它会失败但会在延迟后工作(有时长达五或十分钟!)

此时,我通过读取表“MSysObjects”中的信息,然后将新记录添加到“MSysNavPaneObjectIDs”中解决了这个问题(当它失败时)。

下面的代码只是创建了五个小表并添加到 Nav Group 'Clients'

修改代码以使用您的组名/表名。

Option Compare Database
Option Explicit

Sub Test_My_Code()
Dim dbs         As DAO.Database
Dim strResult   As String
Dim i           As Integer
Dim strSQL      As String
Dim strTableName    As String

Set dbs = CurrentDb
For i = 1 To 5
    strTableName = "Query" & i
'>>> CHANGE FOLLOWING LINE TO YOUR CUSTOM NAME
    ' Pass the Nav Group, Object Name, Object Type
    strResult = SetNavGroup("Clients", strTableName, "Query")
    Debug.Print strResult
Next i

For i = 1 To 5
    strTableName = "0000" & i
    strSQL = "CREATE TABLE " & strTableName & " (PayEmpID INT, PayDate Date);"
    dbs.Execute strSQL
'>>> CHANGE FOLLOWING LINE TO YOUR CUSTOM NAME
    ' Pass the Nav Group, Object Name, Object Type
    strResult = SetNavGroup("Clients", strTableName, "Table")
    Debug.Print strResult
Next i
dbs.Close
Set dbs = Nothing
End Sub

Function SetNavGroup(strGroup As String, strTable As String, strType As String) As String
Dim strSQL          As String
Dim dbs             As DAO.Database
Dim rs              As DAO.recordSet
Dim lCatID          As Long
Dim lGrpID          As Long
Dim lObjID          As Long
Dim lType           As Long

    SetNavGroup = "Failed"
    Set dbs = CurrentDb

' Ignore the following code unless you want to manage 'Categories'
    ' Table MSysNavPaneGroupCategories has fields: Filter, Flags, Id (AutoNumber), Name, Position, SelectedObjectID, Type
'    strSQL = "SELECT Id, Name, Position, Type " & _
'            "FROM MSysNavPaneGroupCategories " & _
'            "WHERE (((MSysNavPaneGroupCategories.Name)='" & strGroup & "'));"
'    Set rs = dbs.OpenRecordset(strSQL)
'    If rs.EOF Then
'        MsgBox "No group named '" & strGroup & "' found. Will quit now.", vbOKOnly, "No Group Found"
'        rs.Close
'        Set rs = Nothing
'        dbs.Close
'        Set dbs = Nothing
'        Exit Function
'    End If
'    lCatID = rs!ID
'    rs.Close

    ' When you create a new table, it's name is added to table 'MSysNavPaneObjectIDs'

    ' Types
        ' Type TypeDesc
        '-32768  Form
        '-32766  Macro
        '-32764  Reports
        '-32761  Module
        '-32758  Users
        '-32757  Database Document
        '-32756  Data Access Pages
        '1   Table - Local Access Tables
        '2   Access object - Database
        '3   Access object - Containers
        '4   Table - Linked ODBC Tables
        '5   Queries
        '6   Table - Linked Access Tables
        '8   SubDataSheets
    If LCase(strType) = "table" Then
        lType = 1
    ElseIf LCase(strType) = "query" Then
        lType = 5
    ElseIf LCase(strType) = "form" Then
        lType = -32768
    ElseIf LCase(strType) = "report" Then
        lType = -32764
    ElseIf LCase(strType) = "module" Then
        lType = -32761
    ElseIf LCase(strType) = "macro" Then
        lType = -32766
    Else
        MsgBox "Add your own code to handle the object type of '" & strType & "'", vbOKOnly, "Add Code"
        dbs.Close
        Set dbs = Nothing
        Exit Function
    End If

    ' Table MSysNavPaneGroups has fields: Flags, GroupCategoryID, Id, Name, Object, Type, Group, ObjectID, Position
    Debug.Print "---------------------------------------"
    Debug.Print "Add '" & strType & "' " & strTable & "' to Group '" & strGroup & "'"
    strSQL = "SELECT GroupCategoryID, Id, Name " & _
            "FROM MSysNavPaneGroups " & _
            "WHERE (((MSysNavPaneGroups.Name)='" & strGroup & "') AND ((MSysNavPaneGroups.Name) Not Like 'Unassigned*'));"
    Set rs = dbs.OpenRecordset(strSQL)
    If rs.EOF Then
        MsgBox "No group named '" & strGroup & "' found. Will quit now.", vbOKOnly, "No Group Found"
        rs.Close
        Set rs = Nothing
        dbs.Close
        Set dbs = Nothing
        Exit Function
    End If
    Debug.Print rs!GroupCategoryID & vbTab & rs!ID & vbTab & rs!Name
    lGrpID = rs!ID
    rs.Close

Try_Again:
    ' Filter By Type
    strSQL = "SELECT Id, Name, Type " & _
            "FROM MSysNavPaneObjectIDs " & _
            "WHERE (((MSysNavPaneObjectIDs.Name)='" & strTable & "') AND ((MSysNavPaneObjectIDs.Type)=" & lType & "));"
    Set rs = dbs.OpenRecordset(strSQL)
    If rs.EOF Then
        ' Seems to be a refresh issue / delay!  I have found no way to force a refresh.
        ' This table gets rebuilt at the whim of Access, so let's try a different approach....
        ' Lets add the record vis code.
        Debug.Print "Table not found in MSysNavPaneObjectIDs, try MSysObjects."
         strSQL = "SELECT * " & _
            "FROM MSysObjects " & _
            "WHERE (((MSysObjects.Name)='" & strTable & "') AND ((MSysObjects.Type)=" & lType & "));"
        Set rs = dbs.OpenRecordset(strSQL)
        If rs.EOF Then
            MsgBox "This is crazy! Table '" & strTable & "' not found in MSysObjects.", vbOKOnly, "No Table Found"
            rs.Close
            Set rs = Nothing
            dbs.Close
            Set dbs = Nothing
            Exit Function
        Else
            Debug.Print "Table not found in MSysNavPaneObjectIDs, but was found in MSysObjects. Lets try to add via code."
            strSQL = "INSERT INTO MSysNavPaneObjectIDs ( ID, Name, Type ) VALUES ( " & rs!ID & ", '" & strTable & "', " & lType & ")"
            dbs.Execute strSQL
            GoTo Try_Again
        End If
    End If
    Debug.Print rs!ID & vbTab & rs!Name & vbTab & rs!type
    lObjID = rs!ID
    rs.Close

    ' Add the table to the Custom group
    strSQL = "INSERT INTO MSysNavPaneGroupToObjects ( GroupID, ObjectID, Name ) VALUES ( " & lGrpID & ", " & lObjID & ", '" & strTable & "' )"
    dbs.Execute strSQL

    dbs.Close
    Set dbs = Nothing
    SetNavGroup = "Passed"

End Function

【讨论】:

这很完美!但是我该如何修改它以包含查询呢? 我更新了答案以支持其他对象类型。查看修改后的代码。【参考方案2】:

非常感谢您的代码, 由于刷新表格的问题,我不得不根据我的具体情况对其进行一些修改。 事实上,我正在重新创建一个表(之前删除旧表)。由于 MSysNavPaneObjectIDs 不刷新,旧 ID 保留在里面。

例如让我们使用我想放入组 TEMP 的表 tmpFoo。

tmpFoo 已经在组 TEMP 中。 TEMP 的 ID 为 1,tmpFoo 的 ID 为 1000 然后我删除 tmpFoo,并立即重新创建 tmpFoo。 tmpFoo 现在位于“未分配对象”中。

在 MSysObjects 中,tmpFoo 的 ID 现在是 1100,但是在 MSysNavPaneObjectIDs 中表格没有刷新,这里的 tmpFoo 的 ID 仍然是 1000。

在这种情况下,在表 MSysNavPaneGroupToObjects 中创建了 TEMP(1) 和 tmpFoo(1000) 之间的链接 => 由于 ID 1000 不再存在于 MSysObjects 中,因此没有任何反应。

所以,下面的修改代码在所有情况下都从 MSysObjects 获取 ID,然后检查该 ID 是否存在于 MSysNavPaneObjectIDs 中。

如果没有,添加该行,然后使用相同的 ID 将其添加到 MSysNavPaneGroupToObjects。

这样看来我没有任何刷新问题(在上层函数中添加Application.RefreshDatabaseWindow)。 再次感谢韦恩,

Function SetNavGroup(strGroup As String, strTable As String, strType As String) As String
Dim strSQL          As String
Dim dbs             As DAO.Database
Dim rs              As DAO.Recordset
Dim lCatID          As Long
Dim lGrpID          As Long
Dim lObjID          As Long
Dim lType           As Long

    SetNavGroup = "Failed"
    Set dbs = CurrentDb

    ' When you create a new table, it's name is added to table 'MSysNavPaneObjectIDs'

    ' Types
        ' Type TypeDesc
        '-32768  Form
        '-32766  Macro
        '-32764  Reports
        '-32761  Module
        '-32758  Users
        '-32757  Database Document
        '-32756  Data Access Pages
        '1   Table - Local Access Tables
        '2   Access object - Database
        '3   Access object - Containers
        '4   Table - Linked ODBC Tables
        '5   Queries
        '6   Table - Linked Access Tables
        '8   SubDataSheets
    If LCase(strType) = "table" Then
        lType = 1
    ElseIf LCase(strType) = "query" Then
        lType = 5
    ElseIf LCase(strType) = "form" Then
        lType = -32768
    ElseIf LCase(strType) = "report" Then
        lType = -32764
    ElseIf LCase(strType) = "module" Then
        lType = -32761
    ElseIf LCase(strType) = "macro" Then
        lType = -32766
    Else
        MsgBox "Add your own code to handle the object type of '" & strType & "'", vbOKOnly, "Add Code"
        dbs.Close
        Set dbs = Nothing
        Exit Function
    End If

    ' Table MSysNavPaneGroups has fields: Flags, GroupCategoryID, Id, Name, Object, Type, Group, ObjectID, Position
    Debug.Print "---------------------------------------"
    Debug.Print "Add '" & strType & "' '" & strTable & "' to Group '" & strGroup & "'"
    strSQL = "SELECT GroupCategoryID, Id, Name " & _
            "FROM MSysNavPaneGroups " & _
            "WHERE (((MSysNavPaneGroups.Name)='" & strGroup & "') AND ((MSysNavPaneGroups.Name) Not Like 'Unassigned*'));"
    Set rs = dbs.OpenRecordset(strSQL)
    If rs.EOF Then
        MsgBox "No group named '" & strGroup & "' found. Will quit now.", vbOKOnly, "No Group Found"
        rs.Close
        Set rs = Nothing
        dbs.Close
        Set dbs = Nothing
        Exit Function
    End If
    Debug.Print rs!GroupCategoryID & vbTab & rs!ID & vbTab & rs!Name
    lGrpID = rs!ID
    rs.Close

    ' Get Table ID From MSysObjects
    strSQL = "SELECT * " & _
        "FROM MSysObjects " & _
        "WHERE (((MSysObjects.Name)='" & strTable & "') AND ((MSysObjects.Type)=" & lType & "));"
    Set rs = dbs.OpenRecordset(strSQL)
    If rs.EOF Then
        MsgBox "This is crazy! Table '" & strTable & "' not found in MSysObjects.", vbOKOnly, "No Table Found"
        rs.Close
        Set rs = Nothing
        dbs.Close
        Set dbs = Nothing
        Exit Function
    End If

    lObjID = rs!ID

    Debug.Print "Table found in MSysObjects " & lObjID & " . Lets compare to MSysNavPaneObjectIDs."

   ' Filter By Type
    strSQL = "SELECT Id, Name, Type " & _
            "FROM MSysNavPaneObjectIDs " & _
            "WHERE (((MSysNavPaneObjectIDs.ID)=" & lObjID & ") AND ((MSysNavPaneObjectIDs.Type)=" & lType & "));"
    Set rs = dbs.OpenRecordset(strSQL)
    If rs.EOF Then
        ' Seems to be a refresh issue / delay!  I have found no way to force a refresh.
        ' This table gets rebuilt at the whim of Access, so let's try a different approach....
        ' Lets add the record via this code.
        Debug.Print "Table not found in MSysNavPaneObjectIDs, add it from MSysObjects."
        strSQL = "INSERT INTO MSysNavPaneObjectIDs ( ID, Name, Type ) VALUES ( " & lObjID & ", '" & strTable & "', " & lType & ")"
        dbs.Execute strSQL
    End If
    Debug.Print lObjID & vbTab & strTable & vbTab & lType
    rs.Close

    ' Add the table to the Custom group
    strSQL = "INSERT INTO MSysNavPaneGroupToObjects ( GroupID, ObjectID, Name ) VALUES ( " & lGrpID & ", " & lObjID & ", '" & strTable & "' )"
    dbs.Execute strSQL

    dbs.Close
    Set dbs = Nothing
    SetNavGroup = "Passed"
End Function

【讨论】:

谢谢,dbs.Execute "INSERT INTO MSysNavPaneObjectIDs ( Id, Name, Type ) SELECT MSysObjects.Id, MSysObjects.Name, MSysObjects.Type FROM (MSysNavPaneGroupToObjects INNER JOIN MSysObjects ON MSysNavPaneGroupToObjects.ObjectID = MSysObjects.Id) LEFT JOIN MSysNavPaneObjectIDs ON MSysObjects.Id = MSysNavPaneObjectIDs.Id WHERE MSysNavPaneObjectIDs.Id Is Null;" 哎呀,让SELECT DISTINCT【参考方案3】:

这是我的代码,它不像主代码那样对用户错误友好,但进行大规模移动应该更快一些。

Public Sub Test_My_Code()
    Dim i As Long, db As Database, qd As QueryDef

    Set db = CurrentDb
    For i = 1 To 10
        DoCmd.RunSQL "CREATE TABLE [~~Table:" & Format(i, "00000") & "](PayEmpID INT, PayDate Date)"
        Set qd = db.CreateQueryDef("~~Query:" & Format(i, "00000"), "SELECT * FROM [~~Table:" & Format(i, "00000") & "];")
    Next i
    MsgBox IIf(SetNavGroup(CategorySelection:="Like '*'", GroupSelection:="='TestGroup'", ObjectSelection:="Like '~~Table:#####'"), "New Tables Moved", "Table Move Failed")
    MsgBox IIf(SetNavGroup(CategorySelection:="Like '*'", GroupSelection:="='TestGroup'", ObjectSelection:="Like '~~Query:#####'"), "New Queries Moved", "Query Move Failed")
End Sub

Private Sub SetNavGroup_tst(): MsgBox IIf(SetNavGroup(GroupSelection:="='Verified Formularies'", ObjectSelection:="Like '*Verified*'"), "Tables Moved OK", "Failed"): End Sub
'Parameters:
'  CategorySelection   --  used to filter which custom(type=4) categories to modify
'       ex select the 'Custom' Navigation Category (default): "='Custom'"
'  GroupSelection      --  used to filter which custom(type=-1) groups to add the objects to
'       ex select a specific group: "='Verified Formularies'"
'       ex select set of specific groups: "In ('Group Name1','Group Name2')"
'  ObjectSelection     --  used to filter which database objects to move under the groups
'       ex select a range of tables: "Like '*Verified*'"
'  UnassignedOnly      --  used to only look at objects from the Unassigned group
'       True  - set only unassigned objects
'       False - add objects even if they're already in a group
Public Function SetNavGroup(GroupSelection As String, ObjectSelection As String, Optional CategorySelection As String = "='Custom'", Optional UnassignedOnly As Boolean = True) As Boolean
    SetNavGroup = False
    If Trim(GroupSelection) = "" Then Exit Function
    If Trim(ObjectSelection) = "" Then Exit Function
    DoCmd.SetWarnings False
    On Error GoTo SilentlyContinue

    'TempTable Name
    Dim ToMove As String
    Randomize: ToMove = "~~ToMove_TMP" & (Fix(100000 * Rnd) Mod 100)

    'Build temporary table of what to move
    Dim SQL As String: SQL = _
        "SELECT [Ghost:ToMove].* INTO [" & ToMove & "] " & _
        "FROM ( " & _
            "SELECT MSysNavPaneGroups.GroupCategoryID, MSysNavPaneGroupCategories.Name AS CategoryName, MSysNavPaneGroups.Id AS GroupID, MSysNavPaneGroups.Name AS GroupName, MSysObjects.Id AS ObjectID, MSysObjects.Name AS ObjectName, MSysObjects.Type AS ObjectType, '' AS ObjectAlias " & _
            "FROM MSysObjects, MSysNavPaneGroupCategories INNER JOIN MSysNavPaneGroups ON MSysNavPaneGroupCategories.Id = MSysNavPaneGroups.GroupCategoryID " & _
            "WHERE (((MSysNavPaneGroupCategories.Name) " & CategorySelection & ") AND ((MSysNavPaneGroups.Name) " & GroupSelection & ") AND MSysObjects.Name " & ObjectSelection & " AND ((MSysNavPaneGroupCategories.Type)=4) AND ((MSysNavPaneGroups.[Object Type Group])=-1)) " & _
            "GROUP BY MSysNavPaneGroups.GroupCategoryID, MSysNavPaneGroupCategories.Name, MSysNavPaneGroups.Id, MSysNavPaneGroups.Name, MSysObjects.Id, MSysObjects.Name, MSysObjects.Type " & _
            "ORDER BY Min(MSysNavPaneGroupCategories.Position), Min(MSysNavPaneGroups.Position)" & _
        ") AS [Ghost:ToMove] LEFT JOIN ( " & _
            "SELECT MSysNavPaneGroups.GroupCategoryID, MSysNavPaneGroupToObjects.GroupID, MSysNavPaneGroupToObjects.ObjectID " & _
            "FROM MSysNavPaneGroups INNER JOIN MSysNavPaneGroupToObjects ON MSysNavPaneGroups.Id = MSysNavPaneGroupToObjects.GroupID " & _
        ") AS [Ghost:AssignedObjects] ON ([Ghost:ToMove].ObjectID = [Ghost:AssignedObjects].ObjectID) AND ([Ghost:ToMove].GroupID = [Ghost:AssignedObjects].GroupID) AND ([Ghost:ToMove].GroupCategoryID = [Ghost:AssignedObjects].GroupCategoryID) " & _
        "WHERE [Ghost:AssignedObjects].GroupCategoryID Is Null;"
    If Not UnassignedOnly Then SQL = _
        "SELECT MSysNavPaneGroups.GroupCategoryID, MSysNavPaneGroupCategories.Name AS CategoryName, MSysNavPaneGroups.Id AS GroupID, MSysNavPaneGroups.Name AS GroupName, MSysObjects.Id AS ObjectID, MSysObjects.Name AS ObjectName, MSysObjects.Type AS ObjectType, '' AS ObjectAlias " & _
        "INTO [" & ToMove & "] " & _
        "FROM MSysObjects, MSysNavPaneGroupCategories INNER JOIN MSysNavPaneGroups ON MSysNavPaneGroupCategories.Id = MSysNavPaneGroups.GroupCategoryID " & _
        "WHERE (((MSysNavPaneGroupCategories.Name) " & CategorySelection & ") AND ((MSysNavPaneGroups.Name) " & GroupSelection & ") AND MSysObjects.Name " & ObjectSelection & " AND ((MSysNavPaneGroupCategories.Type)=4) AND ((MSysNavPaneGroups.[Object Type Group])=-1)) " & _
        "GROUP BY MSysNavPaneGroups.GroupCategoryID, MSysNavPaneGroupCategories.Name, MSysNavPaneGroups.Id, MSysNavPaneGroups.Name, MSysObjects.Id, MSysObjects.Name, MSysObjects.Type " & _
        "ORDER BY Min(MSysNavPaneGroupCategories.Position), Min(MSysNavPaneGroups.Position);"
    DoCmd.RunSQL SQL

    If DCount("*", "[" & ToMove & "]") = 0 Then Err.Raise 63 'Nothing to move

    'Add the objects to their groups
    DoCmd.RunSQL _
        "INSERT INTO MSysNavPaneGroupToObjects ( GroupID, Name, ObjectID ) " & _
        "SELECT TM.GroupID, TM.ObjectAlias, TM.ObjectID  " & _
        "FROM [" & ToMove & "] AS TM LEFT JOIN MSysNavPaneGroupToObjects ON (TM.ObjectID = MSysNavPaneGroupToObjects.ObjectID) AND (TM.GroupID = MSysNavPaneGroupToObjects.GroupID)  " & _
        "WHERE MSysNavPaneGroupToObjects.GroupID Is Null;"

    'Add any missing NavPaneObjectIDs
    DoCmd.RunSQL _
        "INSERT INTO MSysNavPaneObjectIDs ( Id, Name, Type ) " & _
        "SELECT DISTINCT TM.ObjectID, TM.ObjectName, TM.ObjectType " & _
        "FROM [" & ToMove & "] AS TM LEFT JOIN MSysNavPaneObjectIDs ON TM.ObjectID = MSysNavPaneObjectIDs.Id " & _
        "WHERE (((MSysNavPaneObjectIDs.Id) Is Null));"

    SetNavGroup = True
EOFn:
    On Error Resume Next
    DoCmd.DeleteObject acTable, ToMove
    On Error GoTo 0
    DoCmd.SetWarnings True
    Exit Function
SilentlyContinue: Resume EOFn
End Function

【讨论】:

以上是关于通过 vba 访问更改导航窗格组的主要内容,如果未能解决你的问题,请参考以下文章

如何在VBA中确定选择上方的标题-X样式标题?

关闭即时窗口 - VBA

如何快速自定义Win10文件资源管理器界面

更改背景图像引导导航药丸

Excel VBA通​​过单击形状更改形状的背景图像

没有选择的excel vba冻结窗格