12

Hopefully this is an easy one. I have a series of charts in MS Excel that point to data on the same worksheet. The data on the worksheet is calculated using a VBA function. When the data is updated by the VBA function the new numbers are not reflected in the charts that are pointing to them. I tried calling Application.Calculate, but that didn't do the trick. Any thoughts?


UDPATE:

I was able to duplicate this issue on a much smaller scale. Here's how:

  • Create a new workbook
  • Rename Sheet 1 to "Summary"
  • Rename Sheet 2 to "Data"
  • Open the Summary sheet in the VBA editor and paste the following code:

    Private Sub Worksheet_Change(ByVal Target As Range)
       If Target.Parent.Range("worksheetDate") = Target Then
          Application.CalculateFull
       End If
    End Sub
    
  • Create a new VBA module

  • Paste the following code into the new VBA module (I apologize - I can't get Stack Overflow to format this correctly for the life of me - this is the best I could get it to do):
    .

     Function getWeekValue (weekNumber As Integer, valuesRange As Range) As Integer   
    
     Dim aCell As Range  
     Dim currentDate As Date  
     Dim arrayIndex As Integer  
     Dim weekValues(1 To 6) As Integer  
    
     currentDate = ThisWorkbook.Names("worksheetDate").RefersToRange.Value
     arrayIndex = 1  
     For Each aCell In valuesRange 
         If month(currentDate) = month(ThisWorkbook.Sheets("Data").Cells( _  
                                       aCell.Row - 1, aCell.Column)) Then
             weekValues(arrayIndex) = aCell.Value 
             arrayIndex = arrayIndex + 1 
         End If 
     Next
    
     getWeekValue = weekValues(weekNumber)   
     End Function  
    

    .

  • Modify the Data worksheet to match the following image:

alt text

  • Select Cell B1 and name the range "worksheetDate"
  • Duplicate rows 1 through 3 in the following image:

alt text

  • In row 4, under the "Week X" headers, enter the following formula

.

 = getWeekValue(1, Data!$A$2:$M$2)

incrementing the first argument to the getWeekValue function by one for each week (e.g., pass 1 for Week 1, 2 for Week 2, 3, for Week 3, etc.

  • Create a bar graph using cells A3 through E4 as the data
  • Change the date in cell B2 to a date between 10/1/2010 and 12/31/2010, choosing a month other than the month that is currently in the cell. For example, if the date is 12/11/2010, change it to something like 11/11/2010 or 10/11/2010. Note that both the data and chart update correctly.
  • Modify the date in cell B2 gain. Note that the data updates, but the chart does not.

Oddly, after a period of time (several minutes) has elapsed, the chart finally updates. I'm not sure if this is because I have been performing other activities that triggered the update or because Excel is triggering an update after several minutes.

Community
  • 1
  • 1
Adam
  • 1,011
  • 2
  • 19
  • 37
  • are your charts based on simple data ranges or are they PivotCharts which are based on (a) PivotTable(s) (which in turn may be based on your VBA calculated values). The two chart types behave different - to update the latter you must refresh the Pivots rather than recalculate - and the PivotCharts will follow automatically – MikeD Dec 17 '10 at 09:48
  • @MikeD Thanks for the info - I'll keep that in mind in the future. In this case I'm working with a bar graph pointing to a data a simple data range that isn't tied to a pivot table. – Adam Dec 17 '10 at 18:21
  • So were you able to resolve this after all? I noticed that inserting first row to worksheet updates the chart – Arthur Stankevich Oct 22 '12 at 10:13
  • Note: see answer below by me, need to use DoEvents in code to force updates on graph. – Cyberchipz Jul 21 '17 at 12:37

9 Answers9

2

Just figured out the solution to this issue as I was suffering from the same.

I've just added "DoEvents()" prior to printing or exporting and the chart got refreshed.

example

Sub a()
   Dim w As Worksheet
   Dim a
   Set w = Worksheets(1)

   For Each a In w.Range("a1:a5")
     a.Value = a.Value + 1
   Next

   DoEvents

End Sub  
1

at the end of my changes I close the workbook and reopen it. that seems the easiest and most reliable way to update everything for me.

TmTron
  • 17,012
  • 10
  • 94
  • 142
0

This solution worked for me. For the offending worksheet add:

Private Sub Worksheet_Activate()
  Dim rngSelection          As Range
  Dim objChartObject        As ChartObject
  Dim objChart              As Chart
  Dim objSeriesCollection   As SeriesCollection
  Dim objSeries             As Series
  Dim strFormula            As String

  Set rngSelection = Selection

  For Each objChartObject In Me.ChartObjects
    Set objChart = objChartObject.Chart
    Set objSeriesCollection = objChart.SeriesCollection
    For Each objSeries In objSeriesCollection
      strFormula = objSeries.Formula

      objSeries.Delete

      Set objSeries = objSeriesCollection.NewSeries

      objSeries.Formula = strFormula
    Next objSeries
  Next objChartObject

  rngSelection.Select
End Sub
SLeepdepD
  • 91
  • 1
  • 9
  • Be careful when posting copy and paste boilerplate/verbatim answers to multiple questions, these tend to be flagged as "spammy" by the community. If you're doing this then it usually means the questions are duplicates so flag them as such instead: http://stackoverflow.com/a/12322420/419 – Kev Sep 07 '12 at 22:01
  • Thank you. I'll delete my other response. – SLeepdepD Sep 12 '12 at 16:12
0

It's possible that the issue is the argument list of getWeekValue, which includes only the week number and the data stream.

If you add a third argument, worksheetDate, then Excel's recalculation engine will be hit on the side of the head with the fact that getWeekValue uses the value held in worksheetDate. In your current implementation, this fact is held only in the VBA code, where it is probably invisible to the recalculation engine.

I write this so hedgingly because I am not privy to the inner workings of the recalculation engine. (Maybe someone who knows about this better than I can comment on my speculation) But I did do a test, in which getWeekValue does have that third argument, and the chart does recalculate properly. Nice added benefit of this approach: you can remove all that other VBA event management. -HTH

user3716832
  • 111
  • 3
0

For example:

Sub a()
   Dim w As Worksheet
   Dim a
   Set w = Worksheets(1)

   For Each a In w.Range("a1:a5")
     a.Value = a.Value + 1
   Next

   w.ChartObjects(1).Chart.Refresh

End Sub  

alt text

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
  • You know, I would expect that to work, but it doesn't. Neither does a Data -> Refresh All – Adam Dec 17 '10 at 00:23
  • @Adam I tested it on my machine before posting – Dr. belisarius Dec 17 '10 at 00:38
  • @belisarius -- I should clarify - Your code works as expected. Even modifying it with some added complexity to mimic what I have set up works fine. In fact, the line to refresh the chart isn't even needed - that happens automatically. However, for some reason there appears to be an issue with this particular workbook/dataset/set of VBA functions. The charts refresh correctly with the first set of data changes, but not thereafter... Still investigating. – Adam Dec 17 '10 at 01:13
  • @Adam Try to trim your workbook for all irrelevant details and upload it somewhere ... – Dr. belisarius Dec 17 '10 at 02:12
  • @belisarius See the updates to my post for steps to duplicate the issue – Adam Dec 17 '10 at 21:27
  • @belisarius -- I certainly would have uploaded the workbook rather than detailing how to set it up if I had a place to upload it, but I don't. That would have been far easier for me as well. – Adam Dec 20 '10 at 18:29
0

I've found that calling this Sub works...

Sub DoAllEvents()
    DoEvents
    DoEvents
End Sub

BUT Microsoft cautions about being caught with the next DoEvents executing before the first DoEvents completes, which can happen depending on how often it's called without a delay between calls. Thus DoEvents appears to be acting as a type of non maskable interrupt, and nesting non maskable interrupts can cause the machine to freeze for multiple reasons without any recovery other than reboot.

(Note: If one is not calling the routine above, often and quickly, nesting may not be an issue.)

Using the following Sub below, which I modified from their suggestion, prevents this from happening.

Sub DoAllEvents()
    On Error GoTo ErrorCheck
    Dim i
    For i = 1 To 4000    ' Start loop. Can be higher, MS sample shows 150000
        'I've found twice is enough, but only increased it to four or 4000.
        If i Mod 1000 = 0 Then     ' If loop has repeated 1000 times.
            DoEvents    ' Yield to operating system.
        End If
    Next i
    Exit Sub
ErrorCheck:
    Debug.Print "Error: "; Error, Err
    Resume Next
End Sub

I appears that the number of DoEvents needed is based on the number of background tasks running on your machine, and updating the graph appears to be a background task for the application. I only needed two DoEvents because I call the routine frequently; however, I may end up upping it later if needed. I also keep the Mod at 1000 so to not change the lag between each DoEvents as Microsoft suggests, preventing nesting. One possible reason you might want to increase the number from 2000 to a higher number is if you system does not update the graph. Increasing this number allows the machine to handle larger numbers of background events that DoEvents might encounter through multiple calls as they are probably on a stack, and the DoEvents event is only allowed to run a specific number of cycles before marking its place in the stack to allow unhandled events and returning, leaving them to be handled on the next call. Thus the need for multiple calls. Changing this to their example of 150000 doesn't appear to slow the machine too much, to play it safe you might want to make it 150000.

Note: the first example Sub with two DoEvents is probably safe depending on how often you call the Sub, however, if called too often, your machine might freeze up. Your call. ;-)

PS: DoEvents will become one of your best calls if you create a lot of nested loops and the program doesn't behave as expected. Fortunately, this is available in all apps that use VBA!

Cyberchipz
  • 117
  • 3
0

Running Excel 2019.

Added the following to the macro code:

ActiveSheet.ChartObjects(1).Chart.Refresh    
DoEvents

The chart now updates during macro execution

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
0

UDF getWeekValue has to be marked as volatile.

 Function getWeekValue (weekNumber As Integer, valuesRange As Range) As Integer   
 
 Application.Volatile '!!

 Dim aCell As Range  
 Dim currentDate As Date
 
 '... 
Majid Hajibaba
  • 3,105
  • 6
  • 23
  • 55
aDude
  • 1
-2

Just an idea: in your Worksheet_Change Sub, insert as the first line:

Application.EnableEvents = False

in order to avoid self-firing events....
Of course set it back to True at the end of the Sub.

iDevlop
  • 24,841
  • 11
  • 90
  • 149