PROGRAMMING WORKSHOP

화일관리도구 |검색기능

화일을 많이 저장하다 보면 검색을 하고 싶어질 것이다
그래서 검색기능을 달아 달라고 하신다
그림과 같이 버튼을 하나 달고



폼이 로딩될때 화일테이블에 정보가 있는지 없는지 확인하여
있으면 버튼을 활성화해주고, 등록된 화일이 하나도 없다면 비활성화시키는 것은
메너 좋은 소루션

소루션을 만들면서 콘트롤을 계속 추가시키는 것은 항상 있는 일
콘트롤을 추가하면 자동으로 버튼의 캡션명을 상수화시켜주는 습관을 갖고

Public Const FILE_SEARCH_CAPTION = "화일검색"

폼이 로딩되면서 발생하는 Initiaize 이벤트에서 SetControls 프로시져를 호출하고
SetControls 프로시져내에 아래를 추가한다

Me.cmdSearch.Caption = modMain.FILE_SEARCH_CAPTION
Dim rFiletable As Range
Set rFiletable = modMain.getTable(modMain.FILE_TABLE_START)
If rFiletable.Rows.Count = 1 Then
    Me.cmdSearch.Enabled = False
Else
    Me.cmdSearch.Enabled = True
End If
End Sub

getTable 과 같은 함수를 이미 만들어서 사용하고 있으니
그냥 편리하게 다시 사용하면 된다
테이블의 행의 갯수가 하나밖에 없다면 테이블의 열머리만 있는 것
그렇지 않으면 화일정보가 있다는 것
이것을 읽어서 버튼의 Enabled 속성을 처리한다

버튼을 크릭하면 검색창이 하나 나타나게 만들자
콘트롤이나 폼은 필요할때 마다 만들어서 사용하는 것이니까..
망설이지 말고 만들어서 사용하면 된다
아래의 그림과 같이 텍스트상자+라벨+버튼 이렇게 3개의 콘트롤이면
충분할 것이다



이때 중요한 속성설정이 있다
이 자식폼(검색창)을 열고 작업후 닫으면 이것을 호출했던 부모폼도
같이 닫혀 버리게 되는 수가 있다
속성설정을 잘못하면 그렇게 된다
이 창을 불렀던 부모폼의 ShowModal속성은 True로 하고
검색창의 ShowModal속성도 True로 한다

사용자가 텍스트상자에 찾고 싶은 검색어를 입력하고
엔터키를 치면 찾아서 찾은 화일 목록을
부모폼의 FileList목록상자에 채워주면 검색임무끝이다
검색대상은 아래의 그림과 같이 화일목록을 담아 놓은 범위를 찾아야 할 것이다



이런 연속된 프로젝트 작업을 하면서 여러분들이 실감하게 되는 것은
한번 만들어 놓은 변수나 프로시져나 함수들을 다시 활용하여야 하겠다는
생각이 들면 프로그래밍워크샵의 목적에 맞는 것이 될 것이다
화일테이블찾는 것은 이미 만들어져 있는 것을 활용하면 되고
화일명이 몇번째 열에 있는지 상수를 활용하면 되는 것이고
이전에 활용하였던 경로를 선택하면 해당 경로상의 화일을 채우는
프로시져의 일부를 활용하면 되는 것이다

아래와 같이 텍스트박스에 검색하고자 하는 문자를 입력하고
검색버튼을 크릭하면 아래의 이벤트프로시져가 실행되게 하면 되겠다

Private Sub cmdSearch_Click()
Dim sSearch As String
Dim rTable As Range, rRow As Range
Dim sFileName As String
' 콘트롤을 담을 매개변수의 타입은 반드시 MSForms을 붙이고 콘트롤명을 주어야 한다
Dim lstFile As MSForms.ListBox
sSearch = UCase(Me.txtSearch.Text)
If sSearch = "" Then MsgBox "검색어 입력하세요": Exit Sub
'검색할 대상테이블, 이전에 사용하는 함수를 그대로 사용함 되고..
Set rTable = modMain.getTable(modMain.FILE_TABLE_START)
Set rTable = rTable.Offset(1).Resize(rTable.Rows.Count - 1)
' 목록상자 초기화 
Set lstFile = frmFileManager.lstFile
lstFile.Clear
lstFile.ColumnCount = 4
lstFile.ColumnWidths = ";0;0;0"
lstFile.Enabled = False
'검색 대상테이블을 순환하면서
이때 엑셀개체의 FIND속성을 사용하여도 되지만 아래와 같이 문자열검색을
사용해되 괞찮다
For Each rRow In rTable.Rows
    sFileName = UCase(Split(rRow.Cells(modMain.TBL_FILE_FILENAME_COL).Value, ".")(0))
 ' Like "*" & sSearch & "*" 로 화일명에 검색하려는 문자나 단어가 있으면 모두 찾도록하고    
    If sFileName Like "*" & sSearch & "*" Then
 '찾았으면 목록상자에 하나씩 추가하고	 
        lstFile.AddItem rRow.Cells(modMain.TBL_FILE_FILENAME_COL)
        lstFile.List(lstFile.ListCount - 1, 1) = rRow.Cells(modMain.TBL_FILE_FILEPATH_COL)
        lstFile.List(lstFile.ListCount - 1, 2) = rRow.Cells(modMain.TBL_FILE_CATEGORY_ID_COL)
        lstFile.List(lstFile.ListCount - 1, 3) = rRow.Row
    End If
Next
 '검색한 내용이 하나라도 있으면 목록상자 활성화
If lstFile.ListCount > 0 Then lstFile.Enabled = True
Unload Me
End Sub

위와 같이 하여 검색내용이 목록상자에 나타나면
삭제를 하던 화일을 열던 하는 작업내용은 정상적인 목록상자에서의
작업내용이 그대로 적용이 되면 되니까..
더 이상 손댈 것이 없게 간단하게 처리된 셈이다

***[LOG-IN]***

화일검색과 더불어 분류명도 검색하고 싶을수 있을 것이다
같은 요령으로 버튼 컨트롤을 하나 더 달고
검색대화상자는 같은 것을 사용하면 될 것이다



그런데 검색대화상자가 로딩될때,
화일을 검색하기 위한 것인지, 아니면 분류명을 검색할 것인지를
어딘가에 표시하여 주는 것이 좋을 것이다
그래야 검색창이 로딩되면서 이 정보를 읽고 필요한 초기화를 할수 있을것이다
아래와 같이 일반모듈에 변수를 하나 선언하고

'일반모듈 전역변수및상수
Public Const CATEGORY_SEARCH_CAPTION = "분류검색"
Public sSearchType As String  'file|category

'화일검색버튼
Private Sub cmdSearch_Click()
modMain.sSearchType = "file"
frmSearch.Show
End Sub

'분류검색버튼
Private Sub cmdCategorySearch_Click()
modMain.sSearchType = "category"
frmSearch.Show
End Sub

실은 분류검색은 목록상자에 모두 나와 있으니까
별 필요한 기능은 아니지만
프로그래밍적으로 학습을 할 좋은 쌤플이라서 달아 놓아 보는 것
테이블에 모아놓은 정보는 Tree형식으로 구성하도록 하여 놓은 것이니까..
어떤 검색을 한 내용이 Tree의 중간 분류명이라고 한다면
이 분류명을 중심으로 상위분류명과 하위 분류명을 프로그래밍적으로
찾아 보는 좋은 학습의 도전꺼리가 될 것이다
아래와 같이 검색버튼을 크릭하면 실행되는 이벤트프로시져에
하나는 화일검색, 다른 하나는 분류명 검색을 두도록 하자

Private Sub cmdSearch_Click()
Dim sSearch As String
Dim rTable As Range, rRow As Range
sSearch = UCase(Me.txtSearch.Text)
If sSearch = "" Then MsgBox "검색어 입력하세요": Exit Sub
'모듈시트의 전역변수 bSearchType값을 읽어서..
If modMain.sSearchType = "file" Then
'아래는 이전 화일에서 한 화일검색내용
    Dim lstFile As MSForms.ListBox
    Dim sFileName As String
    Set rTable = modMain.getTable(modMain.FILE_TABLE_START)
    Set rTable = rTable.Offset(1).Resize(rTable.Rows.Count - 1)
    Set lstFile = frmFileManager.lstFile
    lstFile.Clear
    lstFile.ColumnCount = 4
    lstFile.ColumnWidths = ";0;0;0"
    lstFile.Enabled = False
    For Each rRow In rTable.Rows
        sFileName = UCase(Split(rRow.Cells(modMain.TBL_FILE_FILENAME_COL).Value, ".")(0))
            
        If sFileName Like "*" & sSearch & "*" Then
            lstFile.AddItem rRow.Cells(modMain.TBL_FILE_FILENAME_COL)
            lstFile.List(lstFile.ListCount - 1, 1) = rRow.Cells(modMain.TBL_FILE_FILEPATH_COL)
            lstFile.List(lstFile.ListCount - 1, 2) = rRow.Cells(modMain.TBL_FILE_CATEGORY_ID_COL)
            lstFile.List(lstFile.ListCount - 1, 3) = rRow.Row
        End If
    Next
    If lstFile.ListCount > 0 Then lstFile.Enabled = True
Else
'새로 추가한 분류명검색..찾고자 하는 분류명의 최상위 분류명을 찾는 것이 핵심이다
    Dim lstCategory As MSForms.ListBox
    Set lstCategory = frmFileManager.lstCategoryView
'분류테이블범위를 찾아서
    Set rTable = modMain.getTable(modMain.CATEGORY_TABLE_START)
    Set rTable = rTable.Offset(1).Resize(rTable.Rows.Count - 1)
'행별로 순환한다
    For Each rRow In rTable.Rows
'분류명이 맞는 것을 찾는다, 분류명은 찾고자 하는 값이 일부가 아니고 전체를 찾도록 한다
        If sSearch = UCase(rRow.Cells(modMain.TBL_CATE_CATEGORY_COL)) Then
'찾았으면..
            Dim rTopParentRow As Range
'찾은 행의 부모 ID값이 0이면 최상위 분류명 이 행을 그대로 사용하면 된다
            If rRow.Cells(modMain.TBL_CATE_P_CATEGORY_ID_COL) = 0 Then
                Set rTopParentRow = rRow
            Else
'최상위 부모행이 아니면 최상위 부모를 찾는 함수를 별도로 하나 작성하여 호출한다			
                Set rTopParentRow = modMain.getTopParentRowByChildRow(rRow, rTable)
            End If
' 최상위 부모행을 찾았다면, 이 행으로 목록상자에 해당 분류명을 기록하는 프로시져를 별도로 하나 작성호출한다
            If Not rTopParentRow Is Nothing Then
                frmFileManager.lstCategoryView.Clear
                modMain.fillOneCategory frmFileManager.lstCategoryView, rTable, rTopParentRow
                GoTo X
            End If
        End If
    Next
    MsgBox "[" & Me.txtSearch.Text & "] 는 존재하지 않습니다"
End If
Exit Sub
X:
Unload Me
End Sub

찾고자 하는 분류명의(어느 레벨에 있던 상관없이)
최상위분류명을 찾아서 아래와 같이 목록상자에 그리고자 하는 것

최상위분류
  |___중간분류
      |____중간분류..
	       |____하위분류...

분류명을 찾는 것은 별로 의미가 없지만(목록상자에 주욱 표현이 이미 되어 있는 것이니까)
프로그래밍적으로 내공을 쌓기 좋은 연습대상이라서 만들어 넣었다

아래 11번째 화일에서..
getTopParentRowByChildRow 함수와
fillOneCategory 프로시져를 잘 보시고 응용하시기 바란다

***[LOG-IN]***

항상 부족한 것이 있고, 항상 개선하여야할 것이 나타나는 것이
프로그래밍이다
분류명을 검색하고 나서 최초로 돌아가야 하는데 없다..
버튼을 하나 더 달아야겠다, 그림과 같이



비활성화시켜놓고 검색이 성공하면 활성화시키고 크릭하여
원래의 분류목록으로 갱신되면 다시 버튼을 비활성화시키도록 한다
이 버튼을 크릭하면

Private Sub cmdCategoryAllView_Click()
fillCategories
cmdCategoryAllView.Enabled = False
End Sub

UserForm을 초기화할때 목록상자를 채우던 프로시져를 그냥
다시 호출하면 되는 것
그래서 프로시져는 귀찮더라도 토막을 쳐서 별동부대로 만들어 두는
습관이 좋은 습관이다

그리고 버튼은 다시 비활성화시켜놓고..

***[LOG-IN]***