본 글에서는 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