[예제/완료 파일 포함] MS Access Tree View – Navigation

본 글에서는 MS Access Tree View – Navigation 기능을 구현해 보도록 하겠습니다.

MS Access Tree View – Navigation

실행 예제 파일 다운로드

서브폼을 만들기 위해서 ‘tbl_Reqs’ 테이블을 드래그 앤 드롭하여 트리 뷰가 있는 폼으로 끌어놓습니다. 그러면 ‘하위 폼 마법사’가 자동으로 실행되며, 이름을 ‘frm_Reqs’로 변경한 뒤 ‘마침’ 버튼을 클릭합니다.

다음과 같이 폼이 선택되어 있는 상태에서 오른 클릭을 한 뒤 ‘새 창에서 하위 폼 보기’를 클릭하면 하위폼만 별도로 열 수 있습니다.

하위폼이 열린 상태에서 몇가지 변경을 진행하도록 하겠습니다. 먼저 ID_Type을 기존의 ‘텍스트 상자’에서 ‘콤보 상자’로 형태를 바꿔주도록 하겠습니다. ID_Type 텍스트 상자가 선택된 상태에서 오른클릭을 한 뒤 ‘변경’ -> ‘콤보 상자’를 순차적으로 클릭하여 양식을 바꿔줍니다.

ID_Type의 ‘행 원본’ 속성값을 아래의 값으로 입력해줍니다.

SELECT * FROM tbl_Type

‘형식’ 탭에서 ‘열 개수’는 2개로 ‘열 너비는 ‘0;3’을 입력합니다. tbl_Type 테이블이 2개의 컬럼으로 되어 있기 때문에 2개 컬럼값을 설정하는 것이며, 그 중 첫번째 ID에 해당하는 열을 숨기기 위해서 첫번째 열의 너비를 0으로 설정하였습니다.

PK_Req값은 변경되면 안되는 값이기 때문에 ‘데이터’ 탭에서 ‘잠금’ 속성을 예로 변경하였습니다.

폼 전체를 선택한 뒤 속성에서 ‘모두’ 탭에서 ‘기본 보기’ 옵션을 ‘단일 폼’으로 변경합니다.

기존에 작성한 코드를 다음과 같이 업데이트 합니다. 중간에 Node를 add 할 때 처음 프로그래밍 시에는 Key값을 반환하지 않았는데 Key를 반환하도록 변경하였습니다. 기존의 코드를 다음의 코드를 엎어쓰기 하면 됩니다.

Option Compare Database

Public Sub loadTreeView()
    
    Dim tv As MSComctlLib.TreeView
    Set tv = Forms("frm_treeView").treeReqs.Object
    
    ' Clear all node of treeView
    tv.Nodes.Clear
    
    
    Dim rsReqs As DAO.Recordset
    Set rsReqs = CurrentDb.OpenRecordset("SELECT * FROM tbl_Reqs ORDER BY ID_Parent, dbl_Sort", dbOpenDynaset)
    
    
    Dim strFind As String
    
    strFind = "ID_Type = 1"
    rsReqs.FindFirst strFind
    
    Dim nodX As MSComctlLib.Node
    Dim strBook As String
        
    
    Do While Not rsReqs.NoMatch
        
        Set nodX = tv.Nodes.Add(, , genReqKey(rsReqs!PK_Req), rsReqs!mem_Req)
        nodX.Bold = True
        strBook = rsReqs.Bookmark
        
        addChildren tv, nodX, rsReqs, rsReqs!PK_Req
        rsReqs.Bookmark = strBook
        
        rsReqs.FindNext strFind
    
    Loop
    
    
End Sub


Private Sub addChildren(tv As TreeView, nodParent As Node, rsReqs As DAO.Recordset, lngParentId As Long)

    Dim strFind As String
    strFind = "ID_Parent =" & lngParentId
    rsReqs.FindFirst strFind
    
    Dim nodX As Node
    Dim strBook As String
    
    Do While Not rsReqs.NoMatch
    
        Set nodX = tv.Nodes.Add(nodParent, tvwChild, genReqKey(rsReqs!PK_Req), rsReqs!mem_Req)
        
        Select Case rsReqs!ID_Type
            Case 1 'Document
            
            Case 2 'Header
                nodX.Bold = True
                
            Case 3 'Requirement
            
            Case 4 ' Guide
                nodX.ForeColor = vbBlue
                
        End Select
        
        strBook = rsReqs.Bookmark
        
        addChildren tv, nodX, rsReqs, rsReqs!PK_Req
        
        rsReqs.Bookmark = strBook
    
        rsReqs.FindNext strFind
    Loop

End Sub


Private Function genReqKey(lngReqID As Long) As String
   genReqKey = "<Type>Req</Type><ID>" & lngReqID & "</ID>"
   Debug.Print genReqKey
End Function

왼편에 프로젝트 탐색기에서 모듈을 추가한 뒤 이름을 ‘mod_Tools’로 변경합니다. 변경한 뒤에는 아래의 코드를 복사 붙여넣기 합니다.

Option Compare Database
Option Explicit

Public Const g_DebugMode As Boolean = True
Public Const g_ProjectName As String = "Treeview Demo"
Public Function getID(nodX As MSComctlLib.Node) As Long
On Error GoTo err_Handler

    
    getID = getXML(nodX.Key, "ID")
    
exit_Function:
    Exit Function
   
err_Handler:
    doStdErrMsg ("getID Function")
    GoTo exit_Function


End Function

Public Function getType(nodX As MSComctlLib.Node) As String
On Error GoTo err_Handler

    getType = getXML(nodX.Key, "Type")
    
exit_Function:
    Exit Function

err_Handler:
    doStdErrMsg ("GetType")
    GoTo exit_Function


End Function

Public Function getXML(strxml As String, strElement As String) As String
On Error GoTo errHandler
    If InStr(1, strxml, "<" & strElement & ">", vbTextCompare) > 0 And InStr(1, strxml, "</" & strElement & ">", vbTextCompare) > 0 Then
        'Found it
        Dim intLeft As Integer
        Dim intRight As Integer
        intLeft = InStr(1, strxml, "<" & strElement & ">", vbTextCompare) + Len(strElement) + 2
        intRight = InStr(1, strxml, "</" & strElement & ">", vbTextCompare)
        getXML = Mid(strxml, intLeft, intRight - intLeft)
        Else
        
        GoTo badXML
    End If
    
Exit Function

badXML:
    MsgBox "An error occured while trying to retrieve XML information,probably due to bad tags", vbOKOnly + vbCritical, "Error reading Node information"
    Exit Function
    
errHandler:
    MsgBox "An error occured while trying to retrieve XML information from a node" & vbOKOnly + vbCritical, "Error reading Node information"
    Err.Clear
    Exit Function
End Function


Public Sub ExpandArray(ByRef InputArray As Variant, Optional lngExpansion As Long = 50)
    Dim lngSize As Long
    lngSize = lngExpansion + UBound(InputArray)
    ReDim Preserve InputArray(lngSize)
End Sub

Public Sub StoreExpanded(tv As TreeView, ByRef strKeyArray() As String)
    If Not g_DebugMode Then On Error GoTo err_Handler
    Dim nodX As Node
    
    ReDim strKeyArray(50) 'Notice the no preserve, so array is cleared between each use
    Dim intI As Integer
    intI = 1
    For Each nodX In tv.Nodes
        If nodX.Expanded Then
            strKeyArray(intI) = nodX.Key
            intI = intI + 1
        End If
    Next
    
    
    
exit_Sub:
    Set nodX = Nothing
    Exit Sub
    
err_Handler:
    If Err.Number = 9 Then
        ExpandArray strKeyArray
        Resume
    Else
        doStdErrMsg "Document Treeview (Remember expanded nodes)"
        Resume exit_Sub
    End If
End Sub
Public Sub RestoreExpanded(tv As TreeView, strKeyArray() As String)
    
    'Loop through
        Dim intI As Integer
        
        On Error Resume Next 'Disable error handling, nodes could have been deleted
        
        For intI = 1 To UBound(strKeyArray)
            tv.Nodes(strKeyArray(intI)).Expanded = True
        Next
    
exit_Sub:
    Exit Sub
End Sub


Public Sub doStdErrMsg(Optional strProcName As String)
'Small function to put in each error handler for unexpected errors

    'Compose message string
        Dim strMsg As String
        strMsg = "An unexpexted error [" & Err.Number & "] has occured."
    
    'If proc name was supplied, add it to string
        If strProcName & "" <> "" Then
            strMsg = strMsg & vbNewLine & "In procedure '" & strProcName & "'"
        End If
    
    'Add error description
        strMsg = strMsg & vbNewLine & vbNewLine & Err.Description
    
    'Post it
        MsgBox strMsg, vbOKOnly + vbExclamation, g_ProjectName & ": Unexpected Error"
        'logError strMsg
End Sub

전체 폼 뷰로 돌아와 서브 폼을 선택한 상태에서 서브폼의 이름을 기존의 frm_Reqs에서 ctrlSubForm으로 변경해 줍니다.

프로젝트 탐색기에서 폼을 더블클릭하여 코드 입력창을 띄운 뒤 ‘treeReqs’ 가 선택된 상태에서 오른편 목록중에서 ‘NodeClick’을 찾아 클릭합니다. 그럼 자동으로 코드가 생성되어 노드를 클릭하는 이벤트가 발생할 때 동작을 입력할 수 있는 기본 코드가 작성됩니다.

Private Sub treeReqs_NodeClick(ByVal Node As Object)

    Me.ctrlSubForm.Form.RecordSource = "SELECT * FROM tbl_Reqs WHERE PK_Req=" & getID(Node)
    
End Sub

위와 같이 코드 내용을 입력해 줍니다.

트리뷰 업데이트

폼에서 데이터가 수정될 때 트리뷰가 자동으로 업데이트 되도록 설정하겠습니다.

서브 폼을 선택한 뒤 ‘이벤트’에 ‘After Update’ 항목에 코드 편집기를 엽니다.

여기에서 폼이 변경되었을 때 자동으로 트리 뷰가 새롭게 그려지고 이전에 선택했던 Node를 지속적으로 선택하고 있을 수 있도록 아래의 코드를 추가해 줍니다.

Private Sub Form_AfterUpdate()

    loadTreeView
    
    Dim tv As MSComctlLib.TreeView
    
    'Set tv = Me.Parent.treeReqs.Object
    'tv.Nodes(genReqKey(Me!PK_Req)).Selected = True
    'Set tv = Nothing
    
    'Set tv = getTV
    
    getTV.Nodes(genReqKey(Me!PK_Req)).Selected = True

End Sub

완성파일 다운로드

Categories VBA