I am quite new to VBA. I have written a macro which creates about 10 pivot charts and some normal charts after filtering and cutting some data from a database spreadsheet. I now want to write a sub which goes through applying the same formatting to each one. The sub is as follows:
Sub FormatChart(Cht As ChartObject, title As String)
Cht.Activate
MsgBox ActiveChart.SeriesCollection.Count
With ActiveChart
.HasTitle = True
.ChartTitle.Text = title
.Axes(xlValue).HasMajorGridlines = False
End With
ActiveChart.SeriesCollection(1).Select
With Selection.Format.Fill
.Visible = msoTrue
.ForeColor.RGB = RGB(255, 182, 0)
End With
End Sub
I originally didn't include all the activates and selects, but couldn't get the macro to work without them and don't see it as the end of the world - the datasets are never going to be massive so speed isn't so much of a concern, and I disable screenupdating so that users can't click on cells/other objects and disrupt the macro as it runs.
Here's my problem. If I take out the second With loop, everything proceeds perfectly and the gridlines are removed and the title is added. However, whenever I try to edit the colours of columns with the above I get Run time error '1004': Invalid parameter. I've also tried keeping the content of the second with loop inside the first but then moved it out to try using selection to see if it made a difference.
I've fiddled around quite a bit and recorded various macros changing the colour of the chart in question, but I think the problem might be to do with referencing SeriesCollection as when I try to debug with
MsgBox ActiveChart.SeriesCollection.Count
I get "0".
Please let me know if I'm missing the point - as I said I am new to VBA and am trying to learn as much as possible :)
EDIT: The solution was that I was passing each chart to the above sub after I had created the chart, but before I had set a data source for the chart. Doh!
This obviously meant that there were no seriesCollections for that chart, hence the error I was getting.
I marked Joehannah as answering the question (even though it isn't technically the solution) because it made me check my code and notice that the above could be causing the problem - if I shouldn't do that someone please tell me and I'll try to fix it!
You are better off using Set cht = (create chart object) for each chart and then Immediately calling the format method passing in cht.
I have been told to post my own answer since I figured out the issue.
I was passing each chart to this function just after creating it, as follows:
Set ptr1 = Sheets("withinBRSPivots").PivotTables("UniqueClicks").TableRange1
Set cht1 = Sheets("BRS Overview").ChartObjects.Add(Left:=950, Top:=500, _
Width:=400, Height:=300)
cht1.Name = "UCChart"
Charter.FormatChart cht1, "Average Number of Unqique Clicks"
I then set the source data for the chart after doing the above. This meant that when the chart was passed to my chartformat sub, it had no source data. This is what resulted in being able to edit the title and gridlines, but not the seriesCollection. The fix was to just move the FormatChart sub line to after I had set the source data.
Many thanks to everyone who posted answers!
Related
On occasion you will want to copy a chart in Excel and paste it into a PowerPoint presentation using VBA. This comes in handy when you want to automate a report.
Problem: assuming you are not converting the graph to an image, and depending on the size and complexity of the graph you are trying to paste, the graph does not have time to paste before VBA attempts to continue with the procedure. This is annoying when you want to manipulate the graph after it has been pasted (e.g. positioning it on the slide). What effectively happens is you end up trying to move a chart on the slide... that does not yet 'exist' as a PowerPoint shape.
This issue is annoying and in my humble opinion actually constitutes a bug. I have not yet seen a robust solution to the problem on SO, with the potential exception of this answer from John Peltier. His answer is most likely correct (and has been designated as such on the thread) but unfortunately I could not get this to work in my implementation.
The issue I lay out below.
Code (simplified for ease of reference):
Public Sub X2P()
'Copy chart and paste
ActiveChart.ChartArea.Copy
Set mySlide = myPresentation.Slides(2)
PasteChartIntoSlide mySlide
End Sub
Function PasteChartIntoSlide(theSlide As Object) As Object
CreateObject("PowerPoint.Application").CommandBars.ExecuteMso ("PasteSourceFormatting")
End Function
I see others have tried to break up the script and to copy, paste and position charts using separate functions. This is cleaner, but it has not worked for me.
The solution I have found is to count the number of shapes on the slide before the code pastes the chart, num_obj, and to set variable num_obj_final as num_obj + 1 (i.e. total number of shapes once the chart has been pasted). I then create a loop after the paste event, where I recalculate num_obj with every iteration. Only when num_obj is equal to num_obj_final does the procedure exit the loop. And then the script can resume as expected, as you can be sure that the shape now 'exists' on the slide.
Final code for PasteChartIntoSlide() function:
Function PasteChartIntoSlide(theSlide As Object) As Object
theSlide.Select
num_obj = 0
num_obj_final = theSlide.Shapes.Count + 1
CreateObject("PowerPoint.Application").CommandBars.ExecuteMso ("PasteSourceFormatting")
DoEvents
Do Until num_obj = num_obj_final
num_obj = theSlide.Shapes.Count
'Debug.Print num_obj
Loop
End Function
Question overview:
I am using Excel VBA histogram function from 'Analysis Toolpak' to generate approximately 25 histograms automatically. When Histogram graph is generated, it is placed on top of cells that have values in it, effectively hiding them (Which is OK with me). Therefore a following message is generated "Histogram - some data will be hidden by embedded chart(s)" with "OK" & "Help" buttons. I don't want to press "OK" 25 times whenever I am running this macro.
What I have tried:
Application.DisplayAlerts = False/True is not working out for me. I have tried placing this in variaty of location in my code
Application.ScreenUpdating = False/True
Also tried playing with SetWarning function
Code (1/25):
Dim binrng As Range
Set binrng = Sheets("PSDreport").Range("P4:P64")
Dim outputrng As Range
Set outputrng = Sheets("PSDreport").Range("Q3")
Application.Run "Histogram", inprng, outputrng, binrng, False, False, True, False
My partial solution:
With Application
//CODE GOES HERE//
.SendKeys "{ENTER}"
End With
Problem with my current solution:
Note that all Histogram generating segments of code (1/25) are wrapped around the 'With'. For some reason the first Histogram generated still produces the pop-out (Not good). the remaining 24 successfully skip the pop-outs but the noise of the pop-outs is still produced (slight annoyance).
I am looking for a more elegant way to solve this problem
I had this warning too when I tried to output my embedded chart on the same worksheet that I had my source data (your "imprng"). Once I moved my output range (outputrng) to a different worksheet, the warning stopped.
Bit late to the party but anyone else looking at this should try
With Application
.SendKeys "{ENTER}"
//CODE GOES HERE//
End With
I set out to write a simple function to determine the length of a string in points. Having googled around I decided to avoid the font metrics problem by having excel do the work for me.
Here is the code.
Option Explicit
Function txtWidthPts(MyText As String) As Integer
'A cell on a working worksheet has been named "WidthTest" for easy reference & to ensure data is not overwritten.
'set WidthTest word wrapping off so that strings placed in there aren't wrapped
Application.ScreenUpdating = False
With [WidthTest]
.WrapText = False
.Value = MyText
'autofit WidthTest
.Columns.AutoFit
'get the width of the column
txtWidthPts = .Width
.ClearContents
End With
End Function
I tested the function by placing it in a cell on a working worksheet thus:
=txtWidthPts("Test123")
When I have this working I will be using it in code not as a worksheet function.
My problem is that the function does not throw an error and stops execution on the line:
.Value = MyText
I have placed the code and name into an empty workbook to ensure no interaction with other workbook contents / code.
I have searched extensively and tried various suggestions (DoEvents, Application.Update = False, etc, etc.) to no result.
I have cleared all breakpoints, closed and opened the workbook & restarted. I have tested with options set to Break on All Errors.
No result.
I suspect I am missing something obvious but it has me beat at the moment.
Any and all suggestions will be most welcome.
So, #YowE3K was right on the money. After fixing the error in the original code (Corrected code above) this runs fine from vba. I knew I was missing something obvious.
Curiosity sub-question: the function works as desired and indeed, as #YowE3K observed, it does not modify the Excel environment. However the result returned is dependent on it appearing to have modified the Excel environment. Seriously WTF. Just wanting to understand.
Thanks again YoWE3K.
I am trying to hide and unhide series in a chart based on their name using excel vba and I have a error 1004 invalid parameter after the first run of the for cycle.
Sub macroChart3()
'
' macroChart3 Macro test
'
Dim i, n As Integer
For i = 1 To 96 Step 1
If ActiveChart.SeriesCollection(i).Name = "*contracted*" Then
ActiveChart.SeriesCollection(i).IsFiltered = False
Else
ActiveChart.SeriesCollection(i).IsFiltered = True
End If
Next i
End Sub
SeriesCollection.IsFiltered method seemed to toggle this check box:
I couldn't use that. I wanted to record macro and see if any other method is used but the checkbox is gone in 2010:
So it might be not possible to use that anymore. If you are using a different version where this method exists you might have a problem that the series is not listed in in seriesCollection anymore:
Remarks from MSDN
When a user filters out a series, the series IsFiltered property switches to True, and the series is transferred out of its parent SeriesCollection.
See if you can use FullSeriesCollection instead when you change the visibility of series:
ActiveChart.FullSeriesCollection(2).IsFiltered = True
If that doesn't work you might add and remove ranges instead of hiding them.
UPDATE:
Run your macro in step mode (F8) so you have full visibility of the execution steps. Add your evaluated expressions (ones that are used within IFs) to see their result and you will find out if they are incorrect or are evaluated as FALSE and try to figure out why.
And don't forget to up vote the answer if you find it helpful.
thanks for the quick reply, the error is fixed, but still not all the series are processed by the if clauses in the for cycle, for example this statement:
Else
ActiveChart.SeriesCollection(i).IsFiltered = True
End If
Isn't executed and I don't understand why.
I Know similar questions have been asked, but I still can't figure out this error.
I am working in Access, exporting to excel and used some excel macros to format the data and create charts. I am now trying to put those charts into a Powerpoint, and I am having trouble pasting only one of the four charts I am trying to copy over.
My code to export the third and fourth charts is as follows:
xl.Sheets("Sheet with chart 3").Select
Set rng = xl.ActiveChart
rng.CopyPicture
mySlide.Shapes.PasteSpecial DataType:=ppPasteEnhancedMetafile
Set myShapeRange = mySlide.Shapes(mySlide.Shapes.Count)
myShapeRange.Left = 365
myShapeRange.Top = 200
myShapeRange.Width = 345
xl.Sheets("Sheet with chart 4").Select
Set rng = xl.ActiveChart
rng.CopyPicture
mySlide.Shapes.PasteSpecial DataType:=ppPasteEnhancedMetafile
Set myShapeRange = mySlide.Shapes(mySlide.Shapes.Count)
myShapeRange.Left = 365
myShapeRange.Top = 200
myShapeRange.Width = 345
I get the error "Object variable or With block variable not set (Error 91)" on the second occurrence of the line
rng.CopyPicture
So why would this code fail after running three times prior? I believe I qualified everything correctly, and this code isn't running inside a with block either. Also, The excel sheet it is pulling from is practically identical to the others.
I had been stuck on this for awhile, the problem was caused by this line:
xlSheet.Range("G7").Select
which was the last line during the formatting on the excel book. I had recorded macros in excel to make the charts and pasted the resulting code in Access. I guess having this cell selected was preventing the chart from being set to active, so I couldn't copy it. Hopefully this helps someone having the same silly problem I was!