Nice Bar Chart Data Labels
In Nice Bar Chart Data Labels Using VBA, I showed how to manually make a bar chart with nice data labels, aligned parallel to the bars, in the space above each bar. These labels include the categories and the bar values, so you can clean up the chart by removing axis labels.
Reader Bill McNair asked if I could put together the VBA code to label a bar chart in this way, encapsulating all the steps to make it fast and easy. I thought that was a fine idea, so this article shows how I built a VBA procedure to label a bar chart.
Validate the Active Chart
The code validates the chart at the beginning of the routine. The first step is to check whether there actually is an active chart. Next is to make sure the chart only has one series (this elegant labeling would not work for a chart with more series). Finally make sure the chart is a bar chart; either stacked or clustered works, since they look the same with only one series.
These are the dummy charts I built to test this part of the code.
Here is the code that validates the chart. If any conditions are not met, the code jumps to the ExitSub
label at the end of the procedure.
' validate selected chart
If ActiveChart Is Nothing Then
Dim bIneligibleChart As Boolean
bIneligibleChart = True
ElseIf ActiveChart.SeriesCollection.Count > 1 Then
bIneligibleChart = True
ElseIf ActiveChart.ChartType <> xlBarClustered And _
ActiveChart.ChartType <> xlBarStacked Then
bIneligibleChart = True
End If
If bIneligibleChart Then
MsgBox "Select a bar chart with one series only.", _
vbExclamation, "Select an Eligible Chart"
GoTo ExitSub
End If
Work on a Duplicate Chart
You can’t undo a VBA procedure. To protect the original chart, the code duplicates the active chart and makes changes to the duplicate. The code is simple: a Chart variable named cht
is declared and defined as a duplicate of the active chart.
' work on a duplicate so original chart is not broken
Dim cht As Chart
Set cht = ActiveChart.Parent.Duplicate.Chart
Apply General Chart Formatting
The nice data labels allow us to make a few changes, such removing redundant chart elements.
First, if the chart is a stacked bar chart, it is converted to a clustered bar chart. Then the gap width and overlap of the bars are set to 100% and -10%; these are not properties of the bars but of the ‘chart group’ that contains the bars. The tick labels are removed from both axes and the axis lines are made invisible. The gridlines and legend are removed. Finally, if one exists, the chart title is aligned with the left edge of the plot area. Here is the relevant code, which works on cht
, the chart variable defined above.
With cht
' apply general chart formatting
If .ChartType = xlBarStacked Then .ChartType = xlBarClustered
With .ChartGroups(1)
.GapWidth = 100
.Overlap = -10
End With
With .Axes(xlCategory)
.TickLabelPosition = xlNone
.Format.Line.Visible = msoFalse
End With
With .Axes(xlValue)
.TickLabelPosition = xlNone
.Format.Line.Visible = msoFalse
.MajorGridlines.Delete
End With
If .HasLegend Then .Legend.Delete
If .HasTitle Then
.ChartTitle.Left = .PlotArea.InsideLeft
End If
Add and Format a Duplicate Series
The code copies the formula of the first series of bars, adds a second series of bars, and applies this copied formula to the added series. This means the same data is used for this added series. Since the copied formula is for series 1 in the chart, the added series becomes the first series in the chart, not the second. This new series will be hidden by using no fill color, and the data labels will be attached to this hidden series.
There are implications to the series order. The category axis of any chart can be presented in the original order or in reverse order, as shown below. Most charts use the original category order, but bar charts are different. In the original category order, the categories are in the opposite order as they appear in the worksheet. This is such an important issue that I wrote two articles about it: Excel Plotted My Bar Chart Upside-Down and Why Are My Excel Bar Chart Categories Backwards? The code will have to take this into account,
When the new series is added as series 1, it appears in the position of the orange bars below. If the categories are in reverse order, the new label series is above the data series (below right), which is what we want. But if the categories are in the original order, the label series is below the data series, which is the opposite of what we want.
We have to modify the series formula of the added series, changing the last two characters (encoding the plot order) from “1)” to “2)”. Now the labels will be above the data.
When the series formula is fixed, we declare a Series variable named srs, assign this variable to a new series, then apply the formula to this new series, and apply no fill to the bars. The code is straightforward.
Dim srs As Series
Set srs = .SeriesCollection.NewSeries
With srs
.Formula = sFmla
.Format.Fill.Visible = msoFalse
Add and Format Data Labels
In one command we can add data labels, formatted to show category names and values, separated by a pipe character, and not show the leader lines.
Manually we assigned a label position of Inside Base, but using a simple For-Next
loop we can align each label individually to the left edge of the plot area (which is a weakness of the manual approach). Then we format the textbox of each label so the text does not wrap lines and so the left margin within the textbox is zero. Here is the relevant code.
' add and format data labels
.ApplyDataLabels HasLeaderLines:=False, _
ShowCategoryName:=True, ShowValue:=True, Separator:=" | "
With .DataLabels
.Position = xlLabelPositionInsideBase
Dim iLabel As Long
For iLabel = 1 To .Count
.Item(iLabel).Left = cht.PlotArea.InsideLeft
Next
With .Format.TextFrame2
.WordWrap = msoFalse
.MarginLeft = 0
End With
End With
The Complete Procedure
Let’s put the code together. I’ve added a final command to select the new chart; otherwise the added series is selected, and that didn’t seem as smooth as it should.
Sub ApplyNiceBarChartLabels()
' validate selected chart
If ActiveChart Is Nothing Then
Dim bIneligibleChart As Boolean
bIneligibleChart = True
ElseIf ActiveChart.SeriesCollection.Count > 1 Then
bIneligibleChart = True
ElseIf ActiveChart.ChartType <> xlBarClustered And _
ActiveChart.ChartType <> xlBarStacked Then
bIneligibleChart = True
End If
If bIneligibleChart Then
MsgBox "Select a bar chart with one series only.", _
vbExclamation, "Select an Eligible Chart"
GoTo ExitSub
End If
' work on a duplicate so original chart is not broken
Dim cht As Chart
Set cht = ActiveChart.Parent.Duplicate.Chart
With cht
' apply general chart formatting
If .ChartType = xlBarStacked Then .ChartType = xlBarClustered
With .ChartGroups(1)
.GapWidth = 100
.Overlap = -10
End With
With .Axes(xlCategory)
.TickLabelPosition = xlNone
.Format.Line.Visible = msoFalse
End With
With .Axes(xlValue)
.TickLabelPosition = xlNone
.Format.Line.Visible = msoFalse
.MajorGridlines.Delete
End With
If .HasLegend Then .Legend.Delete
If .HasTitle Then
.ChartTitle.Left = .PlotArea.InsideLeft
End If
' add and format series
Dim sFmla As String
sFmla = .SeriesCollection(1).Formula
If Not .Axes(xlCategory).ReversePlotOrder Then
sFmla = Left$(sFmla, Len(sFmla) - 2) & "2)"
End If
Dim srs As Series
Set srs = .SeriesCollection.NewSeries
With srs
.Formula = sFmla
.Format.Fill.Visible = msoFalse
' add and format data labels
.ApplyDataLabels HasLeaderLines:=False, _
ShowCategoryName:=True, ShowValue:=True, Separator:=" | "
With .DataLabels
.Position = xlLabelPositionInsideBase
Dim iLabel As Long
For iLabel = 1 To .Count
.Item(iLabel).Left = cht.PlotArea.InsideLeft
Next
With .Format.TextFrame2
.WordWrap = msoFalse
.MarginLeft = 0
End With
End With
End With
.ChartArea.Select
End With
ExitSub:
End Sub
Implementing the Code
To use the code as is, open the VB Editor. In the Project Explorer pane, find the workbook where you want to add the code (perhaps your Personal.xlsb
workbook). Insert a module and rename it from Module1
(or whatever number it is assigned) to a more descriptive name, like M_DataLabels
.
Running the Code
The Results
Here is the result of selecting a bar chart with its categories in the original order and running the code. The duplicate chart is in front of the original, offset down and to the right.
Here is the result after running the code on a bar chart with its categories in reverse order.
The above results are for clustered bar charts, but the results were the same when the original charts were stacked bar charts.
The Nice Bar Chart Data Labels Add-In
I encapsulated this code into a VBA add-in. You can download it from this link:
VBA Nice Bar Chart Data Labels Add-In.zip
The download is a zip file that includes the add-in. After you download the file you need to unblock it in Windows. Right-click on the file in Windows File Explorer, choose Properties, check the Unblock box near the bottom of the dialog, and click OK. Now you can unzip the file into a convenient directory.
You don’t have to install the add-in if you don’t want; you can simply double-click on it to open it like any other workbook. If you install it, it will always be available. Whether you install it or open it like a workbook, you will not see a window in Excel. because an add-in’s window is always hidden.
To install the add-in, go to File > Options > Add-Ins. At the bottom of the dialog, make sure Excel Add-Ins is selected in the Manage dropdown. and click Go. In the Add-Ins dialog, click Browse, and find the add-in workbook where you unzipped it. Click OK until you’re back in Excel.
When you select a chart and click on the Chart Design tab of the ribbon, you will see a new button at the far end. Click the button to add the nice data labels to your bar chart.