It is easy to set a datasource for an Excel Chart with Visual Studio, like
Chart.SetSourceData(Source:=SomeRange)
But how do I GET (retrieve) the datasource (Range) for an already exisiting Chart in an Excel file ?
Here's a function that will parse the ranges out of the series. If you have custom series formulas that don't use ranges, it will probably break.
Public Function GetSourceData(ByRef cht As Chart) As Range
Dim srs As Series
Dim vaArgs As Variant
Dim i As Long
Dim rReturn As Range
For Each srs In cht.SeriesCollection
vaArgs = Split(Split(srs.Formula, "SERIES(")(1), ",")
For i = 0 To UBound(vaArgs) - 1
If rReturn Is Nothing Then
Set rReturn = Range(vaArgs(i))
Else
Set rReturn = Union(rReturn, Range(vaArgs(i)))
End If
Next i
Next srs
Set GetSourceData = rReturn
End Function
Use as:
?getsourcedata(activechart).Address
$B$2:$C$2,$A$3:$C$14
I think its been solved here already:
Excel VBA - Get chart data range
https://stackoverflow.com/a/28391220/6868389
The Visual Studio code looks like this:
Dim g As String
Dim gg() As String
Dim a As Excel.ChartObject
a = Globals.ThisAddIn.Application.ActiveSheet.chartobjects(1)
g = a.Chart.SeriesCollection(1).formula
gg = g.Split(",")
MsgBox(gg(2))
Related
I am trying to make a fairly easy macro to activate the next series in an already active chart. (my main macro contains all the formatting stuff).
Problem with this coding, is that i am not able to assign the current active series (already selected in the graph in excel). In a graph with n series (SeriesCollection.Count = n) I want to go from x, to x+1 and if x=n i want to go back to series 1 (so the for/next included here is not necessary if I get the macro to work as intended).
Sub NextButton_Click()
Dim cht As Chart
Set cht = ActiveChart
If cht Is Nothing Then
MsgBox "Select a chart."
Exit Sub
End If
With cht
For SrsIndx = 1 To .SeriesCollection.Count
.SeriesCollection(SrsIndx).Select
Next SrsIndx
End With
End Sub
For Simple graphs, just use PlotOrder: If you are not using a Combo Chart (i.e. not mixing Line and Bar charts, and not using the Secondary Axis), then you can just use the following code:
IIF(TypeName(Selection)="Series", Selection.PlotOrder, -1)
This will return -1 if you do not have a Series selected.
However, this is actually the order within the ChartGroup - the conditions above were for when there is only 1 ChartGroup on the Chart.
Otherwise, try using Name and a loop:
Function ActiveSeriesNumber(ThisSeries AS Series) AS Long
Dim ThisChart AS Chart, TestNumber AS Long
ActiveSeriesNumber = -1
On Error GoTo FunctionError
Set ThisChart = ThisSeries.Parent.Parent 'Object Model Is Chart.ChartGroup.Series
For TestNumber = 1 to ThisChart.SeriesCollection.Count
If ThisChart.SeriesCollection(TestNumber).Name = ThisSeries.Name THen
ActiveSeriesNumber = TestNumber
Exit Function
End If
Next TestNumber
FunctionError:
On Error GoTo -1
End Function
Use this by calling ActiveSeriesNumber(Selection)
Here's the General Idea. Step through that code and you can see each series gets selected. You'll need add some object validation as this assumes the selected object is a Series.
Sub CycleSeries()
Dim MyObject As ChartObject
Dim MyChart As Chart
Dim SerCol As SeriesCollection
Dim SelSeries As Series
Dim indexSeries As Series
Dim ChartSheet As Worksheet
Dim x As Integer
Set ChartSheet = ThisWorkbook.Sheets(1)
Set MyObject = ChartSheet.ChartObjects(1)
Set MyChart = MyObject.Chart
Set SerCol = MyChart.SeriesCollection
Set SelSeries = Excel.Application.Selection
For x = 1 To SerCol.Count
Set indexSeries = SerCol(x)
If indexSeries.Name = SelSeries.Name Then
If (x = SerCol.Count) Then
Set SelSeries = SerCol(1)
SelSeries.Select
Exit For
Else
Set SelSeries = SerCol(x + 1)
SelSeries.Select
Exit For
End If
End If
Next x
End Sub
I need, for some reason, the category names of my chart. Here's what I got so far:
xlWorkbook = xlApp.ActiveWorkbook
Dim wsnat As Excel.Chart = TryCast(xlWorkbook.ActiveChart, Excel.Chart)
If Not wsnat Is Nothing Then
Dim axxxis As Excel.Axis = DirectCast(wsnat.Axes(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary), Excel.Axis)
Dim areyoukiddingme As Object = axxxis.CategoryNames
Dim arr As Array = DirectCast(areyoukiddingme, Array)
For q As Integer = 0 To arr.GetUpperBound(0)
Debug.Print(arr(q).ToString) ' HERE, the array 'arr' has two things which are EMPTY!
Next
End If
My problem is, that the array (arry) has the correct amount of EMPTY objects. If I do the whole thing in VBA, it works as expected. But it does not for VB.net. Any clues?
Here's the code in VBA:
Sub test()
Dim chrt As Chart
Set chrt = ActiveChart
Dim names As Variant
names = chrt.Axes(xlCategory, xlPrimary).CategoryNames
End Sub
This sub nicely outputs the category names of my chart!
I am outputting an excel chart into powerpoint and I need to add a new data series to the Chart, I have recorded the macro to see how to do it, which it requires a range to a series collection but no luck.
Here is the full working example using access 2010 vba, in which at the end I try to add a new data series:
Option Compare Database
Public Sub CreateChart()
Dim myChart As Chart
Dim gChartData As ChartData
Dim gWorkBook As Excel.Workbook
Dim gWorkSheet As Excel.Worksheet
Dim pptApp As Object
Dim pptobj As Object
Set pptApp = CreateObject("Powerpoint.Application")
Set pptobj = pptApp.Presentation.Add
pptobj.Slides.Add 1, ppLayoutBlank
' Create the chart and set a reference to the chart data.
Set myChart = pptobj.Slides(1).Shapes.AddChart.Chart
Set gChartData = myChart.ChartData
' Set the Workbook and Worksheet references.
Set gWorkBook = gChartData.Workbook
Set gWorkSheet = gWorkBook.Worksheets(1)
' Add the data to the workbook.
gWorkSheet.ListObjects("Table1").Resize gWorkSheet.Range("A1:B5")
gWorkSheet.Range("Table1[[#Headers],[Series 1]]").Value = "Items"
gWorkSheet.Range("a2").Value = "Coffee"
gWorkSheet.Range("a3").Value = "Soda"
gWorkSheet.Range("a4").Value = "Tea"
gWorkSheet.Range("a5").Value = "Water"
gWorkSheet.Range("b2").Value = "1000"
gWorkSheet.Range("b3").Value = "2500"
gWorkSheet.Range("b4").Value = "4000"
gWorkSheet.Range("b5").Value = "3000"
' Apply styles to the chart.
With myChart
.ChartStyle = 4
.ApplyLayout 4
.ClearToMatchStyle
End With
' Add the axis title.
With myChart.Axes(xlValue)
.HasTitle = True
.AxisTitle.Text = "Units"
End With
'Add a new data series - TYPE MISMATCH ERROR!!!
myChart.SeriesCollection.NewSeries
myChart.SeriesCollection(2).Name = "New_Series"
myChart.SeriesCollection(2).Values = gWorkSheet.Range("C2:C5") 'Range that is in the worksheet
'myChart.ApplyDataLabels
' Clean up the references.
Set gWorkSheet = Nothing
' gWorkBook.Application.Quit
Set gWorkBook = Nothing
Set gChartData = Nothing
Set myChart = Nothing
End Sub
This example is in the following link: https://msdn.microsoft.com/en-us/library/office/ff973127(v=office.14).aspx
In order to run this code is necessary to import: Visual Basic For Applications, Microsoft access Object Library, OLE Automation, Microsoft office access database engine object, Microsoft Office Object Library, Microsoft Powerpoint Object Library and Microsoft Excel Object Library. (Powerpoint object library should be imported first and then Microsoft Excel Object Library or there are reference problems)
Do you have any idea of how to add a new data series or what could be wrong with the code?
Thanks a lot in advance.
Try it this way:
With myChart.SeriesCollection.NewSeries
.name = "New_Series"
.Values = gWorkSheet.Range("C2:C5").Value2
End With
First you need to reference the newly added series and you cannot just assume that it will have index 2. This method is safer.
Second take the .Value property of the said range, to get an array of values. This fixed it. The default .Value property of the Range object is not guaranteed to work in all circumstances, i.e. when the context or container is not Excel. So it is better to always be explicit when referencing the value of a range.
Somewhat of a VBA newbie here.
It is clear to me how to create a ShapeRange using individual or multiple Shape objects:
Dim sht As Worksheet
Set sht = MySht
'
'*Add some shapes*
'
Dim shprng As ShapeRange
Set shprng = sht.Shapes.Range(Array(1,2,3))
Is there a way to add ALL of the currently existing shapes on a worksheet to shprng? In other words, is there a method to return a ShapeRange from a Shapes object...? Something like this:
Set shprng = sht.Shapes.Range.SelectAll '<--- Does not work: Type Mismatch
Set shprng = sht.Shapes '<--- Same error
Set shprng = sht.Shapes.Range '<--- Error: Argument not optional
Thanks!
If you want to create a ShapeRange by selecting all the shapes on a sheet, you would first select them then get the ShapeRange from the Selection object.
sht.Shapes.Range.SelectAll
Set shprng = Selection.ShapeRange
I usually prefer not to use the Selection object in VBA because it tends to be flaky and can cause errors in weird situations. I think a better way to do this is to build an array of Shape indexes and get the ShapeRange using this array.
Dim shape_index As Variant
Dim i As Long
ReDim shape_index(1 To sht.Shapes.Count)
For i = 1 To UBound(shape_index)
shape_index(i) = i
Next
Set shprng = sht.Shapes.Range(shape_index)
In my Office 365, the code of TmDean did not work. It was necessary explicitly declare of variable as dinamic array.
Dim shape_index() As Variant 'Dim shape_index() as Long
Dim i As Long
ReDim shape_index(1 To sht.Shapes.Count)
For i = 1 To UBound(shape_index)
shape_index(i) = i
Next
Set shprng = sht.Shapes.Range(shape_index)
I have a chart on one of my sheets and I need to change a series in it in code. The problem is that I keep getting the error 1004 message. I've looked around and can't find a reason for it. Here's the code:
Sheets("Charts").ChartObjects(1).Chart.SeriesCollection(1).Formula = "=G49:I" & dblResult & ")"
Are you trying this?
Sheets("Charts").ChartObjects(1).Chart.SeriesCollection(1).Formula = _
"=SERIES(,," & "Charts!G49:I" & dblResult & ",1)"
An alternative that I prefer to manipulating the Series Formula is to just work with the individual properties of the SeriesCollection.
Note I'm writing this from memory/untested so let me know if there's any problems with it!
Inside the With block, you would need to determine the Ranges to use for the Values, XValues, Name, and Order, of course you can omit the parts that you don't need (e.g., I rarely need to manipulate the series .Order)
Dim cht as Chart
Dim srs as Series '# Series variable'
Dim s as Long '# Series iterator'
Dim ws as Worksheet
Set ws = ActiveSheet
Set cht = ws.ChartObjects(1).Chart '## Modify as needed.'
For each srs in cht.SeriesCollection
With srs
s = s+1
.Values = ws.Range("Some_Range_For_Values")
.XValues = ws.Range("Range_For_XValues")
.Name = ws.Range("Range_For_SeriesName")
.Order = s
End With
Next
Practically speaking, here is a pretty simple example. I often build or update a chart dynamically using an approach like this. Assuming that XValues are in column A, while series data is in columns B:F, you could do something like:
Dim rngData as Range '# A range containing all of the series values & xValues'
Dim s as Long
Dim cht as Chart
Dim srs as Series
Dim ws as Worksheet
Set ws = ActiveSheet
Set cht = ws.ChartObjects(1).Chart
Set rngData = Range("A2:F10")
'## I like to remove existing series, and then add in the new data. '
For each srs in cht.SeriesCollection
srs.Delete
Next
'## Iterate over our range and add series back in to the chart.'
For s = 2 to rngData.Columns.Count
Set srs = cht.NewSeries
With srs
.XValues = rngData.Columns(1).Address
.Values = rngData.Columns(s).Address
'Name = rngData.Cells(1,s).Offset(-1,0).Value
'Order = s-1
End With
Next