How to trigger/Update refresh Data from website - excel

Hi I am using the following code to parsing some data from website to excel , I need to create a routine in order to update /Refresh the data and keep is up to date and I been advice to start new topic , is big list with multiple sheets so take long time every time excel has to calculate , I hope someone can may help me out
Public Function giveMeValue(ByVal link As Variant) As Variant
Set htm = CreateObject("htmlFile")
With CreateObject("msxml2.xmlhttp")
.Open "GET", link, False
.send
htm.body.innerhtml = .responsetext
End With
If Not htm.getelementbyId("JS_topStoreCount") Is Nothing Then
giveMeValue = htm.getelementbyId("JS_topStoreCount").innerText
Else
giveMeValue = "0"
End If
htm.Close
Set htm = Nothing
End Function
to retrive the value I use =GiveMeValue(A1) and condition formatting the returned value I use the Following code :
Dim color As Integer 'Start Color range
For Each cell In Sheets(1).Range("M4:M5000")
If IsEmpty(cell) Then GoTo nextcell:
If Not IsNumeric(cell.Value) Then GoTo nextcell:
If cell.Value > 14 Then
color = 4
ElseIf cell.Value < 8 Then color = 3
Else: color = 6
End If
cell.Interior.ColorIndex = color
nextcell:
Next cell
End Sub
so as I am not skilled at all with VBA I may unappropriate use the following code to try get refresh it but without result:
Sub Refresh()
Dim WAIT As Double
WAIT = Timer
While Timer < WAIT + 10
DoEvents 'do nothing'
ActiveWorkbook.RefreshAll
Wend
MsgBox "Update Finished Successfully !"
'End Sub

Sounds ambitious. I like it! :)
For a start can you just use conditional formatting? Will be a lot quicker than looping through every cell and changing colour.
Secondly, make your GiveMeValue function "volatile" (http://www.excel-easy.com/vba/examples/volatile-functions.html) so that it refreshes every time you recalculate the page i.e. add a line "application.volatile(true) before anything else in the function.
Thirdly: run your refresh code in the "worksheet_calculate" event and change it to:
Sub Refresh()
Dim WAIT As Double
WAIT = Timer
While Timer < WAIT + 10
DoEvents 'do nothing'
Wend
wsSheet.Calculate
End Sub
So it runs... waits a bit... calculates, then that calculation triggers another wait... etc... Now that your function is volatile, it should refresh all the "getvalue" formulas you have on the current sheet...
And I guess forthly put a "wsSheet.calculate" in the worksheet_activate event to trigger start of that endless loop...
Having said all that, perhaps that endless loop will cause huge issues i.e. mega computer slow down, inability to use computer, and general doom... only one way to find out! The doevents thing should in theory make everything OK. Maybe add if activesheet.codename = "wsSheet" then... so its only running when you're on that sheet...

Related

How do I do a Do Event timer in vba excel while still being able to change values in my worksheet?

I have a timer I am actively running through a DoEvents function. The problem is that every thirty secs I want to manually put some temperature values in my worksheet. However, if I do that while the event is running I get:
Run-time error '1004': The specified dimension is not valid for the
current chart type.
Some thoughts: I was going to try turning off screen updating but that would keep me from seeing the time change in real time.
Is the active event keeping me from working in my document? And if so, is there a way around this?
Here is the body of my code (I also have a sub for the stop and reset timer but I don't think that's pertinent):
Sub START_TIMER()
Dim sh As Worksheet
Set sh = ThisWorkbook.ActiveSheet
sh.Range("AI1").Value = "Start"
If sh.Range("AI2").Value = "" Then
sh.Range("AI2").Value = Now
sh.Range("AL2").Value = Now
End If
x:
VBA.DoEvents
If sh.Range("AI1").Value = "Stop" Then Exit Sub
sh.Range("AI3").Value = Now
sh.Range("AL3").Value = Now
If sh.Range("AL4").Value > TimeValue("00:00:29") Then
sh.Range("A17").Interior.Color = vbYellow
sh.Range("AL2").Value = Now
Beep
Else
sh.Range("A17").Interior.Color = vbWhite
End If
GoTo x
If sh.Range("AL4").Value = "0:00:30" Then
sh.Range("AL2").Value = Now
End If
End
End Sub
I figured it out!
If I just put an on error resume next then it runs well enough to work for my needs.

Bloomberg Data taking time to load in Excel- VBA

I currently have this Excel VBA code written using Bloomberg API. I am trying to pull data from Bloomberg into Excel using VBA. That is working fine but the problem is that I have another macro that then copies the data from Bloomberg and pastes it in another sheet.
If the data hasn't all been output from Bloomberg then I end up having insufficient data to copy then paste into the other sheet.
Currently, I am using this line of code:
Application.OnTime Now + TimeValue("00:01:45"), "RunAll"
which waits after running the first macro for 1 min 45s till it runs the remaining macros. It is useful but way too much time. Issue is that this is around how long the data takes to output.
Is there any other more efficient way for me to pull in the bloomberg data faster by ensuring that the data gets output into excel faster?
One way to handle it would be when you start your second macro that copies the data, check to see to see if a mid-point cell is empty (something like A100?? Seeing your code would help here...). If so, wait 10 seconds and check again. This will force that second macro to stay in a holding pattern while the first one catches up.
Word of caution though, I would set a max number of loops otherwise if that data doesn't download for some reason it won't hang up your machine.
UPDATE 1:
I wrote out the code below to accomplish what you're trying to do. There are a couple of things you'll need to work into your current code, but it I've purposely made it comment heavy so you should be able to follow. Let me know if this works for you.
Public boolBloombergCompleted As Boolean
Sub GetBloombergData()
'add this line after the data grab is complete
boolBloombergCompleted = True
End Sub
Sub WriteData()
Dim iRow As Integer
Dim boolTimeOut As Boolean
'change the last number as fit, make sure it's larger than the number of rows of data you're pulling in though
For iRow = 1 To 1000
' Check to see if the cell is blank
If Sheet1.Cells(iRow, 1) = vbNullString Then
' If the cell is blank and GetBloombergData has completed then exit sub
If boolBloombergCompleted = True Then
Exit Sub: Debug.Print "WriteData completed"
Else
' Call the wait function below
boolTimeOut = WaitForDataGrabToCatchUp(Sheet1.Cells(iRow, 1))
If boolTimeOut = True Then GoTo TimeOutErr:
End If
End If
' < Add your code to write data in here >
Next iRow
Exit Sub
TimeOutErr:
MsgBox "The write sub timed out while waiting for data to load", vbExclamation
End Sub
Function WaitForDataGrabToCatchUp(rng As Range) As Boolean
Dim StartTime1 As Long
Dim StartTime2 As Long
Dim PauseTime As Long
Dim StopTime As Long
' Set the amount of time to pause between checking the spreadsheet for updates
PauseTime = 5 'seconds
' Set the maximum amount of time to wait before timing out
StopTime = 60 'seconds
' StartTime1 is used for calculating overall time
StartTime1 = Timer
Do While rng = vbNullString
' check if the StopTime has been reached
If Timer - StartTime1 > StopTime Then
WaitForDataGrabToCatchUp = True
Exit Function
Else
' loop for amount of PausedTime (the DoEvents part here is key to keep the data grab moving)
StartTime2 = Timer
Do While Timer < StartTime2 + PauseTime
Debug.Print Timer - StartTime1
DoEvents
Loop
End If
Loop
WaitForDataGrabToCatchUp = False ' means it did not time out
End Function

Excel error, stops running macro

I am experiencing an odd bug on Excel. I have a macro that shows a non-modal userform when I press CTRL+m (Macro shortcut). Every once in a while, and it's not that frequent (Shows up once or twice during the day, I use the macro every 5 minutes or so), Excel won't run the macro, won't show the userform and will just beep (as in "mistake, cannot proceed executing code").
I went into the Macro window to try to press "Run" and manually execute, but all buttons are disabled, except for "Create". If you click it, it says the macro name is not valid. As you can see in the screenshot below, the name of the macro shows the instance where the code is (Sheet1 of the workbook).
Sometimes it can be fixed by saving the workbook and just re-trying, but sometimes it doesn't; when it doesn't, I run a different macro (by double clicking a specific column) that shows a modal userform, and executing its code. Then my first macro returns to normal.
Any help will be very much appreciated.
Edit: Adding the code as requested in the comments
Sub ShowCommentWindow()
Dim myCell As Range
Dim companyColumn As Long
Dim wbk as Workbook
Dim company as String
Dim phone as Long
Set wbk = ActiveWorkbook
For Each myCell In wbk.Worksheets(1).Range("A1:Q1")
If myCell.Text = "Company" Then
companyColumn = myCell.Column
company = ActiveCell.Text
phone = ActiveCell.Offset(0, 4).Value
Exit For
End If
Next myCell
If ActiveCell.Column = companyColumn Then
If EmailForm.Visible Then
GoTo ExitProc
Else
If Not ActiveCell.Row < 4 Then
ActiveWindow.ScrollRow = ActiveCell.Row - 3
Else
ActiveWindow.ScrollRow = ActiveCell.Row
End If
If CommentWindow.Visible Then
CommentWindow.AddButton.SetFocus
CommentWindow.CommentBox.SetFocus
Exit Sub
Else
CommentWindow.Show
ManageComments
AddComment
End If
End If
End If
ExitProc:
End Sub
Edit2: Posting more code, for QueryClose:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Dim myCell As Range
Dim isCompany As String
If Not CommentWindow.CommentBox.Text = CommentWindow.TextCopy.Text Then
saveConf = MsgBox("Changes have not been saved yet. Do you want to save?", vbExclamation + vbYesNoCancel + vbDefaultButton2, "Save changes?")
If saveConf = vbYes Then
Call SaveComment
GoTo ExitProc
ElseIf saveConf = vbCancel Then
changed = True
Cancel = 1
CommentWindow.AddButton.SetFocus
CommentWindow.CommentBox.SetFocus
'CommentWindow.CommentBox.Text = CommentWindow.TextCopy.Text
Else
CommentWindow.TextCopy.Text = CommentWindow.CommentBox.Text
GoTo ExitProc
End If
Else
If Not changed = True Then
GoTo ExitProc
End If
End If
ExitProc:
End Sub
Seems like the issue is not unloading the forms from ( Unload(UserForm) )
This leads to a memory leak.
Even the official documentation -this refers to Access, but, should behave the same for Excel (there's no Form object or userform documentation there)- state the Lifecycle is Unload->Deactivate->Close, and this should happen when you close the userform as well, daily usage has shown that Unload if not stated may not be triggered when closing the userform.
The lifecycle is not that strictly monitored sometimes, but, that may lead to memory leaks and strange behaviors, always when working with objects you shouldn't rely that garbage collector will clean them if not specified. Probably adding something to confirm that terminate is being correctly handled will be helpful.
EDIT
If you're having problems remembering the unload -or still having problems with memory-, it will be a good practice to do the following:
Sub MyMainProcess()
Dim myform As UserForm1: Set myform = UserForm1 'this is your UserForm name
myform.Show
'my stuff needed...
Unload myform
Set myform = Nothing
End Sub
Unload and Nothing to clean as much as possible with coding
I see that you're calling an "outside" macro (it's not within the active workbook) - Is it possible that then those roughly 2 times a day that it doesn't work that workbook (Database 2 Lumber.xlsm) is being used by someone else at that time (eight running that, or another macro?).
If so, What I have done before is save a local copy of the workbook each time the macro is run

Bloomberg data doesn't populate until Excel VBA macro finishes

I'm running a macro in a blank Excel 2007 workbook on a PC with a Bloomberg license. The macro inserts Bloomberg functions into sheet1 that pull yield curve data. Some additional functions' results are dependent on the first functions finishing and correctly displaying the Bberg data. When I step through the program it only displays '#N/A Requesting Data . . .' instead of the results of the query, no matter how slowly I go. Because some of the functions are dependent on string and numeric field results being populated, the program hits a run-time error at that code. When I stop debugging -- fully ending running the program -- all the Bberg values that should have populated then appear. I want these values to appear while the program is still running.
I've tried using a combination of DoEvents and Application.OnTime() to return control to the operating system and to get the program to wait for a long time for the data update, but neither worked. Any ideas would be helpful. My code is below. wb is a global-level workbook and ws1 is a global level worksheet.
Public Sub Run_Me()
'Application.DisplayAlerts = False
'Application.ScreenUpdating = False
Call Populate_Me
Call Format_Me
'Application.DisplayAlerts = True
'Application.ScreenUpdating = True
End Sub
Private Sub Populate_Me()
Dim lRow_PM As Integer
Dim xlCalc As XlCalculation
Set wb = ThisWorkbook
Set ws1 = wb.Sheets(1)
'clear out any values from previous day
If wb.Sheets(ws1.Name).Range("A1").Value <> "" Then
wb.Sheets(ws1.Name).Select
Selection.ClearContents
End If
xlCalc = Application.Calculation
Application.Calculation = xlCalculationAutomatic
Range("A1").Value = "F5"
Range("B1").Value = "Term"
Range("C1").Value = "PX LAST"
Range("A2").Select
ActiveCell.FormulaR1C1 = "=BDS(""YCCF0005 Index"",""CURVE_MEMBERS"",""cols=1;rows=15"")"
BloombergUI.RefreshAllStaticData
Range("B2").Select
ActiveCell.FormulaR1C1 = "=BDS(""YCCF0005 Index"",""CURVE_TERMS"",""cols=1;rows=15"")"
BloombergUI.RefreshAllStaticData
Application.OnTime Now + TimeValue("00:00:10"), "HardCode"
'******more code*******'
End Sub
Sub HardCode()
Range("C2").Select
ActiveCell.FormulaR1C1 = "=BDP($A2,C$1)"
BloombergUI.RefreshAllStaticData
End Sub
A way to get around this issue is to put all subs, etc that you want to run after pulling the bloomberg data into a different sub. You must do this each time you call Bloomberg information. If you call another sub in the "master" sub after the Application.OnTime Now +TimeValue("00:00:15"), it will fail- you must put all subs following into a new master sub.
For example:
Instead of
Sub Master1()
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeValue("00:00:15"), "OtherSub1"
'This will cause the Bloomberg Data to not refresh until OtherSub2 and 3 have run
OtherSub2
OtherSub3
End Sub
It should be
Sub Master1()
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeValue("00:00:15"), "Master2"
End Sub
Sub Master2()
OtherSub1
OtherSub2
OtherSub3
End Sub
Hope that helps
I googled for BloombergUI.RefreshAllStaticData and was immediately taken to this Mr Excel page: http://www.mrexcel.com/forum/showthread.php?t=414626
We are not supposed post answers which are only links in case that link disappears and takes the answer with it. However, I am not sure I understand the question or the answer well enough to summarise it.
The Google link will probably exist for the forseeable future.
Within Mr Excel, the chain is: MrExcel Message Board > Question Forums > Excel Questions > Bloomberg links and macros.
The key information appears to be:
On your Bloomberg terminal if you type in WAPI < GO > you will find listings of the Bloomberg API and downloadable examples.
Using the helpfile information in that area we can build a more robust solution to this using the Bloomberg Data Type Library. Go to Tools | References and add a reference to this library. This code can then be used to populate the cells:
Sub Test2()
Dim vResults, vSecurities, vFields
Dim objBloomberg As BLP_DATA_CTRLLib.BlpData
'fill our arrays - must be 1 dimension so we transpose from the worksheet
With Application.WorksheetFunction
vSecurities = .Transpose(Sheet1.Range("B2:B4").Value)
vFields = .Transpose(.Transpose(Range("C1:H1").Value))
End With
Set objBloomberg = New BLP_DATA_CTRLLib.BlpData
objBloomberg.AutoRelease = False
objBloomberg.Subscribe _
Security:=vSecurities, _
cookie:=1, _
Fields:=vFields, _
Results:=vResults
Sheet1.Range("C2:H4").Value = vResults
End Sub
Once you have tried out Mr Excel's solution, perhaps you could update this answer for the benefit of future visitors.
I gathered some information from around the web and wrote what imho is an improved version in comparison with everything I have found so far:
Private WaitStartedAt As Double
Private Const TimeOut As String = "00:02:00"
Public Function BloomCalc(Callback As String) As Boolean
Dim rngStillToReceive As Range
Dim StillToReceive As Boolean
Dim ws As Worksheet
StillToReceive = False
If WaitStartedAt = 0 Then
WaitStartedAt = TimeValue(Now())
End If
If TimeValue(Now()) >= WaitStartedAt + TimeValue(TimeOut) Then
GoTo errTimeOut
End If
For Each ws In ActiveWorkbook.Worksheets
Set rngStillToReceive = ws.UsedRange.Find("*Requesting Data*", LookIn:=xlValues)
StillToReceive = StillToReceive Or (Not rngStillToReceive Is Nothing)
Next ws
If StillToReceive Then
BloomCalc = False
Application.OnTime Now + (TimeSerial(0, 0, 1)), Callback
Else
WaitStartedAt = 0
BloomCalc = True
End If
Exit Function
errTimeOut:
Err.Raise -1, , "BloomCalc: Timed Out. Callback = " & Callback
End Function
It should an arbitrary task by calling a sub like DoSomething()
Public Sub DoSomething()
DoSomethingCallback
End Function
That calls a "callback" function that will call itself until either the data has been refreshed or the time limit reached
Public Sub AutoRunLcbCallback()
If BloomCalc("AutoRunLcbCallback") Then
MsgBox "Here I can do what I need with the refreshed data"
' for instance I can close and save the workbook
ActiveWorkbook.Close True
End If
End Sub
Any comment is appreciated. A possible improvement might be to allow the workbook and / or worksheet to be an input of the function but I really didn't see the need for that.
Cheers
Hello there I think I have found a solution to this problem and I really want to share this with you guys.
Before starting with the real answer I want to make sure everyone understands how Application.OnTime actually works. And If you already know then you can safely skip to THE SOLUTION below.
Let's make a TOY EXAMLPE example with two subroutines Sub First() and Sub Second() and one variable x that is declared outside, so that it has scope inside the whole Module
Dim x as integer
Sub First()
x = 3
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
x = 2*x
End Sub
Sub Second()
x = x + 1
End Sub
I thought that the commands were executed in the following order:
x = 3
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
Then after 2 seconds of wait, in Sub Second() x = x + 1, hence 4
Finally we go back to Sub First() where x = 2*x , so that in the end x is equal to 8.
It turns out that this is not the way VBA operates; what happens instead is:
x = 3
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
Here the remaing code in Sub First() is executed until THE END, before switching to Sub Second().
So x = 2*x is executed right away along with every line of code that appears until the end of Sub First(). Now x is equal to 6.
Finally, after 2 seconds of waiting it executes the instruction in Sub Second(), x = x + 1, so that in the end x is equal to 7
This happens independently of how much time you make the application wait. So for instance if in my example, after
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
VBA took 10 seconds to execute the line
x = 2*x
it would still have to finish the execution of that command before switching to Sub Second().
WHY IS THIS IMPORTANT?
Because in the light of what I just explained I can now show you my solution to the OP question. Then you can adapt it to your needs.
And YES!!! This works with For Loops too!
THE SOLUTION
I have two subroutines:
BLPDownload() one where I refresh a workbook and I have to wait for the values to be dowloaded in order to execute some other code ...
BLPCheckForRefresh() where I check if all data have been downloaded
So just like before, I declare two variables with Module-Level Scope
Dim firstRefreshDone As Boolean, Refreshing As Boolean
Sub BLPDownload()
CHECK:
What I do right below is to:
check if I already told VBA to Refresh the workbook. Of course the first time you run the macro you have not; hence firstRefreshDone = False and it steps into this block of the if statement.
next it calls the other Sub BLPCheckForRefresh() and it exits the current Subroutine.
And this is the trick. To Exit the Subroutine after calling Application.OnTime*
Inside BLPCheckForRefresh() what happens is
that I set the value of firstRefreshDone = True
check if, in the UsedRange, I have cells wiht #N/A Requesting Data. If I have, the value of Refreshing = True.
finally I call back the Sub BLPDownload()
If Not firstRefreshDone Then
Application.Run "RefreshEntireWorkbook"
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
Exit Sub
This time though, firstRefreshDone = True so, if also the refreshing is finished it goes to AFTER_REFRESH where you can put all the code you want, else ...
ElseIf Not Refreshing Then
GoTo AFTER_REFRESH
if the refresh is not finished, i.e. if I have cells wiht #N/A Requesting Data it calls the other Sub BLPCheckForRefresh() and it exits the current Subroutine again.
This funny game goes on and on until we have no more #N/A Requesting Data in our UsedRange
Else
Refreshing = False
Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
Exit Sub
End If
AFTER:
some code ...
End Sub
This is the sub where I check if refreshing is done.
Sub BLPCheckForRefresh()
Dim rng As Range, cl As Range
Set rng = Foglio1.UsedRange
As explained above here I set the value of firstRefreshDone = True
firstRefreshDone = True
And this is the loop where i go through each cell in the usedrange looking for #N/A Requesting Data
On Error Resume Next
For Each cl In rng
If InStr(cl.Value2, "#N/A Request") > 0 Then
Refreshing = True
Exit For
End If
Next cl
On Error GoTo 0
Finally I call back the Sub BLPDownload()
Call BLPDownload
End Sub
So this is my solution. I works for me and with another dirty trick that exploits always the GoTo statements and another Module-Level Scope variable that keeps count of the number of iteration it is possible to use this structure in For Loops too.
That being said I want to point out that in my opinion the best solution to this problem is to use Bloomberg API as suggested by Tony Dallimore.
Hope this helps!!

Charts Do Not Automatically Update When Data Changes

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:
Select Cell B1 and name the range "worksheetDate"
Duplicate rows 1 through 3 in the following image:
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.
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
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.
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
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
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
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!
Running Excel 2019.
Added the following to the macro code:
ActiveSheet.ChartObjects(1).Chart.Refresh
DoEvents
The chart now updates during macro execution
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
'...
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.

Resources