PROGRAMMING WORKSHOP

화일관리도구 |

목록상자속성변경

앞의 화일에서 최하위 분류명에만 화일을 넣는 것을 헷갈리는 숫자상수로
열의 위치를 잡는 것은 상수선언하고 사용하는 것과
최하위분류명에만 화일을 배정하라는 조건문 하나를 없애 버리니
모든 분류명에 화일을 배정할수 있는 것도 시스템으로 바뀌었다
수정을 하거나, 기능을 추가하거나 할때 느끼게 된다
부지런히 상수와 변수를 정리 정돈하는 것이 모든 일을 수월하게 할수 있다는 것을
그리고 그런 경우를 만나기 위하여서는 이런 식의 하나의 소루션을
꾸며가면서 학습하여야 모든 프로그래밍환경의 경우의 수를 만들수 있고
경험할수 있다..그러니, 중간, 중간 뜸성뜸성 화일을 받아 보았자
뭔소린지 모른다
처음 화일부터 문제의식을 갖고 달려 들어야 내공이 쌓인다

이번에는 화일목록상자의 목록을 여러개선택할수 있게 하고
한꺼번에 삭제할수 있도록 하고,
또 같은 분류명하에 똑같은 화일이름이 등록이 되는 것은 바보짓이다
한번 등록된 것은 등록하지 못하게 제어를 하도록 하자



그리고 목록상자의 여러개의 아이템을 선택할수있도록 설정하는 것은 해당목록상자의 속성창에서 MultiSelect속성을 설정하여도 되겠지만 이것은 폼이 로딩되는 초기화작업에서 프로그래밍적으로 처리하는 습관이 좋다

Private Sub UserForm_Initialize()
fillCategories
setControls
End Sub

setControls이라는 콘트롤초기화부분에서 아래와 같이 추가 시키는 것이 좋다



그림과 같이 하면 여러개의 아이템을 선택할수 있게 되는데.. 어라...[삭제]버튼이 활성화되지 않네??!!! [삭제]버튼이 활성화되어야 삭제를 하던가 말던가 하지 않겠는가?? 이전의 코드는 아래와 같다

Private Sub lstFile_Change()
If lstFile.ListIndex < 0 Then Exit Sub

If Me.lstFile.Text <> "" Then
    Me.cmdFileDelete.Enabled = True
Else
    Me.cmdFileDelete.Enabled = False
End If
End Sub

이것은 하나만 선택하면 [삭제]버튼이 활성화되는 멍청한 코드가 된셈이다
그러니, 프로그래밍이라는 것이 만만한 것이 아니다
똑똑해야 되나?? 똑똑한 것보다 중요한 것은 섬세하고
경우의 수를 잘 따져보는 끈질김이 프로그래밍의 덕목이다
이것을 치면 저쪽에서 소리가 날까..안날까..두세수를 미리 넘겨보는
차분함이 필요한 것이다
그러니 할만한 것이 프로그램이다
정신없이 일에 치여서 , 사는 것인지, 그냥 굴러가는지도 모르는 세상에서
프로그래밍은 명상이고, 차분하고, 고요하게 하는 도구이기도 하다
위의 내용을 아래와 같이 수정하면..

Dim iX As Integer
Me.cmdFileDelete.Enabled = False
For iX = 0 To Me.lstFile.ListCount - 1
    If Me.lstFile.Selected(iX) Then
        Me.cmdFileDelete.Enabled = True
        Exit Sub
    End If
Next
End Sub

여러개 선택한다는 것은 배열이나, 집합체이다
여기에서는 목록상자의 정보를 담고 있는 것이 배열이고
lstFile.Selected
에서 Selected 가 배열인 것이다
즉 선택된 것을 Selected라는 속성(배열)이 보관하고 있는 것이고
이것을 순환하면서 이값이 True이면 선택된 아이템이 된다
하나라도 선택되면 [삭제]버튼을 활성화시키도록 한 것이다
이제 흥미로운 경우가 나타난다
화일을 여러개 삭제 한다고할때
지금까지 만들었던 삭제버튼의 프로시져는 아래와 같았다

Private Sub cmdFileDelete_Click()
Dim sPath As String
Dim sFile As String
Dim iRowNum As Integer

With Me.lstFile
    sFile = .List(.ListIndex, 0)
    sPath = .List(.ListIndex, 1)
    iRowNum = .List(.ListIndex, 3)
End With

If MsgBox(sPath & sFile & " 를 삭제하시겠습니까?", vbYesNo, modMain.PROJ_NAME) = vbYes Then

    If MsgBox(sPath & sFile & "의 실제화일도 삭제하시겠습니까?", vbYesNo, modMain.PROJ_NAME) = vbYes Then
        VBA.FileSystem.Kill sPath & "\" & sFile
        MsgBox sPath & "\" & sFile & " 을 영구삭제되었습니다"
    End If
    
    Dim rTbl As Range
    Set rTbl = modMain.getTable(modMain.FILE_TABLE_START)
    Application.Goto rTbl.Rows(iRowNum)
    rTbl.Rows(iRowNum).Delete Excel.XlDeleteShiftDirection.xlShiftUp
    fillFilesList modMain.stripBad(Me.lstCategoryView.Text)
    cmdFileDelete.Enabled = False
End If
End Sub

이제 위와 같은 작업을 선택한 화일갯수만큼 진행하여야 한다
순환문으로 처리하겠지만..좀 복잡해진다
그래서 프로시져를 별도로 만드는 습관이 좋다
버튼이나 다양한 콘트롤에서 발생하는 작업은 이벤트가 발생한 곳에서는
순환과 조건, 그리고 초기화작업만 따지고 본 작업은 별도의 프로시져에서
하게 하는 습관이 좋은 습관이다
위의 경우는 버튼의 크릭이벤트가 발생하는 곳, 한곳에서 때려잡으려고
기를 쓴 경우다
분산 시켜 놓으면 다른 작업을 할때 호출이 되게 할수도 있는 자원절약이
되기도 하는 것이다
그리고 이번 작업에서는 실제화일의 삭제부분은 없애 버리자
그냥 분류명하에 배정된 화일의 정보만 없애도록 하자

삭제 전에 화일을 등록할때 같은 분류부호에 중복되는 화일을 넣는 것을
방지하는 구문을 추가 시켜 보자
여러분이 갖고 있는 화일의 코드에 아래의 빨강색이 추가 되었다
여러분이 직접 옮겨 보신후 다음 화일에서 확인하여 보시기 바란다

Private Sub cmdFileEdit_Click()
Dim sTemp As String
Dim sFname As Variant
Dim iX As Long
Dim oX As New Collection  불량화일이름 모을 집합체하나 선언하고 반드시 New를 빼먹지 마시고..
sFname = Application.GetOpenFilename( _
    FileFilter:="All Files, *.*, Excel Files, *.xl*;*.xls;*.xlt", _
    FilterIndex:=2, _
    MultiSelect:=True)
sTemp = Me.lstCategoryView.Text & " 항목에 " & vbNewLine & vbNewLine

If IsArray(sFname) Then
    For iX = LBound(sFname) To UBound(sFname)
        sTemp = sTemp & sFname(iX) & vbNewLine
        oX.Add sFname(iX)
    Next
    sTemp = sTemp & vbNewLine & " 를 포함시키겠습니까?"
    If MsgBox(sTemp, vbYesNo) = vbYes Then
        Dim varX As Variant
        Dim rTable As Range
        Dim iCategoryID As Integer
        Dim iRow As Integer
        Dim iMaxFileId As Integer
        Dim oDupe As New Collection ' 중복되는 화일모음
        
        iCategoryID = modMain.getParentIDByCategoryName(modMain.stripBad(Me.lstCategoryView.Text))
        Set rTable = modMain.getTable(modMain.FILE_TABLE_START)
        Set rTable = rTable.Rows(rTable.Rows.Count)
        iMaxFileId = Application.Max(rTable.Columns(1))
        With rTable
        For Each varX In oX
            iRow = iRow + 1
            iMaxFileId = iMaxFileId + 1
            If isNotDupeFile(varX, oDupe) Then ' 중복검문추가
                With .Offset(iRow)
                    .Cells(modMain.TBL_FILE_ID_COL) = iMaxFileId
                    .Cells(modMain.TBL_FILE_CATEGORY_ID_COL) = iCategoryID
                    .Cells(modMain.TBL_FILE_FILEPATH_COL) = Left(varX, InStrRev(varX, "\"))
                    .Cells(modMain.TBL_FILE_FILENAME_COL) = Replace(varX, .Cells(3), "")
                End With
            End If
        Next
        End With
' 중복 체크 함수 기능별로 토막을 내는 습관(독립함수)이 좋습니다
' oDupe매개변수는 위에서 선언한 집합체 입니다, 이곳에 불량 화일이름 모아놓습니다
' 그냥 sTemp변수에 문자열로 연결을 하여도 좋지만, 이런 개체를 사용하는
' 습관이 좋습니다 
Private Function isNotDupeFile(sFile As Variant, oDupe As Collection)
On Error Resume Next
Dim iX As Integer
sFile = Right(sFile, Len(sFile) - InStrRev(sFile, "\"))
For iX = 0 To Me.lstFile.ListCount - 1
    If sFile = Me.lstFile.List(iX) Then
        oDupe.Add sFile
        isNotDupeFile = False
        Exit Function
    End If
Next
isNotDupeFile = True
End Function

이제 삭제를 하는데 그림과 같이 여러개를 한꺼번에 삭제를 하거나,
아무튼 만만하지 않다, 어느 부분이 만만하지 않냐하면
테이블의 해당화일을 삭제하는 것이 엉뚱한 것을 삭제하게 되는것을
초보님들은 항상 겪는 일들이다 , 전략을 잘 세워야 한다
어떻게 접근하여 어떻게 삭제 할 것인가?



여러개의 선택목록을 순환하면서 삭제 프로시져를 호출한다

Private Sub cmdFileDelete_Click()
Dim sPath As String
Dim sFile As String
Dim iRowNum As Integer
Dim iX As Integer
For iX = 0 To Me.lstFile.ListCount - 1
    If Me.lstFile.Selected(iX) = True Then
        With Me.lstFile
            sFile = .List(iX, 0)
            sPath = .List(iX, 1)
            iRowNum = .List(iX, 3)
            deleteFile iRowNum, sPath, sFile
        End With
    End If
Next
fillFilesList modMain.stripBad(Me.lstCategoryView.Text)
cmdFileDelete.Enabled = False
End Sub

그런데 여러 화일을 선택하고 삭제를 하면 어라..
두번째 것 부터는 엉뚱한 것을 삭제 한다
왜 그럴까..??
하나의 화일을 삭제 할때마다 범위의 크기가 변하는데
목록에 올라와있던 화일내용은 이전의 범위를 참조하고 있다
범위의 크기가 변하것과 동기화가 안되는 있는 상태에서
삭제되기전의 범위의 행번호를 목록은 그대로 유지하고 있는 것이다
삭제를 하면 관련된 것은 모두 동기화가 되어야 한다
은행에서 출금을 하면 관련된 계정은 모두 동기화가 되어야
하는 것과 마찬가지 일 것이다

방법은 삭제하고자 하는 화일명이 있는 범위를 Union으로 묶어서
한번에 처리하면 될 것이다



여기에서 아하..UNION 메소드가 이럴때 필요한 것이구나!!!!를 접수하시면 된다
아래와 같이 수정하자..

매개변수를 집합체로 바꾸고
Private Sub deleteFile(oX As Collection) ' //// iRowNum As Integer, sPath As String, sFile As String)
Dim rTbl As Range
Dim varX As Variant, sTemp As String, varY As Variant
Dim rDelete As Range
Set rTbl = modMain.getTable(modMain.FILE_TABLE_START)

For Each varX In oX
    varY = Split(varX, "|")집합체에 문자열을 조합하여 받은 정보를 분리하여 의미있는 값으로
    sTemp = sTemp & varY(1) & varY(2) & vbNewLine메세지의 보여줄 안내문..
    
    If rDelete Is Nothing ThenUNION으로 여러범위를 하나로 묶는다
        Set rDelete = rTbl.Rows(varY(0))
    Else
        Set rDelete = Union(rDelete, rTbl.Rows(varY(0))) 
    End If

Next
Application.Goto rDelete이 구문은 필요없지만,이동하여,시각적으로 삭제 확인 If MsgBox(sTemp & "를 삭제하시겠습니까?", vbYesNo, modMain.PROJ_NAME) = vbYes Then rDelete.Delete End If 아래는 이전 하나씩 삭제한 구문 'If MsgBox(sPath & sFile & " 를 삭제하시겠습니까?", vbYesNo, modMain.PROJ_NAME) = vbYes Then ' Application.Goto rTbl.Rows(iRowNum) ' rTbl.Rows(iRowNum).Delete Excel.XlDeleteShiftDirection.xlShiftUp 'End If End Sub

위의 내용을 화일에서 확인해 보시고..
이곳에 표현되지 않은 내용은 모듈시트에서 확인하세요

***[LOG-IN]***