Forum Discussion

JJ Eggers's avatar
JJ Eggers
Copper Contributor
Oct 19, 2017

I am trying to make a separate charts for each row on the same sheet.

I am having an issue with making the charts uniform.

 

Row 1 is the Titles for the columns

 

Row 2 - 5 is what I am looking to make separate charts for. Well this works well with Row 2, but when I try to make charts for 3-5 the charts do not look uniform. The titles from Row 1 do not carry over.

 

Is there a way to copy the chart from Row 2, then edit the copied chart so instead of pulling data from C2 it gets the data from C3?

23 Replies

  • I wrote a little VBA routine to take a chart that uses Y values in one row, and make a copy of the chart, use the next row for the Y values (and name if it uses a cell for the name), and repeat until the next row has no numeric values. Here's how it looks at the start:

     

     

    Here is the code:

     

    Sub DuplicateActiveChartOncePerRow()
      If ActiveChart Is Nothing Then
        MsgBox "Select a chart and try again!", vbExclamation + vbOKOnly
        GoTo ExitSub
      End If
      Dim nCharts As Long
      nCharts = ActiveSheet.ChartObjects.Count
      Dim OriginalChart As Chart
      Set OriginalChart = ActiveChart
      
      ' SERIES formula
      Dim sFormula As String
      sFormula = OriginalChart.SeriesCollection(1).Formula
      ' formula arguments only
      sFormula = Mid$(Left$(sFormula, Len(sFormula) - 1), InStr(sFormula, "(") + 1)
      ' array of arguments
      Dim vFormula As Variant
      vFormula = Split(sFormula, ",")
      
      ' series name - first argument
      Dim sName As String
      sName = vFormula(LBound(vFormula))
      Dim rName As Range
      On Error Resume Next
      Set rName = Range(sName)
      On Error GoTo 0
      Dim bNameIsRange As Boolean
      bNameIsRange = Not rName Is Nothing
      
      ' y values - third argument
      Dim sYVals As String
      sYVals = vFormula(LBound(vFormula) + 2)
      Dim rYVals As Range
      On Error Resume Next
      Set rYVals = Range(sYVals)
      On Error GoTo 0
      If rYVals Is Nothing Then
        MsgBox "Y Values Are Not in a Range!", vbExclamation + vbOKOnly
        GoTo ExitSub
      End If
      
      Dim iChart As Long
      iChart = 1
      Do
        ' loop until we run out of Y values
        If WorksheetFunction.Count(rYVals.Offset(iChart)) = 0 Then
          MsgBox "Finished!", vbExclamation + vbOKOnly
          GoTo ExitSub
        End If
        ' make copy of original chart
        OriginalChart.Parent.Copy
        Do
          ' loop to avoid error because sometimes clipboard isn't ready to paste
          DoEvents
          On Error Resume Next
          ActiveSheet.Paste
          On Error GoTo 0
          If ActiveSheet.ChartObjects.Count >= nCharts + iChart Then Exit Do
        Loop
        Dim NewChart As Chart
        Set NewChart = ActiveSheet.ChartObjects(ActiveSheet.ChartObjects.Count).Chart
        NewChart.Parent.Left = OriginalChart.Parent.Left
        NewChart.Parent.Top = OriginalChart.Parent.Top + iChart * rYVals.Height
        ' change Y values and name in new chart
        With NewChart.SeriesCollection(1)
          .Values = rYVals.Offset(iChart)
          If bNameIsRange Then
            .Name = "=" & rName.Offset(iChart).Address(, , , True)
          End If
        End With
        iChart = iChart + 1
      Loop
      
    ExitSub:
    End Sub

     

    And here is the end result:

     

    • BGrover42's avatar
      BGrover42
      Copper Contributor

      JonPeltier 

       

      I have been looking for something like this forever, except my scenario is with XY plots and I want to plot in groups of n. For example, my time is in column B and my data goes through column CT. I want to have 3 consecutive columns per graph. Can something similar be done for this or is that too complicated for excel?

       

      Thanks!

      • JonPeltierMVP's avatar
        JonPeltierMVP
        Iron Contributor
        AFK so I can't give you code now. It sounds like you want column B as X values for all charts, then columns C-E for Y values for the first chart, F-H for the second, etc. It's not really any more complicated than the earlier example.
    • jasper1984's avatar
      jasper1984
      Copper Contributor

      JonPeltier 

       

       

      Hello- I found this code very very helpful so thank you very much for sharing. I have modified it to include two data series. However, I am now struggling with adding a chart title. How can I make the chart title link to a column and change with each new chart generated per row? (As your posted code does but for two data series.) I can post my attempt below -

      Sub DuplicateActiveChartOncePerRow()
      If ActiveChart Is Nothing Then
      MsgBox "Select a chart and try again!", vbExclamation + vbOKOnly
      GoTo ExitSub
      End If
      Dim nCharts As Long
      nCharts = ActiveSheet.ChartObjects.Count
      Dim OriginalChart As Chart
      Set OriginalChart = ActiveChart

      ' SERIES formula
      Dim sFormula As String
      Dim sFormula2 As String
      sFormula = OriginalChart.SeriesCollection(1).Formula
      sFormula2 = OriginalChart.SeriesCollection(2).Formula
      ' formula arguments only
      sFormula = Mid$(Left$(sFormula, Len(sFormula) - 1), InStr(sFormula, "(") + 1)
      sFormula2 = Mid$(Left$(sFormula2, Len(sFormula2) - 1), InStr(sFormula2, "(") + 1)
      ' array of arguments
      Dim vFormula As Variant
      Dim vFormula2 As Variant
      vFormula = Split(sFormula, ",")
      vFormula2 = Split(sFormula2, ",")

      ' series name - first argument
      Dim sName As String
      sName = vFormula(LBound(vFormula))
      Dim rName As Range
      On Error Resume Next
      Set rName = Range(sName)
      On Error GoTo 0
      Dim bNameIsRange As Boolean
      bNameIsRange = Not rName Is Nothing

      Dim sName2 As String
      sName2 = vFormula2(LBound(vFormula2))
      Dim rName2 As Range
      On Error Resume Next
      Set rName2 = Range(sName2)
      On Error GoTo 0
      Dim bNameIsRange2 As Boolean
      bNameIsRange2 = Not rName2 Is Nothing

      ' y values - third argument
      Dim sYVals As String
      sYVals = vFormula(LBound(vFormula) + 2)
      Dim rYVals As Range
      On Error Resume Next
      Set rYVals = Range(sYVals)
      On Error GoTo 0
      If rYVals Is Nothing Then
      MsgBox "Y Values Are Not in a Range!", vbExclamation + vbOKOnly
      GoTo ExitSub
      End If

      Dim sYVals2 As String
      sYVals2 = vFormula2(LBound(vFormula2) + 2)
      Dim rYVals2 As Range
      On Error Resume Next
      Set rYVals2 = Range(sYVals2)
      On Error GoTo 0
      If rYVals2 Is Nothing Then
      MsgBox "Y Values Are Not in a Range!", vbExclamation + vbOKOnly
      GoTo ExitSub
      End If


      Dim iChart As Long
      iChart = 1
      Do
      ' loop until we run out of Y values
      If WorksheetFunction.Count(rYVals.Offset(iChart)) = 0 Then
      MsgBox "Finished!", vbExclamation + vbOKOnly
      GoTo ExitSub
      End If

      ' make copy of original chart
      OriginalChart.Parent.Copy
      Do
      ' loop to avoid error because sometimes clipboard isn't ready to paste
      DoEvents
      On Error Resume Next
      ActiveSheet.Paste
      On Error GoTo 0
      If ActiveSheet.ChartObjects.Count >= nCharts + iChart Then Exit Do

      Loop
      Dim NewChart As Chart
      Set NewChart = ActiveSheet.ChartObjects(ActiveSheet.ChartObjects.Count).Chart
      NewChart.Parent.Left = OriginalChart.Parent.Left
      NewChart.Parent.Top = OriginalChart.Parent.Top + iChart * rYVals.Height

      ' change Y values and name in new chart

      With NewChart.SeriesCollection(1)
      .Values = rYVals.Offset(iChart)
      If bNameIsRange Then
      .Name = "=" & rName.Offset(iChart).Address(, , , True) & "!" & Cells(iChart, 3).Address(, , , True)

      End If

      End With

      With NewChart.SeriesCollection(2)
      .Values = rYVals2.Offset(iChart)

      End With
      iChart = iChart + 1
      Loop

      ExitSub:
      End Sub

      • JonPeltier's avatar
        JonPeltier
        MVP

        jasper1984 

         

        Before

        Dim iChart As Long

        insert this block of code:

        ' chart title
        Dim sTitle As String
        sTitle = OriginalChart.ChartTitle.Formula
        sTitle = Mid$(sTitle, 2) ' trim leading =
        Dim rTitle As Range
        On Error Resume Next
        Set rTitle = Range(sTitle)
        On Error GoTo 0
        Dim bTitleIsRange As Boolean
        bTitleIsRange = Not rTitle Is Nothing

         

        then before

        iChart = iChart + 1

        insert this code:

         

        If bTitleIsRange Then
          NewChart.ChartTitle.Formula = "=" & rTitle(Offset(iChart).Address(, , , True)
        End If
    • Owen_Boyle's avatar
      Owen_Boyle
      Copper Contributor
      That looks great! It's exactly what I'm trying to do; the columns (A,B,C,D,E) are the stages in a process. The numbers in the cells are the amount of product at each stage in the process. The rows are different times. So each graph is a snapshot of the production line. I want to see how it progresses over time, so I had a movie where each graph is a frame. The more frames, the more fine-grained the visualisation, so I need an automatic process to turn the data from the process monitor into individual frames. This looks like it could that!

      I'll try it and get back to you...

Resources