I have a chart series using one color (it can be an msoThemeColorAccent<#> or any other) for the marker line, another color for the marker fill (it can be an msoThemeColorAccent<#>, lighter x%, or any other), and no lines. I want to determine if the chart series is using an msoThemeColorAccent<#> for the marker line, an msoThemeColorAccent<#>, lighter x%, for the marker fill, and if so which specific accent color in each case.
I tried returning some properties of a series, with no luck. At a breakpoint during execution of a Sub that has a reference sr to a chart series (which was, for testing purposes, set to have lines, with its color being the same as marker lines and marker fill, set at Accent1), and inside a With sr construction, I tested in the immediate window:
? .Format.Line.BackColor.ObjectThemeColor, .Format.Line.ForeColor.ObjectThemeColor
0 0
? .Format.Fill.BackColor.ObjectThemeColor, .Format.Fill.ForeColor.ObjectThemeColor
0 -2
I expect to obtain in some way a value of msoThemeColorAccent1 (=5), which is used in my series. Is there any way to do this?
All the examples that I found used a msoThemeColorIndex for setting a color, none as a value that was read.
PS: I did not succeed in this first part, with a series with uniform color. I foresee that if I overcome this hurdle, I may still have problems in detecting the correct TintAndShade, or even the accent color in the target case of no line, different marker fill vs. marker line.
So this is interesting. I create a line chart using all defaults, and then I run this procedure:
Sub getLineCOlors()
Dim cht As Chart
Dim srs As Series
Dim colors As String
Dim pt As Point
Set cht = ActiveSheet.ChartObjects(1).Chart
For Each srs In cht.SeriesCollection
With srs.Format.Line
colors = colors & vbCrLf & srs.Name & " : " & _
.ForeColor.RGB
End With
Next
Debug.Print "Line Colors", colors
End Sub
The Immediate window then displays:
Line Colors
Series1 : 16777215
Series2 : 16777215
Series3 : 16777215
But this is clearly not the case. It is obvious that they all are different colors. If, instead of .RGB I do .ObjectThemeColor, then I get all 0, which is equally and demonstrably false by observing the chart!
Line Colors
Series1 : 0
Series2 : 0
Series3 : 0
Now here is where it gets interesting:
If, after having created the chart I change the series colors (or even leave them unchanged, by assigning to the same ThemeColors), then the function shows valid RGBs:
Line Colors
Series1 : 5066944
Series2 : 12419407
Series3 : 5880731
It is as if Excel (and PowerPoint/etc.) are completely unable to recognize the automatically assigned colors, on Line Charts. Once you assign a color, then it may be able to read the color.
NOTE: Line charts are picky, because you don't have a .Fill, but rather a .Format.Line.ForeColor (and .BackColor) and IIRC there are some other quirks, too, like you can select an individual point and change it's fill color, and then that affects the visual appearance of the preceding line segment, etc...
Is this limited to line charts? Perhaps. My past experience says "probably", although I am not in a position to say that this is a bug, it certainly seems to be a bug.
If I run a similar procedure on a Column Chart -- again using only the default colors that are automatically assigned,
Sub getCOlumnColors()
Dim cht As Chart
Dim srs As Series
Dim colors As String
Dim pt As Point
Set cht = ActiveSheet.ChartObjects(2).Chart
For Each srs In cht.SeriesCollection
With srs.Format.Fill
colors = colors & vbCrLf & srs.Name & " : " & _
.ForeColor.RGB
End With
Next
Debug.Print "Column Colors", colors
End Sub
Then I get what appear to be valid RGB values:
Column Colors
Series1 : 12419407
Series2 : 5066944
Series3 : 5880731
HOWEVER: It still doesn't recognize a valid ObjectThemeColor. If I change .RGB then this outputs:
Column Colors
Series1 : 0
Series2 : 0
Series3 : 0
So based on these observations, there is certainly some inability to access the ObjectThemeColor and/or .RGB property of automatically-assigned color formats.
As Tim Williams confirms, this was a bug as far back as 2005 at least as it pertains to the RGB, and probably that bug carried over in to Excel 2007+ with the ObjectThemeColor, etc... It is not likely to be resolved any time soon then, so we need a hack solution :)
UPDATED SOLUTION
Combine the two methods above! Convert each series from line to xlColumnClustered, then query the color property from the .Fill, and then change the series chart type back to its original state. This may be more reliable than trying to exploit the sequential indexing (which will not be reliable at all if the users have re-ordered the series, e.g., such that "Series1" is at index 3, etc.)
Sub getLineColors()
Dim cht As Chart
Dim chtType As Long
Dim srs As Series
Dim colors As String
Set cht = ActiveSheet.ChartObjects(1).Chart
For Each srs In cht.SeriesCollection
chtType = srs.ChartType
'Temporarily turn this in to a column chart:
srs.ChartType = 51
colors = colors & vbCrLf & srs.Name & " : " & _
srs.Format.Fill.ForeColor.RGB
'reset the chart type to its original state:
srs.ChartType = chtType
Next
Debug.Print "Line Colors", colors
End Sub
credit: David Zemens
It is one of solutions DeerSpotter.
But after you change your chart type to some temp ChartType and then changing it back to original one. You are losing some format. For an example, you get second axis after it, what is not always need.
So, when your chart line color set automatically, you can not simply get color index of it. You would always receive 0 or 16777215 - white color.
The best solution is to just before you try to change or get your chart line color, to do this little trick with Line.Visible tur off and then trun on. After it you can simply read and chnage your line color.
ppSeries.Format.Line.Visible = msoFalse
ppSeries.Format.Line.Visible = msoTrue
Related
I am trying create a chart using cell colors from the active cells on a specific worksheet. When using the macro provided below I find that only some of the assigned cell RGB color codes match to the chart. I am not sure why some colors would match and some would not. The correct colors display in the chart when I manually enter the color codes. Is there something I am leaving out of this macro or an extra step I need to take?
I am using Excel 2016 for this project.
Sub ColorChartColumnsbyCellColor()
With Sheets("Sheet1").ChartObjects(1).Chart.SeriesCollection(1)
Set vAddress = ActiveSheet.Range(Split(Split(.Formula, ",")(1), "!")(1))
For i = 1 To vAddress.Cells.Count
.Points(i).Format.Fill.ForeColor.RGB =
ThisWorkbook.Colors(vAddress.Cells(i).Interior.ColorIndex)
Next i
End With
End Sub
You're assigning a color index to the RGB property. Color indexes have nothing to do with Red Green Blue. Besides, #Tim William's has a point: conditional formatting may play a role in what you're doing.
Try this code, which assigns the Color property to the RGB property:
Sub ColorChartColumnsbyCellColor()
With Sheets("Sheet1").ChartObjects(1).Chart.SeriesCollection(1)
Set vAddress = ActiveSheet.Range(Split(Split(.Formula, ",")(1), "!")(1))
For i = 1 To vAddress.Cells.Count
'Comment the line below and uncomment the next one to take conditional formatting into account.
.Points(i).Format.Fill.ForeColor.RGB = vAddress.Cells(i).Interior.Color
'.Points(i).Format.Fill.ForeColor.RGB = vAddress.Cells(i).DisplayFormat.Interior.Color
Next i
End With
End Sub
I need to fill a cell with different colors as in this picture (3 rows are merged vertically and colors are drawn manually in this picture using 3 rectangular shapes):
The only way I could find to fill part of a cell is using conditional formatting (by setting style as data bar and fill as solid) but it support only one color.
Is this possible with or without VBA?
It is possible.
I have found two ways to do that.
1- Using a black square shaped character (character code 2588 – vba: ActiveSheet.Cells(1, 1) = ChrW(&H2588)) and color them according to percentage. This character fills the cell height and also there is no spacing between them which allows filling a cell completely (Sure you should consider left indent in a cell). Only issue here that you cannot use a lot of characters in one cell; I use 30 of them and scale the number of characters according to 30 (ie. 50% red means 15 red character-2588).
2- It is same as what #Doktor Oswaldo has suggested: Inserting a plot in a cell using cell's position and size in pixels. This method has one big advantage: you can show the ratios exactly. In addition, you can fill a data series with a pattern as well. However if you have a lot of plots, you will sacrifice from Excel performance. For plot settings, I use following VBA code:
'Define var's
Dim src As Range, targetCell As Range
Dim chacha As ChartObject
'Set var's
Set src = Worksheets("Sheet1").Range("B1:B3")
Set targetCell = Worksheets("Sheet1").Range("C2")
'Create plot at the target cell
Set chacha = Sheets("Sheet1").ChartObjects.Add(targetCell.Left, targetCell.Top, targetCell.Width, targetCell.Height)
'Change plot settings to fill the cell
With chacha.Chart
.ChartType = xlBarStacked
.SetSourceData Source:=src, PlotBy:=xlRows
.Axes(xlValue).MinimumScale = 0
.Axes(xlValue).MaximumScale = 100
.Axes(xlCategory).Delete
.Axes(xlValue).Delete
.Legend.Delete
.PlotArea.Top = -50
.PlotArea.Left = -50
.PlotArea.Width = targetCell.Width
.PlotArea.Height = targetCell.Height
.ChartGroups(1).GapWidth = 0
End With
chacha.Chart.SeriesCollection(1).Format.Fill.ForeColor.RGB = RGB(255, 0, 0)
chacha.Chart.SeriesCollection(2).Format.Fill.ForeColor.RGB = RGB(0, 0, 255)
chacha.Chart.SeriesCollection(3).Format.Fill.ForeColor.RGB = RGB(255, 255, 0)
In the code I modified the series colors manually which can also be automatized. Following is the screenshot of both methods. The Cell "C1" is filled with block characters and "C2" is a chart.
Note: You might get an error at the line ".PlotArea.Top". To solve this issue, please check: Error setting PlotArea.Width in Excel, VBA (Excel 2010)
I am working with a pie chart and a legend in Excel 2003.
The legend entries are composed of strings like this:
75% Ice Cream
20% Brownies
5% Gummy Bears
I am trying to put the exposure percentage in bold but leave the rest of the series name (Ice Cream, Brownies, or Gummy Bears) in regular font.
Is it possible to do this?
So far I have been working with variations on this code. In addition, I have tried using the Split() function on the SeriesCollection object and even recording a macro to see what Excel would generate in VBA. Thus far I can only get the text to appear in all bold, or all regular font, and not a mix of the two.
For x = 1 To 3
myChartObject.Chart.Legend.LegendEntries(x).Font.Bold = True
Next x
Suggestions would be helpful.
I didn't catch the fact that you're working in a chart, but hopefully the below can help. If you can get the characters, then you can bold certain parts of a string. (Assuming your column A has a cell with 20% Brownies, the next cell 75% Ice Cream, etc.)
Sub boldPercent()
Dim i&, lastRow&, percentLength&, percentAmt$
Dim k&
lastRow = Cells(Rows.Count, 1).End(xlUp).Row ' Assuming your data is in column A
For i = 1 To lastRow
percentAmt = Left(Cells(i, 1), WorksheetFunction.Search("%", Cells(i, 1)))
percentLength = Len(percentAmt)
With Cells(i, 1).Characters(Start:=1, Length:=percentLength)
.Font.Bold = True
End With
Next i
End Sub
So perhaps you can use that and tweak it to work with the chart area? Have VBA loop through your chart titles, and perhaps you can use the same method above.
Edit: I'm making a mock example chart to try and work on this - but how are you getting the percentages of each category into the Legend? I have set up a super simple chart, but don't know where you went from here (screenshot)
(I'm expecting your legend to say 75% Ice Cream, 20% Brownies, etc. right?)
Edit2: Okay, I have moved into using the Chart object, hoping to grab each Legend Entry, and would feather in the bolding of characters as I did above...however, I can't get legendStringever to be a non-empty string:
Sub Bold_Legend_Text()
Dim stringToFind$
Dim cObj As ChartObject
Dim legEnt As LegendEntry
Dim cht As Chart
Dim i&
Dim percentLength&
Dim legendString$
stringToFind = "%"
For Each cObj In ActiveSheet.ChartObjects
Set cht = cObj.Chart
With cht
If .HasLegend Then
Debug.Print .Legend.LegendEntries.Count
For Each legEnt In .Legend.LegendEntries
' This always returns an empty string, not sure why!
legendString = legEnt.Format.TextFrame2.TextRange.Characters.Text
Debug.Print legendString
' Then we'd find where "%" shows up in the Legend title, and try to bold
' just certain characters
Next legEnt
End If
Next cObj
End Sub
(Thanks to this thread)
I am making a line graph (chart) in Excel with several data series being plotted onto the same chart.
I need to create a macro/VBA solution that can turn the visibilty of these series on/off via the pressing of a button (or tick box etc)
Similar to this picture (manually done through the excel menu system)
I have tried to look through all the member vars/methods on
https://msdn.microsoft.com/EN-US/library/office/ff837379.aspx
but haven't had much luck.
I have tried playing around with bits like
Charts("Chart1").SeriesCollection(1)
and
Worksheets("Graphical Data").ChartObjects(1)
but I can neither get the chart object ( I get a subscript out of range error) nor able to find any method that would allow me to turn on/off the visibility of individual series.
Any Ideas?
Whenever I don't know how to do something like this, I turn on the macro recorder.
I had a chart with four series, and I used the filter function in Excel 2013 to hide and show the second series, while the macro recorder was running.
Here's the relevant code:
ActiveChart.FullSeriesCollection(2).IsFiltered = True
' series 2 is now hidden
ActiveChart.FullSeriesCollection(2).IsFiltered = False
' series 2 is now visible
The series type (line or column) does not matter, this works for any of them.
I believe the property you are looking for is the SeriesCollection.Format.Line.Visible property. I quickly created an Excel workbook and added a simple data set (just 1-10) and added a line graph "Chart 2" to the sheet Sheet1.
This code turned the visibility of the line off:
Option Explicit
Private Sub Test()
Dim cht As Chart
Dim ser As Series
'Retrieve our chart and seriescollection objects'
Set cht = Worksheets("Sheet1").ChartObjects("Chart 2").Chart
Set ser = cht.SeriesCollection(1)
'Set the first series line to be hidden'
With ser.Format.Line
.Visible = msoFalse
End With
End Sub
And likewise, setting the ser.Format.Line.Visible property to msoTrue made the line visible again.
As for retrieving the chart itself I had to first activate it, then set my cht variable to the ActiveChart. To view the name of your chart, select it and look in the name box (near where you would enter the cell value / formula).
Update
When using the method above, the series name remains in the legend box. I couldn't find a visibility property for the SeriesCollection in the legend, however one workaround is to simply re-name the series as an empty string (this will make the series disappear from the legend) and then rename the series when you want to show it.
This code below will toggle the visibility of the line and series name in the legend.
Option Explicit
Private Sub Test()
Dim cht As Chart
Dim ser As Series
'Retrieve our chart and seriescollection objects'
Set cht = Worksheets("Sheet1").ChartObjects("Chart 1").Chart
Set ser = cht.SeriesCollection(1)
'Set the first series line to be hidden'
With ser.Format.Line
If .Visible = msoTrue Then
.Visible = msoFalse
ser.Name = vbNullString
Else
.Visible = msoTrue
ser.Name = "Series 1"
End If
End With
End Sub
And, whenever you use .Format.Line.Visible = msoTrue just remember to set ser.Name back to whatever the name for your series is.
There is a simple way to on & off the visibility of the series: using filter on your source data.
May it help you easily as follows.
You can insert a new Window. Setone of them to source data sheet and the other window to Chart sheet. Then arrange the two windows to see both at the same time. Now if you filter the series you like on the source data sheet simultaneously you will see the series you desired on the other sheet.
Problem
I would like to know how to read the current RGB value of an automatically assigned color in a chart, even if this entails freezing the colors to their current values (rather than updating them as the theme is changed, series are reordered, etc.)
Usecase
My actual usecase is that I would like to make the datalabels match the color of the lines/markers in a line chart. This is easy if I have explicitly set the colors of the series via a scheme or explicit RGB values, e.g.
' assuming ColorFormat.Type = msoColorTypeRGB
s.DataLabels.Format.TextFrame2.TextRange.Font.Fill.ForeColor.RGB= _
s.Format.Line.ForeColor.RGB
However, doing this when the series color is assigned automatically results in white labels. More specifically, both of the following equalities hold
s.Format.Line.ForeColor.Type = msoColorTypeRGB
s.Format.Line.ForeColor.RGB = RGB(255,255,255) ' White
And yet the line of course isn't white, but is an automatically assigned color from the theme. This shows that the color is automatically assigned
s.Border.ColorIndex = xlColorIndexAutomatic
I suppose it makes sense that the color isn't stored with the series in question. Even storing the index into the color scheme wouldn't generally work as Excel needs to change the color if another data series is added or someone reorders the data. Still, I would love it if there were some way to identify the current RGB value automatically.
An Ugly Workaround
For charts with 6 or fewer entries, a simple workaround is to exploit the fact that theme colors are assigned sequentially, so I can do (e.g.)
chrt.SeriesCollection(1).DataLabels.Format.TextFrame2.TextRange.Font.Fill.ForeColor.ObjectThemeColor _
= msoThemeColorAccent1
Presumably this could be extended to account for the TintAndShade used to differentiate entries once the theme has been exhausted, but this is such an ugly hack.
Research
Someone asked essentially the same question (how to extract theme colors) here, but it was never answered. There are several sources suggesting ways to convert a known theme color into RGB values (e.g. here and here) but that just begs the question; I don't know the color a priori, other than "whatever color this line currently is."
So this is interesting. I create a line chart using all defaults, and then I run this procedure:
Sub getLineCOlors()
Dim cht As Chart
Dim srs As Series
Dim colors As String
Dim pt As Point
Set cht = ActiveSheet.ChartObjects(1).Chart
For Each srs In cht.SeriesCollection
With srs.Format.Line
colors = colors & vbCrLf & srs.Name & " : " & _
.ForeColor.RGB
End With
Next
Debug.Print "Line Colors", colors
End Sub
The Immediate window then displays:
Line Colors
Series1 : 16777215
Series2 : 16777215
Series3 : 16777215
But this is clearly not the case. It is obvious that they all are different colors. If, instead of .RGB I do .ObjectThemeColor, then I get all 0, which is equally and demonstrably false by observing the chart!
Line Colors
Series1 : 0
Series2 : 0
Series3 : 0
Now here is where it gets interesting:
If, after having created the chart I change the series colors (or even leave them unchanged, by assigning to the same ThemeColors), then the function shows valid RGBs:
Line Colors
Series1 : 5066944
Series2 : 12419407
Series3 : 5880731
It is as if Excel (and PowerPoint/etc.) are completely unable to recognize the automatically assigned colors, on Line Charts. Once you assign a color, then it may be able to read the color.
NOTE: Line charts are picky, because you don't have a .Fill, but rather a .Format.Line.ForeColor (and .BackColor) and IIRC there are some other quirks, too, like you can select an individual point and change it's fill color, and then that affects the visual appearance of the preceding line segment, etc...
Is this limited to line charts? Perhaps. My past experience says "probably", although I am not in a position to say that this is a bug, it certainly seems to be a bug.
If I run a similar procedure on a Column Chart -- again using only the default colors that are automatically assigned,
Sub getCOlumnColors()
Dim cht As Chart
Dim srs As Series
Dim colors As String
Dim pt As Point
Set cht = ActiveSheet.ChartObjects(2).Chart
For Each srs In cht.SeriesCollection
With srs.Format.Fill
colors = colors & vbCrLf & srs.Name & " : " & _
.ForeColor.RGB
End With
Next
Debug.Print "Column Colors", colors
End Sub
Then I get what appear to be valid RGB values:
Column Colors
Series1 : 12419407
Series2 : 5066944
Series3 : 5880731
HOWEVER: It still doesn't recognize a valid ObjectThemeColor. If I change .RGB then this outputs:
Column Colors
Series1 : 0
Series2 : 0
Series3 : 0
So based on these observations, there is certainly some inability to access the ObjectThemeColor and/or .RGB property of automatically-assigned color formats.
As Tim Williams confirms, this was a bug as far back as 2005 at least as it pertains to the RGB, and probably that bug carried over in to Excel 2007+ with the ObjectThemeColor, etc... It is not likely to be resolved any time soon then, so we need a hack solution :)
UPDATED SOLUTION
Combine the two methods above! Convert each series from line to xlColumnClustered, then query the color property from the .Fill, and then change the series chart type back to its original state. This may be more reliable than trying to exploit the sequential indexing (which will not be reliable at all if the users have re-ordered the series, e.g., such that "Series1" is at index 3, etc.)
Sub getLineColors()
Dim cht As Chart
Dim chtType As Long
Dim srs As Series
Dim colors As String
Set cht = ActiveSheet.ChartObjects(1).Chart
For Each srs In cht.SeriesCollection
chtType = srs.ChartType
'Temporarily turn this in to a column chart:
srs.ChartType = 51
colors = colors & vbCrLf & srs.Name & " : " & _
srs.Format.Fill.ForeColor.RGB
'reset the chart type to its original state:
srs.ChartType = chtType
Next
Debug.Print "Line Colors", colors
End Sub
Here is the code I used in the end.
Sub ShowSeries()
Dim mySrs As Series
Dim myPts As Points
Dim chtType As Long
Dim colors As String
With ActiveSheet
For Each mySrs In ActiveChart.SeriesCollection
'Add label
Set myPts = mySrs.Points
myPts(myPts.Count).ApplyDataLabels ShowSeriesName:=True, ShowValue:=False
'Color text label same as line color
'if line has default color
If mySrs.Border.ColorIndex = -4105 Then
chtType = mySrs.ChartType
'Temporarily turn this in to a column chart:
mySrs.ChartType = 51
mySrs.DataLabels.Format.TextFrame2.TextRange.Font.Fill.ForeColor.RGB = _
mySrs.Format.Fill.ForeColor.RGB
'reset the chart type to its original state:
mySrs.ChartType = chtType
'if line has a color manually changed by user
Else
mySrs.DataLabels.Font.ColorIndex = mySrs.Border.ColorIndex
End If
Next
End With
End Sub
After half a day I managed to solve this issue:
Sub ......()
Dim k as Integer
Dim colorOfLine as Long
...............
.................
'Loop through each series
For k = 1 To ActiveChart.SeriesCollection.Count
With ActiveChart.FullSeriesCollection(k)
.HasDataLabels = True
'Put a fill on datalabels
.DataLabels.Format.Fill.Solid
'Get color of line of series
colorOfLine = .Format.Line.ForeColor.RGB
'Assign same color on Fill of datalabels of series
.DataLabels.Format.Fill.ForeColor.RGB = colorOfLine
'white fonts in datalabels
.DataLabels.Format.TextFrame2.TextRange.Font.Fill.ForeColor.RGB = RGB(255, 255, 255)
End With
Next k
..........
End Sub