PROGRAMMING WORKSHOP

VSTO|
작업일정관리_5

oTimeUnits 라는 배열에 줄줄이 담긴 clsTimeUnit 개체로
아주 많은 표현을 할 수 있을 것이고
이제 공정표를 그리는 목적에 맞는 타임밴드를 oTimeUnits를 순환하면서
clsTimeUnit개체가 갖고 있는 정보의 일부를 활용하면 되는 것이다

하면 할수록 얼마나 융통성있고, 효율적인 방법인지 알게 될 것이다



지난 회에 만들어 놓았던 전역변수 oTimeUnits()배열을 순환하면서
위의 그림을 아래와 같이 작성표현된 것이다
병합부분이 좀 길뿐 간단한 것이다

Private Sub drawTimeBand(sStartAddress As String)
Const TIME_BAND_SHEET As String = "TimeBandSample"
On Error Resume Next
Application.DisplayAlerts = False
Worksheets(TIME_BAND_SHEET).Delete
Application.DisplayAlerts = True

Dim rStartCell As Range ' 타임밴드시작기준셀
Dim oTU As clsTimeUnit ' 각 타임유닛
Dim rCurrentTU As Range ' 현재 타임유닛을 표현하는 셀
Dim varX As Variant ' 배열 순환변수
Dim iCol As Integer '  열방향이동변수
Dim rMonth As Range ' 월범위
Dim rYear As Range ' 년도범위
Dim iMonth As Integer ' 월병합 변수
Dim iYear As Integer ' 년도병합 변수

With Worksheets.Add
    .Name = TIME_BAND_SHEET
    Set rStartCell = .Range(sStartAddress)
    For Each varX In oTimeUnits
        Set oTU = varX
        Set rCurrentTU = rStartCell.Offset(, iCol)
        With rCurrentTU
        
            Set oTU.rUnitCell = rCurrentTU
            
            .Value = oTU.datStart ' 타임유닛 기간
            .NumberFormat = "dd"
            .BorderAround xlSolid
            If Weekday(.Value) = 7 Then
                .Interior.ColorIndex = 3
                .Font.ColorIndex = 2
            End If
            .Offset(1) = oTU.iAccumDays ' 누적일수 표기
            '/////////////////////////////  월, 년도별  범위 병합작업시작
            If rMonth Is Nothing Then
                Set rMonth = rCurrentTU.Offset(-1)
                iMonth = Month(oTU.datStart)
            Else
                If iMonth <> Month(oTU.datStart) Then
                    iMonth = Month(oTU.datStart)
                    rMonth.Cells(1) = iMonth
                    rMonth.Merge
                    Set rMonth = rCurrentTU.Offset(-1)
                Else
                    Set rMonth = rMonth.Resize(, rMonth.Cells.Count + 1)
                End If
            End If
            If rYear Is Nothing Then
                Set rYear = rCurrentTU.Offset(-2)
                iYear = Year(oTU.datStart)
            Else
                If iYear <> Year(oTU.datStart) Then
                    iYear = Year(oTU.datStart)
                    rYear.Cells(1) = iYear
                    rYear.Merge
                    Set rYear = rCurrentTU.Offset(-2)
                Else
                    Set rYear = rYear.Resize(, rYear.Cells.Count + 1)
                End If
            End If
            '////////////////////////////월, 년도 셀 병합작업 끝
        End With
        iCol = iCol + 1
    Next
    
End With
'//////////////////// 년도,월행 셀 병합 마무리
rMonth.Cells(1) = IIf(iMonth + 1 > 12, 1, iMonth + 1): rMonth.Merge
rYear.Cells(1) = iYear + 1: rYear.Merge

'/////////////////// 마지막 서식.
With rStartCell.CurrentRegion
    .Font.Bold = True
    .Font.Size = 11
    .Font.Name = "맑은 고딕"
    .HorizontalAlignment = xlCenter
    .Borders.LineStyle = xlSolid
End With

End Sub

개체에다 관련정보를 더 많이 만들어 두었다면..
좀더 다양한 표현을 할수도 있겠구나..라는 생각이 들면
개체의 개념을 이제 정복한 것이 된다
정보의 분석상태는 최대한 다른사람들이 볼때 인상적이고
곧바로 상태를 파악하게 해 줄 수 있다면 최선일 것이다
위에서 oTimeUnits배열에 모아놓은 내용중에 각 일정유닛별로
공사금액을 만들어 놓았었다
이것을 순환하면서 읽어서 엑셀의 도형실력을 첨가하면 아주 좋은
표현을 아래와 같이 할 수 있을 것이다



위의 것도 역시 oTimeUnits()배열을 또 써먹는 것이다
표현하고 싶은대로 써먹는 개체배열...
도형은 타임유닛이 갖고 있는 dblAmount속성값이 가장 큰값을
아래와 같이 찾아서
이것을 기준으로 각각의 유닛에 해당하는 금액의 도형크기만
아래와 같은 코딩으로 만들면 되는 것

Private Sub graphicViewOfAmountOnEachTimeUnit()
Dim dblTopAmount As Double
Dim dblHeightRate As Double

Dim varX As Variant, oTU As clsTimeUnit
Dim shtX As Worksheet
Dim lTop As Long, lLeft As Long, lWidth As Long, lHeight As Long

On Error Resume Next
dblTopAmount = getTopAmount
dblHeightRate = 100 / dblTopAmount

For Each varX In oTimeUnits
    Set oTU = varX
    If shtX Is Nothing Then
        Set shtX = oTU.rUnitCell.Worksheet
    End If
    With oTU.rUnitCell.Offset(3)
        lLeft = .Left
        lTop = .Top
        lWidth = .Width
        lHeight = oTU.dblAmount * dblHeightRate
    End With
    With shtX.Shapes.AddShape(msoShapeRectangle, lLeft, lTop, lWidth, lHeight)
        .Fill.ForeColor.RGB = RGB(0, 0, 200)
    End With
Next

End Sub
Private Function getTopAmount()
Dim varX As Variant
Dim dblTemp As Double
Dim oTU As clsTimeUnit
For Each varX In oTimeUnits
    Set oTU = varX
    If dblTemp < oTU.dblAmount Then
        dblTemp =oTU.dblAmount
    End If
Next
getTopAmount = dblTemp 
End Function
***[LOG-IN]***

이제 작업개체를 모아둔 oTasks()배열과
카렌다정보 개체를 모아둔 oTimeUnits()배열을 조합하여
그리면 작업공정표가 그려질 것이다
oTasks()배열을 행방향으로 순환하면서 계산된 작업테이블을
예쁘게 그리고 카렌다 시작 기준셀을 얻어서
oTimeUnits()배열을 열방향으로 순환하면서 그리면
행과 열의 교차되는 범위가 공정챠트를 그릴 PlotArea범위가
될 것이다
다시한번 작업행방향을 순환하면서 각작업의 시작일자와
종료일자로 PlotArea상에서 카렌다행과 교차되는 점을 찾으면
해당 작업의 도형크기가 계산될 것이고..
중요한 것은 두개의 배열을 준비하는 과정이 문제이고
나머지는 엑셀개체와 적절히 접목하여 예쁘게 표현하는 것을
하면 되는 것이다



그림과 같이
두개의 개체를 그린다
하나는 oTasks() 배열에 담은 clsTask개체를 행방향을
순환하면서 작업테이블과 계산된 시간과 기간등을 그리고
이 작업이 끝난후 이 작업범위에서 타임밴드의 기준셀을
전달하여, 이 기준셀을 기준으로
oTimeUnits() 배열에 담은 clsTimeUnit개체를 열방향으로
순환하면서 시간 프레임을 만들어 주고
이두개의 범위개체가 교차되는 부분이 다음에 작업할
Gantt챠트를 표현할 도형들을 그릴 범위가 된다
두개의 범위의 사이는 한열정도 띄워주는 것이 각 작업을 하는데
융통성이 있다
사용자가 테이블과 중간에 손으로어떤 작업을 삽입할때도
중간이 하나 비어있어야 손쉽게 열삽입작업들을 할수 있을 것이고
각범위에서 CurrentRegion을 잡을때, 다른 개체까지 포함되는
번거로움을 피할수 있다

***[LOG-IN]***

위의 화일을 실행하면 WBS상위레벨의 값이 계산이 아직 안된 부분이 있을 것이다
이것은 중간에 기본테이블에 작업금액을 삽입하고
WBS상위 계산쪽은 건들지 않아서 그런 것이다
각자 WBS상위의 값..날짜와 금액(해당 WBS자식 작업의 값 합계)를
잡아 넣어 보시기 바란다


이제 Gantt챠트를 그리는 일이 남았다
기본적인 것은 모두 끝난것이다
테이블 만들기용 개체
타임밴드 만들기용 개체
두개를 행방향 열방향으로 조합하여 전체적인 프레임을 그렸고
이제
작업테이블개체의 작업명별 정보를 읽어서
타임밴드에서 정확한 위치를 잡으면 ..
즉 사각형도형을 그릴때..
시작 X,Y점을 찾고 폭을 계산하고 높이를 계산하면
Shapes집합체의 AddShape하면 된다

꼭 공정스케쥴을 그리는 것이 아니더라도 많은 산업에
활용될수 있는 프레임이니 모두 열심히 익혔으면 좋겠다

왜 타임밴드를 개체로 복잡을 떨면서 만들었는지
챠트를 그리면서 알게 된다
챠트를 그리려면 시작일자와 종료일자에 해당하는 X값을 찾아야 한다
X값은 당연히 타임밴드를 순환하면서 쉽게 찾을수 있는 것이다
이미 다음에 쓸 정보를 타임밴드라는 프레임을 구성하는
clsTimeUnit개체에 팩키지로 담아 놓은 셈인 것이다
오호..이렇게 편할 수가..
그렇지 않았다면 천지 사방을 계산하면서 찾아야 한다...

이번 화일에서는 아래의 그림과 같이
WBS상위레벨의 종료날자와 기간과 금액을 계산하여 넣는 것



그리고 WBS그룹별 정렬
그리고 테이블 상에서 상위레벨만 색상표시
재귀프로시져를 돌면서
상위레벨의 시작날짜를 계산하였던 것을 이해하였다면
위의 계산은 하나도 어려운 일이 아닌 것
문제는 집합체(배열)속의 사용자정의 개체별 순환..그리고 재귀함수호출
이것이 기본이고 이것을 이해하면 프로그래밍세상이 밝아지는 것이 된다

어라..챠트를 그리고 보니까..한달씩 TimeBand값이 밀려있네..
어디서 잘못된거야??!!
프로그래밍을 하다 보면 아직 실행되지 않은 상태에서는 문제를 잘 인식하지 못한다
아래와 같이 TimeBand를 만드는 과정에서 명령의 순서를 잘못하여
한달씩, 일년씩 셀의 표시되는 값이 병합되면서 밀려 버린것이 발견되기도 한다

iYear = Year(oTU.datStart)
rYear.Cells(1) = iYear

'이런..멍청한..짓을... 아래와 같이 실행 순서를 바꾼다

rYear.Cells(1) = iYear
iYear = Year(oTU.datStart)

챠트를 그리기 전까지는 잘 발견되지 않는 실수들이다..
프로그래밍은 과정마다 앞의 과정의 절차상의 오류를 뒤의 것을
해야 인식하게 되는 경우가 종종 있다

그러니 여러개의 프로세스를 연결하는 과정에서 신경을 쓰면서 해야 할 부분이다
정말 컴퓨터는 시키는대로 아주 앞뒤 순서 맞추어 잘한다
사람한테 이런 명령을 주면 알아서 순서를 바꿀텐데..
컴퓨터는 멍청하면서 우직하고,부지런 하고 요령피우지 않는다
바보짓도 우직하게 잘 수행해준다

아무튼 아래와 같이 그려졌다
기본정보를 랜덤으로 뽑아서 챠트가 좀 허접한 그림을 보인다
여러분들의 실제적인 스케줄을 기본정보로 하여 그리면 그림이
예뻐질 것이다



아래 화일로 기본적인 챠트까지..1차 완료되었다..
기본개념을 잘 소화시켰으면 좋겠고
다음 코너에서 좀더 확장 시켜 나가보도록 하자

***[LOG-IN]***