Option Explicit '変数宣言強制
Sub writeSummaryTable(ByRef dictStructure As Object, ByRef writeSheet As Worksheet, ByRef rowStart As Long, ByRef columnStart As Long, Optional ByVal titleArray As Variant = Empty)
Dim numberOfField As Long, buffer As Long, i As Long, ii As Long
Dim pointOfStart As Range, targetCell As Range, targetArea As Range
Dim sourceOfCount As Long
Dim mergeCellCounter As Object, sortedIndexOfDictStructure As Variant
Dim mergeCellTempName As Object
Dim arrayBuffer() As String, keyText As Variant
Dim realValueFlag As Boolean
Dim maxValue As Long
'諸定数値セット
Set pointOfStart = writeSheet.Cells.Item(rowStart, columnStart)
Set mergeCellCounter = CreateObject("Scripting.Dictionary")
Set mergeCellTempName = CreateObject("Scripting.Dictionary")
sortedIndexOfDictStructure = dictStructure.keys 'キーを配列として取得
If IsEmpty(titleArray) = True Then
titleArray = Array()
End If
'区切り文字の数を計算するブロック
numberOfField = 1
For Each keyText In dictStructure
buffer = UBound(Split(keyText, Chr(31))) + 1
If buffer > numberOfField Then
numberOfField = buffer
End If
Next keyText
'セルのマージを担当するカウンター辞書
For i = 1 To numberOfField
mergeCellCounter.Add i, 0
mergeCellTempName.Add i, vbNullString
Next i
'書式設定
pointOfStart.Offset(1, 0).Resize(dictStructure.Count, numberOfField - 1).NumberFormat = "@" '文字列指定
pointOfStart.Offset(1, numberOfField).Resize(dictStructure.Count, numberOfField).NumberFormat = "0" '数値指定
'記述
For i = dictStructure.Count To 1 Step -1
ReDim arrayBuffer(0 To numberOfField - 1) '不足している場合があるので拡張
arrayBuffer = Split(sortedIndexOfDictStructure(i - 1), Chr(31))
realValueFlag = True
For ii = numberOfField To 1 Step -1
'値の書き込み
pointOfStart.Offset(i, ii - 1).Value = CStr(arrayBuffer(ii - 1)) '値の書き込み
If realValueFlag Then
pointOfStart.Offset(i, numberOfField * 2 - ii).Value = dictStructure(sortedIndexOfDictStructure(i - 1))
realValueFlag = False
Else
pointOfStart.Offset(i, numberOfField * 2 - ii).Formula = "=SUM(" & pointOfStart.Offset(i, numberOfField * 2 - numberOfField).Resize(mergeCellCounter(ii) + 1, 1).Address & ")"
End If
'MERGE処理
If CStr(arrayBuffer(ii - 1)) = mergeCellTempName(ii) And mergeCellCounter(ii) > 0 And numberOfField > ii Then
pointOfStart.Offset(i, ii - 1).Resize(mergeCellCounter(ii) + 1, 1).Merge
pointOfStart.Offset(i, numberOfField * 2 - ii).Resize(mergeCellCounter(ii) + 1, 1).Merge
mergeCellCounter(ii) = mergeCellCounter(ii) + 1
Else
mergeCellCounter(ii) = 1
End If
mergeCellTempName(ii) = CStr(arrayBuffer(ii - 1))
Next ii
Next i
'合計値計算
pointOfStart.Offset(1, numberOfField * 2).Formula = "=SUM(" & pointOfStart.Offset(1, numberOfField).Resize(dictStructure.Count, 1).Address & ")"
With pointOfStart.Offset(1, numberOfField * 2).Resize(dictStructure.Count, 1)
.Merge
.Interior.Color = RGB(250, 250, 250)
End With
'表の形成
With pointOfStart.Offset(0, 0).Resize(dictStructure.Count + 1, numberOfField * 2).Borders
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(0, 0, 0)
End With
Call setBorderAroundRange(pointOfStart.Offset(1, 0).Resize(dictStructure.Count, numberOfField * 2), xlMedium, xlThick, xlThick, xlMedium)
Call setBorderAroundRange(pointOfStart.Offset(1, numberOfField * 2).Resize(dictStructure.Count, 1), xlThick, xlThick, xlMedium, xlThick)
'タイトルの設定
With pointOfStart.Resize(1, numberOfField * 2)
.Interior.Color = RGB(220, 220, 220)
.Font.Bold = True
End With
Call setBorderAroundRange(pointOfStart.Resize(1, numberOfField * 2), xlThick, xlMedium, xlThick, xlThick)
For i = 0 To numberOfField - 1
If LBound(titleArray) <= i And i <= UBound(titleArray) Then
pointOfStart.Offset(0, i).Value = CStr(titleArray(i))
pointOfStart.Offset(0, i + numberOfField).Value = CStr(titleArray(i)) & " 件数"
Else
pointOfStart.Offset(0, i).Value = ""
pointOfStart.Offset(0, i + numberOfField).Value = ""
End If
Next i
Set targetArea = pointOfStart.Offset(1, 0).Resize(dictStructure.Count, numberOfField * 2)
'セルエラー無効化
For Each targetCell In targetArea
If targetCell.Errors.Item(xlNumberAsText).Ignore = False Then
targetCell.Errors.Item(xlNumberAsText).Ignore = True
End If
Next
'シート再計算
writeSheet.Calculate
'条件付き書式を付与
pointOfStart.Offset(1, 0).Resize(dictStructure.Count, numberOfField).FormatConditions.Delete '条件付き書式を抹消
For i = numberOfField To numberOfField * 2 - 1 '条件付き書式を追加(白から赤へのグラデーション)
'最大値取得ブロック
maxValue = 0
Set targetArea = pointOfStart.Offset(1, i).Resize(dictStructure.Count, 1) '再定義
For Each targetCell In targetArea
If IsNumeric(targetCell.Value) And Not IsEmpty(targetCell.Value) Then
If targetCell.Value > maxValue Then
maxValue = targetCell.Value
End If
End If
Next targetCell
'付与ブロック1
With targetArea.FormatConditions.Add(Type:=xlCellValue, Operator:=xlEqual, Formula1:="=0")
.Interior.Color = RGB(255, 255, 255) ' 白色
End With
'付与ブロック2
With targetArea.FormatConditions.AddColorScale(ColorScaleType:=2)
'最小値の色(白: RGB(255,255,255))
.ColorScaleCriteria(1).Type = xlConditionValueLowestValue
.ColorScaleCriteria(1).FormatColor.Color = RGB(255, 255, 255)
'最大値の色(赤: RGB(255,0,0))
.ColorScaleCriteria(2).Type = xlConditionValueHighestValue
.ColorScaleCriteria(2).FormatColor.Color = RGB(255, 0, 0)
End With
Next i
End Sub
'セル範囲の周りを太めの線で囲むサブルーチン
Sub setBorderAroundRange(ByRef area As Range, Optional ByVal topWeight = xlThick, Optional ByVal bottomWeight = xlThick, Optional ByVal leftWeight = xlThick, Optional ByVal rightWeight = xlThick)
With area.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = topWeight
.Color = RGB(0, 0, 0)
End With
With area.Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = bottomWeight
.Color = RGB(0, 0, 0)
End With
With area.Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = leftWeight
.Color = RGB(0, 0, 0)
End With
With area.Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = rightWeight
.Color = RGB(0, 0, 0)
End With
End Sub
'表の幅を調整するためのサブルーチン
Sub adjustRange(ByRef targetArea As Range, Optional ByVal minHeight As Long = 18, Optional ByVal minWidth As Long = 8.5)
Dim ca As Range, ra As Range, cell As Range
Dim aliveColDict As Object, aliveRowDict As Object
Set aliveColDict = CreateObject("Scripting.Dictionary")
Set aliveRowDict = CreateObject("Scripting.Dictionary")
targetArea.WrapText = False '折り返しを無効にする
targetArea.EntireColumn.AutoFit '文字列の長さに合わせて自動調整
targetArea.EntireRow.AutoFit '文字列の長さに合わせて自動調整
'最低幅を設定
For Each cell In targetArea
If aliveRowDict.exists(cell.Top) = False Then
aliveRowDict.Add cell.Top, False
End If
If aliveColDict.exists(cell.Left) = False Then
aliveColDict.Add cell.Left, False
End If
If IsEmpty(cell) = False Then
If aliveRowDict(cell.Top) = False Then
aliveRowDict(cell.Top) = True
End If
If aliveColDict(cell.Left) = False Then
aliveColDict(cell.Left) = True
End If
End If
Next cell
For Each ca In targetArea.Columns
If ca.ColumnWidth < minWidth Then
ca.ColumnWidth = minWidth
End If
Next ca
For Each ra In targetArea.Rows
If ra.RowHeight < minHeight Then
ra.RowHeight = minHeight
End If
Next ra
End Sub
'棒グラフを生成するサブルーチン
Sub CreateBarChart(ByRef chartRange As Range, ByRef chartSheet As Worksheet, Optional ByVal chartTop As Long = 1, Optional ByVal chartLeft As Long = 1, Optional ByVal sizeHeight As Long = 10, Optional ByVal sizeWidth As Long = 12)
Dim ws As Worksheet
Dim chartOfBar As chartObject
Dim chartTopLeft As Range
Dim series As series
Dim i As Long
'グラフを挿入する左上の起点セルを設定
Set chartTopLeft = chartSheet.Cells.Item(chartTop, chartLeft)
Debug.Print chartTopLeft.Width & " * " & sizeWidth & " = " & chartTopLeft.Width * sizeWidth
'グラフを追加
Set chartOfBar = chartSheet.ChartObjects.Add(Left:=chartTopLeft.Left, Top:=chartTopLeft.Top, Width:=chartTopLeft.Width * sizeWidth, Height:=chartTopLeft.Height * sizeHeight)
' グラフの種類を設定(縦棒グラフ)
With chartOfBar.Chart
.SetSourceData Source:=chartRange
.ChartType = xlColumnClustered ' クラスター縦棒グラフ
.HasTitle = True
.ChartTitle.Text = ""
.Axes(xlCategory).HasTitle = True
.Axes(xlCategory).AxisTitle.Text = "カテゴリ"
.Axes(xlValue).HasTitle = True
.Axes(xlValue).AxisTitle.Text = "件数"
.HasLegend = False
For i = 1 To .SeriesCollection.Count 'どちらにしろ1つだけではあるが、一応定義
Set series = .SeriesCollection(i)
series.Format.Fill.ForeColor.RGB = RGB(255, 0, 0) '赤
' データ系列に数値ラベルを追加
With .SeriesCollection(i)
.HasDataLabels = True
.DataLabels.NumberFormat = "0" '数値のフォーマットを設定(整数)
End With
Next i
End With
End Sub
'全てのグラフを抹消するサブルーチン
Sub deleteAllCharts(chartSheet As Worksheet)
Dim chartObj As chartObject
'ワークシート内のすべてのグラフオブジェクトを削除
For Each chartObj In chartSheet.ChartObjects
chartObj.Delete
Next chartObj
End Sub
Sub test()
Dim dictS As Object, titleArray As Variant
Application.DisplayAlerts = False
Call deleteAllCharts(ThisWorkbook.Sheets("Sheet1"))
Set dictS = CreateObject("Scripting.Dictionary")
dictS.Add "AAA" & Chr(31) & "001" & Chr(31) & "a", 0
dictS.Add "AAA" & Chr(31) & "001" & Chr(31) & "b", 2
dictS.Add "AAA" & Chr(31) & "002" & Chr(31) & "a", 3
dictS.Add "AAA" & Chr(31) & "002" & Chr(31) & "b", 0
dictS.Add "AAA" & Chr(31) & "003" & Chr(31) & "a", 3
dictS.Add "AAA" & Chr(31) & "003" & Chr(31) & "b", 0
dictS.Add "BBB" & Chr(31) & "001" & Chr(31) & "a", 2
dictS.Add "BBB" & Chr(31) & "001" & Chr(31) & "b", 0
dictS.Add "BBB" & Chr(31) & "002" & Chr(31) & "a", 0
dictS.Add "BBB" & Chr(31) & "002" & Chr(31) & "b", 1
dictS.Add "BBB" & Chr(31) & "003" & Chr(31) & "a", 0
dictS.Add "BBB" & Chr(31) & "003" & Chr(31) & "b", 1
dictS.Add "CCC" & Chr(31) & "001" & Chr(31) & "a", 1
dictS.Add "CCC" & Chr(31) & "001" & Chr(31) & "b", 1
dictS.Add "CCC" & Chr(31) & "002" & Chr(31) & "a", 0
dictS.Add "CCC" & Chr(31) & "002" & Chr(31) & "b", 0
dictS.Add "CCC" & Chr(31) & "003" & Chr(31) & "a", 1
dictS.Add "CCC" & Chr(31) & "003" & Chr(31) & "b", 0
With ThisWorkbook.Sheets("Sheet1").Cells.Item(1, 1).Resize(90, 90)
.UnMerge
.Clear
End With
titleArray = Array("基幹", "分散", "個別")
Call writeSummaryTable(dictS, ThisWorkbook.Sheets("Sheet1"), 2, 2, titleArray)
Call adjustRange(ThisWorkbook.Sheets("Sheet1").Cells.Item(1, 1).Resize(50, 50))
Call CreateBarChart(ThisWorkbook.Sheets("Sheet1").Range("B2:E20"), ThisWorkbook.Sheets("Sheet1"), 2, 10, 20, 12)
Application.DisplayAlerts = True
End Sub