vba programming charts

11
VBA Code to Add Chart Objects and Series Add a Chart When you record a macro to add a chart object to a worksheet, Excel comes up with the following code: Sub RecordedAddChartObject() ' ' RecordedAddChartObject Macro ' Macro recorded 5/2/02 by Jon Peltier ' Charts.Add ActiveChart.ChartType = xlXYScatterLines ActiveChart.SetSourceData Source:=Sheets("Sheet1").Range("A3:G14") ActiveChart.Location Where:=xlLocationAsObject, Name:="Sheet1" End Sub Excel uses Charts.Add followed later by ActiveChart.Location to create a chart object, and uses ActiveChart.SetSourceData to set all the series data in one shot. The coding is efficient in terms of the small length of the code, but inflexxible in terms of your control over the output. In my examples I use ChartObjects.Add, which also requires (or allows) me to state the position and size of the chart. This example does almost exactly what the recorded macro above does: Sub AddChartObject() ' With ActiveSheet.ChartObjects.Add _ (Left:=100, Width:=375, Top:=75, Height:=225) .Chart.SetSourceData Source:=Sheets("Sheet1").Range("A3:G14") .Chart.ChartType = xlXYScatterLines End With End Sub The difference in the charts produced by the recorded macro and by the amended code are slight. The amended code positions and sizes the chart according to (Left:=100, Width:=375, Top:=75, Height:=225), where these dimensions are in pixels. The Chart Wizard creates a chart roughly half as wide and half as tall as the visible part of the worksheet window, centered within the window (if you have frozen panes in the sheet, the chart is half the size of the active pane, subject to certain minimum dimensions). Errors in recorded macros

Upload: debi

Post on 12-Nov-2015

4 views

Category:

Documents


0 download

DESCRIPTION

VBA Programming

TRANSCRIPT

VBA Code to Add Chart Objects and SeriesAdd a ChartWhen you record a macro to add a chart object to a worksheet, Excel comes up with the following code:Sub RecordedAddChartObject()'' RecordedAddChartObject Macro' Macro recorded 5/2/02 by Jon Peltier' Charts.Add ActiveChart.ChartType = xlXYScatterLines ActiveChart.SetSourceData Source:=Sheets("Sheet1").Range("A3:G14") ActiveChart.Location Where:=xlLocationAsObject, Name:="Sheet1"End SubExcel usesCharts.Addfollowed later byActiveChart.Locationto create a chart object, and usesActiveChart.SetSourceDatato set all the series data in one shot. The coding is efficient in terms of the small length of the code, but inflexxible in terms of your control over the output.In my examples I useChartObjects.Add, which also requires (orallows) me to state the position and size of the chart. This example does almost exactly what the recorded macro above does:Sub AddChartObject()' With ActiveSheet.ChartObjects.Add _ (Left:=100, Width:=375, Top:=75, Height:=225) .Chart.SetSourceData Source:=Sheets("Sheet1").Range("A3:G14") .Chart.ChartType = xlXYScatterLines End WithEnd SubThe difference in the charts produced by the recorded macro and by the amended code are slight. The amended code positions and sizes the chart according to(Left:=100, Width:=375, Top:=75, Height:=225), where these dimensions are in pixels. The Chart Wizard creates a chart roughly half as wide and half as tall as the visible part of the worksheet window, centered within the window (if you have frozen panes in the sheet, the chart is half the size of the active pane, subject to certain minimum dimensions).Errors in recorded macrosThere are some instances when a recorded macro will contain code that will not work properly. For example, a recorded macro always putsActiveChart.ChartTypeahead ofActiveChart.SetSourceData, but I have reversed them in the code above. The steps given by the recorder work fine for most chart types, but a few chart types cannot be correctly assigned until the chart has been populated with sufficient data. In particular, code which createsstock chartsandbubble chartswill fail if you do not reverse the order of the steps.When working with asurfaceorcontour chart, Excel will let you manually delete a series in the chart (via the Source Data dialog, because you cannot select a single series in such a chart). You can record a macro to see the steps you took. When you try to run the macro, however, it will crash on the.Deleteline in the macro. In most chart types the series can be independently formatted, and they don't even have to be the same type, leading to the ability to create custom combination charts. The problem with surface or contour charts is that their series are not treated by VBA as independent series. The trick in this case is to temporarily convert the surface chart to another chart type, say, a line chart, delete the series, then convert back to a surface chart.In what I think of as "Marker charts", that is,XYandLine charts, VBA cannot access certain series properties, including.Values,.XValues, and.Formulaif the source range of the series contains no chartable data (i.e., it consists of blanks or errors). This severe inconvenience can be avoided if you change the series type to an area or column chart type before accessing the forbidden properties, then change it back to a Line or XY chart series.Inefficiencies in recorded macrosAside from error-raising macro problems, a recorded macro is less efficient, because it mimics all the mouseclicks and keystrokes (every cough and camera flash) that occurred while the recording was taking place. A recorded macro clicks on every object to select it, then performs an action on the selection: ActiveChart.Axes(xlValue).Select Selection.TickLabels.Font.Bold = True Selection.TickLabels.NumberFormat = "0.0"Streamline your code by replacing all theObject.SelectplusSelect.Propertysequences with shorterObject.Propertystatements: ActiveChart.Axes(xlValue).TickLabels.Font.Bold = True ActiveChart.Axes(xlValue).TickLabels.NumberFormat = "0.0"If you have two or more property or method statements that work on the same object, wrap them in aWith/End Withblock: With ActiveChart.Axes(xlValue).TickLabels .Font.Bold = True .NumberFormat = "0.0" End WithThere are other inefficiencies specific to charting, such as theCharts.Add . . . ActiveChart.Locationsequence which can be shortened to a singleChartObjects.Addcommand, as discussed earlier in this page.

You may find it more convenient, in a longer procedure, to define some object variables. The next procedure does the same as the two above, but it uses a ChartObject variable for the new chart object that we create. If we need to refer to this chart object later in the procedure, we can conveniently use the variablemyChtObj.Sub AddChartObject()Dim myChtObj As ChartObject' Set myChtObj = ActiveSheet.ChartObjects.Add _ (Left:=100, Width:=375, Top:=75, Height:=225) myChtObj.Chart.SetSourceData Source:=Sheets("Sheet1").Range("A3:G14") myChtObj.Chart.ChartType = xlXYScatterLinesEnd SubYou are not limited to usingSetSourceDatato define the data being charted. You can add the series one-by-one, selecting the precise data you want, not what Excel will assume you want.Top of PageAdd a SeriesThe following is a macro I recorded while adding a series using the Add command in the Source Data dialog:Sub RecordedAddSeries()'' RecordedAddSeries Macro' Macro recorded 5/2/02 by Jon Peltier' ActiveChart.SeriesCollection.NewSeries ActiveChart.SeriesCollection(6).XValues = "=Sheet1!R4C1:R14C1" ActiveChart.SeriesCollection(6).Values = "=Sheet1!R4C7:R14C7" ActiveChart.SeriesCollection(6).Name = "=Sheet1!R3C7"End SubThis is basically my preferred syntax, although the following has been neatened up in a few ways. First, the index of the series is not mentioned in the code, so it's more readily reused. Second, I can use the familiar A1 cell address notation, or any VBA range reference technique.Sub AddNewSeries() With ActiveChart.SeriesCollection.NewSeries .Name = ActiveSheet.Range("G3") .Values = ActiveSheet.Range("G4:G14") .XValues = ActiveSheet.Range("A4:A14") End WithEnd SubAnd as shown below, this is very flexible. For example, you can adjust the code to insert a string for the series name, a VBA array for X or Y values (X values in the following code), or a reference to a defined range name (Y_Rangefor Y values).Sub AddNewSeries() With ActiveChart.SeriesCollection.NewSeries .Name = "Fred" .Values = "=Sheet1!Y_Range" .XValues = Array(1, 2, 3) End WithEnd SubAs with the chart object variable above, you can define an object variable for the new chart series being added. The following procedure assigns the variableMyNewSrsto the new chart series it creates.Sub AddNewSeries() Dim MyNewSrs As Series Set MyNewSrs = ActiveChart.SeriesCollection.NewSeries With MyNewSrs .Name = "Fred" .Values = "=Sheet1!Y_Range" .XValues = Array(1, 2, 3) End WithEnd SubTop of PageToo Many Series?When you create a chart, Excel looks at the selection, and tries to determine how many series you want in the chart. In the Chart Wizard, you see this behavior in step 2, where the Data Range is tentatively filled in for you. When creating a chart in code, you don't get this chance to make it right, and your chart may have any number of series. It is best to clear out all of these initial series, and start from scratch with the series you intend to add. This macro clears the chart:Sub RemoveUnwantedSeries() With ActiveChart Do Until .SeriesCollection.Count = 0 .SeriesCollection(1).Delete Loop End WithEnd SubThe chart now appears completely blank, and the only object you can currently access is the chart area. But you can now add series as shown above.Top of PagePutting It All TogetherLet's put the last few steps together, to produce a robust little procedure that will create a chart sheet with exactly the right number of series in the right place, using the selected range as the chart's data source. The first row contains the series labels, the first column contains the X values, and the rest of the columns contain the Y values for each series.Sub EmbeddedChartFromScratch() Dim myChtObj As ChartObject Dim rngChtData As Range Dim rngChtXVal As Range Dim iColumn As Long

' make sure a range is selected If TypeName(Selection) "Range" Then Exit Sub

' define chart data Set rngChtData = Selection

' define chart's X values With rngChtData Set rngChtXVal = .Columns(1).Offset(1).Resize(.Rows.Count - 1) End With

' add the chart Set myChtObj = ActiveSheet.ChartObjects.Add _ (Left:=250, Width:=375, Top:=75, Height:=225) With myChtObj.Chart

' make an XY chart .ChartType = xlXYScatterLines

' remove extra series Do Until .SeriesCollection.Count = 0 .SeriesCollection(1).Delete Loop

' add series from selected range, column by column For iColumn = 2 To rngChtData.Columns.Count With .SeriesCollection.NewSeries .Values = rngChtXVal.Offset(, iCOlumn - 1) .XValues = rngChtXVal .Name = rngChtData(1, iColumn) End With Next

End With

End SubA similar approach is used inInteractive Chart Creation, which provides dialogs for the user to select the range which will be covered by the chart and the range containing the data to be charted. The series-by-series definition of X and Y ranges is described inExcel XY Chart Variations with VBAto allow much more flexibility in the arrangement of the chart's source data range. TheQuick Chart Utilityis based on this approach.Top of Page

VBA Code to Resize and Reposition Chart ObjectsChart Object Size and PositionWe learned above that it is easy to define the size and position of a chart at the time of its creation: Set myChtObj = ActiveSheet.ChartObjects.Add _ (Left:=100, Width:=375, Top:=75, Height:=225)In the same way, we can position or size the chart, by changing the appropriate properties of the ChartObject:Sub ResizeAndRepositionChart() ' The ChartObject is the Chart's parent With ActiveChart.Parent .Left = 100 .Width = 375 .Top = 75 .Height = 225 End WithEnd SubTo adjust a particular chart on the sheet, useWith ActiveSheet.ChartObjects(1)in place ofWith ActiveChart.Parentin the procedure above.Top of PageCover a Range with a ChartYou can easily configure the chart to cover a specific range of cells on the worksheet. To cover the range D5:K25 with the active chart, run this procedure:Sub CoverRangeWithChart() Dim cht As Chart Object Dim rng As Range

Set cht = ActiveChart.Parent Set rng = ActiveSheet.Range("D5:K25")

cht.Left = rng.Left cht.Width = rng.Width cht.Top = rng.Top cht.Height = rng.HeightEnd SubTop of PageCreate an Array of ChartsSuppose you have a lot of charts on a worksheet, and you'd like to arrange them neatly. The following procedure loops through the charts, resizes them to consistent dimensions, and arranges them in systematic rows and columns:Sub ArrangeMyCharts() Dim iChart As Long Dim nCharts As Long Dim dTop As Double Dim dLeft As Double Dim dHeight As Double Dim dWidth As Double Dim nColumns As Long

dTop = 75 ' top of first row of charts dLeft = 100 ' left of first column of charts dHeight = 225 ' height of all charts dWidth = 375 ' width of all charts nColumns = 3 ' number of columns of charts nCharts = ActiveSheet.ChartObjects.Count

For iChart = 1 To nCharts With ActiveSheet.ChartObjects(iChart) .Height = dHeight .Width = dWidth .Top = dTop + Int((iChart - 1) / nColumns) * dHeight .Left = dLeft + ((iChart - 1) Mod nColumns) * dWidth End With NextEnd Sub

========================

Programming Charts

Below we will look at two programs inExcel VBA. One programloops through all chartson a sheet and changes each chart to a pie chart. The other program changes someproperties of the first chart.1. Create some charts (or download the Excel file).

AdvertisementPlace acommand buttonon the worksheet and add the following code lines:1. First, we need to declare a ChartObject object. The ChartObject object acts as a container for a Chart object. We call the ChartObject cht but you can use any name.DimchtAsChartObject2. The ChartObjects collection contains all the embedded charts on a single sheet. We want to loop through all charts on the first sheet. Add the following For Each Next loop.ForEachchtInWorksheets(1).ChartObjects

Nextcht3. The Chart object represents a chart in a workbook. Add the following code line to the For Each Next loop to change each chart to a pie chart.cht.Chart.ChartType = xlPieNote: again, cht acts as a container for the Chart object. We use the ChartType property to change the chart type. We use the built-in constant xlPie to change each chart to a pie chart.4. Result when you click the command button on the sheet:

Now we will change some properties of the first chart.Place anothercommand buttonon the worksheet and add the following code lines:5. The ChartObjects collection contains all the embedded charts on a single sheet. Add the following code line to activate the first chart:Worksheets(1).ChartObjects(1).ActivateWe can now refer to this chart as the ActiveChart.6. Add the following code line to change the Chart title.ActiveChart.ChartTitle.Text = "Sales Report"7. Add the following code line to move the legend to the bottom of the chart:ActiveChart.Legend.Position = xlBottom8. Result when you click the command button on the sheet: