これまでに作成したオブジェクトを組み合わせて、工程表を描画するクラスを作成します。名称を「clsGanttChart」として、クラスを作成します。
描画に必要な変数の宣言を指定します。
Option Explicit Private ChartData As New clsCategorys 'データ Private Schedules As New clsSchedules Private ColNO As Integer 'NO列 Private ColName As Integer '名称列 Private ColPerson As Integer '担当者列 Private BeginDate As Date '工程表開始日 Private BeginRow As Integer '工程表開始行 Private BeginColumn As Integer '工程表開始列 Private DrawColumns As Integer '工程描画列数
これらの数値を初期化するプロシージャーを作成します。
'変数初期化
Public Sub Initialize()
ColNO = 1
ColName = 2
ColPerson = 3
BeginDate = #1/1/2005#
DrawColumns = 31
BeginRow = 5
BeginColumn = 4
End Sub
本来、外部からこれらの数値を変更できるようにすべきですが、簡易版ということで定数扱いにします。ただし、このプロシージャーを少し変更するだけでとても柔軟にカスタマイズができます。是非、検討してみてください。
次にデータを読み込むプロシージャーを作成します。
'データ読み込み
Private Sub LoadData()
ChartData.Load
Schedules.Load
Dim i As Long
Dim j As Long
For i = 1 To Schedules.Count()
j = Schedules.Items(i).Category - 1
If 0 < j And j <= ChartData.Count() Then
Call ChartData.Items(j).Schedules.Add(Schedules.Items(i))
End If
Next
End Sub
ここでは、分類と項目の全データを読み込んでいます。その後、項目の分類NOを調べて、一致する分類の項目集合オブジェクトに項目を登録する操作を行っています。こうすることで、ガントチャートを描画するマクロが非常に簡素になります。
次に工程表の描画処理を考えます。まず、工程表の描画中の行を取得するため「ActiveRow」という変数を追加します。
Private ActiveRow As Long
できているところまでで描画プロシージャーを記述すると、以下のようになります。
'描画
Public Sub Draw()
Call Initialize
Call LoadData
ActiveRow = BeginRow
End Sub
では、分類を表示するプロシージャーを作成します。
'分類の表示
Private Sub PrintCategorys(wk As clsCategorys)
Dim i As Long
For i = 1 To wk.Count()
With ActiveSheet
.Cells(ActiveRow, ColNO).Value = i
.Cells(ActiveRow, ColName).IndentLevel = 0
.Cells(ActiveRow, ColName).Value = wk.Items(i).Name
End With
ActiveRow = ActiveRow + 1
Next
End Sub
分類集合オブジェクトの持っている分類データの数だけ、分類名を書きます。ここで、セルのプロパティで「IndentLevel = 0」としていますが、これは項目の名称を同じ列に記述するので、その区別をインデントで行うためです。次に作成する項目のインデントは1と設定したいと思います。
では項目を表示するプロシージャーを作成します。
'項目の表示
Private Sub PrintSchedules(wk As clsSchedules, num As Long)
Dim i As Long
For i = 1 To wk.Count()
With ActiveSheet
.Cells(ActiveRow, ColNO).Value = ""
.Cells(ActiveRow, ColName).IndentLevel = 1
.Cells(ActiveRow, ColName).Value = num & "-" & i & ". " & _
wk.Items(i).Name
.Cells(ActiveRow, ColPerson).Value = wk.Items(i).Person
End With
ActiveRow = ActiveRow + 1
Next
End Sub
項目集合オブジェクトと番号表示のための数値を引数としています。与えられた項目集合オブジェクトのデータ数だけ、項目名を書き出します。また、番号列の表示を消去し、担当者も合わせて書き出します。
このプロシージャーを分類表示プロシージャーから呼び出します。変更後は以下のとなります。
'分類の表示
Private Sub PrintCategorys(wk As clsCategorys)
Dim i As Long
For i = 1 To wk.Count()
With ActiveSheet
.Cells(ActiveRow, ColNO).Value = i
.Cells(ActiveRow, ColName).IndentLevel = 0
.Cells(ActiveRow, ColName).Value = wk.Items(i).Name
End With
ActiveRow = ActiveRow + 1
Call PrintSchedules(wk.Items(i).Schedules, i)
Next
End Sub
ではいよいよチャートバーの描画です。基本的な操作は簡易版ガントチャートで説明した内容と変わりません。ただ、もう少し高度にするため、プロシージャーを少し多くして繰り返す処理を簡素化します。
まず、チャートの描画要・不要を判断するために、工程表の最終日を取得する必要があります。そこで、最終日を取得するプロシージャーを作成します。
'最終日の取得
Private Function EndDate() As Date
EndDate = BeginDate + DrawColumns
End Function
最終日を取得できるようになったので、これを用いてチャート表示用プロシージャーを書き始めます。
'チャートの表示 Private Sub PrintChart(wk As clsSchedule) '予定線 If (BeginDate <= wk.PlanBegin And wk.PlanEnd <= EndDate) Or _ (wk.PlanBegin <= BeginDate And EndDate <= wk.PlanEnd) Then '予定線描画処理 End If '実績線 If (BeginDate <= wk.ActionBegin And wk.ActionEnd <= EndDate) Or _ (wk.ActionBegin <= BeginDate And EndDate <= wk.ActionEnd) Then '実績線描画処理 End If End Sub
予定、実績それぞれの開始・終了時間と工程表の期間を判定して、チャートバーの描画が必要か否かを決定します。
次に予定・実績の線描画処理プロシージャーを作成します。
'チャート用シェイプの追加 Private Function AddChartBar(wkBegin As Date, wkEnd As Date, wkMode As Integer) As Shape Dim s As Shape Dim Height As Single Dim Top As Single Dim Left As Single Dim Width As Single Dim Fill As Long With ActiveSheet.Rows(ActiveRow) Top = .Top Height = .Height / 2 End With Select Case wkMode Case □□□□□□'予定の場合 Top = Top + 2 Height = Height - 4 Fill = RGB(255, 255, 255) Case □□□□□□'実績の場合 Top = Top + Height + 2 Height = Height - 4 Fill = RGB(0, 0, 0) End Select Left = □□□□□□'チャートバーの左側取得処理 Width = □□□□□□'チャートバーの幅取得処理 ActiveSheet.Shapes.AddShape(msoShapeRectangle, Left, Top, Width, Height).Select With Selection .ShapeRange.Fill.ForeColor.RGB = Fill Set s = ActiveSheet.Shapes(.Name) End With Set AddChartBar = s End Function
開始日と終了日、そして予定か実績の区別を取得し、それにあったチャートバーを描画します。コード中にある「□□□□□□」は未完成の部分を示します。
まず、引数にあるwkModeですが、ここで予定は1で実績は2と取り決め
Select Case wkMode Case 1 '予定の場合 Top = Top + 2 Height = Height - 4 Fill = RGB(255, 255, 255) Case 2 '実績の場合 Top = Top + Height + 2 Height = Height - 4 Fill = RGB(0, 0, 0) End Select
と書くことができますが、これでは後々わかりにくいかもしれません。そこで、予定と実績をあらわす定数を作成し、それを使用することにします。変数の宣言部に次の2行を追加します。
Private Const MODE_PLAN = 1 Private Const MODE_ACTION = 2
すると、
Select Case wkMode Case MODE_PLAN Top = Top + 2 Height = Height - 4 Fill = RGB(255, 255, 255) Case MODE_ACTION Top = Top + Height + 2 Height = Height - 4 Fill = RGB(0, 0, 0) End Select
となり、判読しやすくなります。数ヶ月、数年経過すると自分の書いたコードも意味がわからなくなることがよくあります。そこで、今後のことを考えて、メンテナンス性も重視したプログラミングを行うことはとても重要です。多くの人が「人にわかるコードを書くよう心がけるのは良いこと」と言っていますし、僕自身そう思います。
次に工程表上である日付がどの位置にあるかを取得するプロシージャーを作成します。
'日付から工程表上の位置を取得
Private Function getPosition(wkDate As Date) As Single
Dim Column As Double
Column = wkDate - BeginDate
If Column < 0 Then
Column = 0
ElseIf DrawColumns < Column Then
Column = DrawColumns
End If
Column = Column + BeginColumn
With ActiveSheet.Columns(Int(Column))
getPosition = .Left + .Width * (Column - Int(Column))
End With
End Function
まず、開始日から日付を引きます。0以下なら工程範囲外なので、0に丸めます。また、描画列数以上ならばやはり工程範囲外なので、描画列数に丸めます。取得した列の左端に列幅 x 取得した数値の小数部とすることで、時間単位での位置を取得することが可能となります。これはVB系列の言語において1日が1であることから、このような計算になります。1秒が1のシステムではそれに対応した位置を考えることになります。
このプロシージャーを用いると、
Left = getPosition(wkBegin) Width = getPosition(wkEnd) - Left
となります。
簡易版ガントチャートでもチャートバーには名称をつけ、他のシェープと分けましたので、ここでもそうします。そこで勝手ですが、予定なら「CHB_PLAN00000」、実績ならば「CHB_ACTION00000」とします。チャートの名称を指定するのに定数を用いたいと思うので変数宣言部に次の行を追加します。
Private Const CHARTNAME = "CHB_"
チャートバー追加のプロシージャーはShapeオブジェクトを返すので、このまま名称を変更するような行をチャートバー表示プロシージャーに追加すると、以下のようになります。
'チャートの表示 Private Sub PrintChart(wk As clsSchedule) '予定線 If (BeginDate <= wk.PlanBegin And wk.PlanEnd <= EndDate) Or _ (wk.PlanBegin <= BeginDate And EndDate <= wk.PlanEnd) Then AddChartBar(wk.PlanBegin, wk.PlanEnd, MODE_PLAN).Name = CHARTNAME & "PLAN" & Format(wk.No, "00000") End If '実績線 If (BeginDate <= wk.ActionBegin And wk.ActionEnd <= EndDate) Or _ (wk.ActionBegin <= BeginDate And EndDate <= wk.ActionEnd) Then AddChartBar(wk.ActionBegin, wk.ActionEnd, MODE_ACTION).Name = CHARTNAME & "ACTION" & Format(wk.No, "00000") End If End Sub
出来上がった部分までを追加します。
'描画
Public Sub Draw()
Call Initialize
Call LoadData
ActiveRow = BeginRow
Call PrintCategorys(ChartData)
End Sub
ですが、このままでは工程表を描画する度にチャートバーが追加され続けますし、同じ名前に設定しようとするとエラーになります。そこで、以下のチャートバー消去プロシージャーを追加して、描画処理のはじめに実行します。
'チャートの消去 Private Sub ClearChartBar() Dim s As Shape For Each s In ActiveSheet.Shapes If s.Name Like CHARTNAME & "*" Then s.Delete Next End Sub '描画 Public Sub Draw() Call Initialize Call ClearChartBar Call LoadData ActiveRow = BeginRow Call PrintCategorys(ChartData) End Sub
ここまでで、描画に必要な処理は一通り作成終わりました。ですが、実行を選択しても実行するマクロの一覧には何も出ていません。ここまで作成してきたのはクラスであり、実体がありません。そこで、標準モジュールを追加して、そこに描画するためのコードを記述しなくてはなりません。
名称を「mdlMain」として標準モジュールを追加し、以下のコードを記述します。
Option Explicit
Option Base 1
'チャートの描画
Public Sub UpdateChart()
If ActiveSheet.Name <> "チャート" Then Exit Sub
Dim wk As New clsGanttChart
wk.Draw
End Sub
では、実行してみましょう。でも、その前にデータを入力しなくては何も表示されません。まだ入力の仕組みはできていないので、とりあえず手入力でデータを入力して、工程表を描画してみて下さい。
うまく描画されましたか? 僕の入力した適当なデータでは以下のようになりました。