Force a screen update in Excel VBA - vba
My Excel tool performs a long task, and I'm trying to be kind to the user by providing a progress report in the status bar, or in some cell in the sheet, as shown below. But the screen doesn't refresh, or stops refreshing at some point (e.g. 33%). The task eventually completes but the progress bar is useless.
What can I do to force a screen update?
For i=1 to imax ' imax is usually 30 or so
fractionDone=cdbl(i)/cdbl(imax)
Application.StatusBar = Format(fractionDone, "0%") & "done..."
' or, alternatively:
' statusRange.value = Format(fractionDone, "0%") & "done..."
' Some code.......
Next i
I'm using Excel 2003.
Add a DoEvents function inside the loop, see below.
You may also want to ensure that the Status bar is visible to the user and reset it when your code completes.
Sub ProgressMeter()
Dim booStatusBarState As Boolean
Dim iMax As Integer
Dim i As Integer
iMax = 10000
Application.ScreenUpdating = False
''//Turn off screen updating
booStatusBarState = Application.DisplayStatusBar
''//Get the statusbar display setting
Application.DisplayStatusBar = True
''//Make sure that the statusbar is visible
For i = 1 To iMax ''// imax is usually 30 or so
fractionDone = CDbl(i) / CDbl(iMax)
Application.StatusBar = Format(fractionDone, "0%") & " done..."
''// or, alternatively:
''// statusRange.value = Format(fractionDone, "0%") & " done..."
''// Some code.......
DoEvents
''//Yield Control
Next i
Application.DisplayStatusBar = booStatusBarState
''//Reset Status bar display setting
Application.StatusBar = False
''//Return control of the Status bar to Excel
Application.ScreenUpdating = True
''//Turn on screen updating
End Sub
Text boxes in worksheets are sometimes not updated
when their text or formatting is changed, and even
the DoEvent command does not help.
As there is no command in Excel to refresh a worksheet
in the way a user form can be refreshed, it is necessary
to use a trick to force Excel to update the screen.
The following commands seem to do the trick:
- ActiveSheet.Calculate
- ActiveWindow.SmallScroll
- Application.WindowState = Application.WindowState
Put a call to DoEvents in the loop.
This will affect performance, so you might want to only call it on each, say, 10th iteration.
However, if you only have 30, that's hardly an issue.
#Hubisans comment worked best for me.
ActiveWindow.SmallScroll down:=1
ActiveWindow.SmallScroll up:=1
Specifically, if you are dealing with a UserForm, then you might try the Repaint method. You might encounter an issue with DoEvents if you are using event triggers in your form. For instance, any keys pressed while a function is running will be sent by DoEvents The keyboard input will be processed before the screen is updated, so if you are changing cells on a spreadsheet by holding down one of the arrow keys on the keyboard, then the cell change event will keep firing before the main function finishes.
A UserForm will not be refreshed in some cases, because DoEvents will fire the events; however, Repaint will update the UserForm and the user will see the changes on the screen even when another event immediately follows the previous event.
In the UserForm code it is as simple as:
Me.Repaint
This worked for me:
ActiveWindow.SmallScroll down:=0
or more simply:
ActiveWindow.SmallScroll 0
I couldn't gain yet the survey of an inherited extensive code. And exact this problem bugged me for months. Many approches with DoEnvents were not helpful.
Above answer helped. Placeing this Sub in meaningful positions in the code worked even in combination with progress bar
Sub ForceScreenUpdate()
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Wait Now + #12:00:01 AM#
Application.ScreenUpdating = False
Application.EnableEvents = False
End Sub
This is not directly answering your question at all, but simply providing an alternative. I've found in the many long Excel calculations most of the time waiting is having Excel update values on the screen. If this is the case, you could insert the following code at the front of your sub:
Application.ScreenUpdating = False
Application.EnableEvents = False
and put this as the end
Application.ScreenUpdating = True
Application.EnableEvents = True
I've found that this often speeds up whatever code I'm working with so much that having to alert the user to the progress is unnecessary. It's just an idea for you to try, and its effectiveness is pretty dependent on your sheet and calculations.
On a UserForm two things worked for me:
I wanted a scrollbar in my form on the left. To do that, I first had to add an Arabic language to "Change administrative language" in the Language settings of Windows 10 (Settings->Time & Language->Change Administrative Language). The setting is actually for "Change the language of Non-Unicode Programs," which I changed to Arabic (Algerian). Then in the properties of the form I set the "Right to Left" property to True. From there the form still drew a partial ghost right scrollbar at first, so I also had to add an unusual timed message box:
Dim AckTime As Integer, InfoBox As Object
Set InfoBox = CreateObject("WScript.Shell")
'Set the message box to close after 10 seconds
AckTime = 1
Select Case InfoBox.Popup("Please wait.", AckTime, "This is your Message Box", 0)
Case 1, -1
End Select
I tried everything to get the screen to redraw again to show the first text box in it's proper alignment in the form, instead of partially underneath or at least immediately adjacent to the scrollbar instead of 4 pixels to the right where I wanted it. Finally I got this off another Stackoverflow post (which I now can't find or I would credit it) that worked like a charm:
Me.Frame1.Visible = False
Me.Frame1.Visible = True
In my case the problem was in trying to make one shape visible and another one invisible on a worksheet.
This is my approach to "inactivating" a button [shape] once the user has clicked it. The two shapes are the same size and in the same place, but the "inactive" version has dimmer colors, which was a good approach, but it didn't work, because I could never get the screen to update after changing .visible = FALSE to = TRUE and vice versa.
None of the relevant tricks in this thread worked. But today I found a solution that worked for me, at this link on Reddit
Essentially you just call DoEvents twice in immediate succession after the code that makes the changes. Now why? I can't say, but it did work.
I've been trying to solve this Force a screen update on a Worksheet (not a userform) for many years with limited success with
doevents and scrolling etc.. This CH Oldie solutions works best with a slight mod.
I took out the Wait and reset ScreenUpdating and EnableEvents back to true.
This works office excel 2002 through to office 365
Sub Sheet1Mess(Mess1 As String)
Sheet1.Range("A6").Value = Mess1
ForceScreenUpdate
End Sub
Sub ForceScreenUpdate()
Application.ScreenUpdating = True
Application.EnableEvents = True
' Application.Wait Now + #12:00:01 AM#
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Related
Dynamically update and display the contents of either a TextBox or Label in VBA [duplicate]
My Excel tool performs a long task, and I'm trying to be kind to the user by providing a progress report in the status bar, or in some cell in the sheet, as shown below. But the screen doesn't refresh, or stops refreshing at some point (e.g. 33%). The task eventually completes but the progress bar is useless. What can I do to force a screen update? For i=1 to imax ' imax is usually 30 or so fractionDone=cdbl(i)/cdbl(imax) Application.StatusBar = Format(fractionDone, "0%") & "done..." ' or, alternatively: ' statusRange.value = Format(fractionDone, "0%") & "done..." ' Some code....... Next i I'm using Excel 2003.
Add a DoEvents function inside the loop, see below. You may also want to ensure that the Status bar is visible to the user and reset it when your code completes. Sub ProgressMeter() Dim booStatusBarState As Boolean Dim iMax As Integer Dim i As Integer iMax = 10000 Application.ScreenUpdating = False ''//Turn off screen updating booStatusBarState = Application.DisplayStatusBar ''//Get the statusbar display setting Application.DisplayStatusBar = True ''//Make sure that the statusbar is visible For i = 1 To iMax ''// imax is usually 30 or so fractionDone = CDbl(i) / CDbl(iMax) Application.StatusBar = Format(fractionDone, "0%") & " done..." ''// or, alternatively: ''// statusRange.value = Format(fractionDone, "0%") & " done..." ''// Some code....... DoEvents ''//Yield Control Next i Application.DisplayStatusBar = booStatusBarState ''//Reset Status bar display setting Application.StatusBar = False ''//Return control of the Status bar to Excel Application.ScreenUpdating = True ''//Turn on screen updating End Sub
Text boxes in worksheets are sometimes not updated when their text or formatting is changed, and even the DoEvent command does not help. As there is no command in Excel to refresh a worksheet in the way a user form can be refreshed, it is necessary to use a trick to force Excel to update the screen. The following commands seem to do the trick: - ActiveSheet.Calculate - ActiveWindow.SmallScroll - Application.WindowState = Application.WindowState
Put a call to DoEvents in the loop. This will affect performance, so you might want to only call it on each, say, 10th iteration. However, if you only have 30, that's hardly an issue.
#Hubisans comment worked best for me. ActiveWindow.SmallScroll down:=1 ActiveWindow.SmallScroll up:=1
Specifically, if you are dealing with a UserForm, then you might try the Repaint method. You might encounter an issue with DoEvents if you are using event triggers in your form. For instance, any keys pressed while a function is running will be sent by DoEvents The keyboard input will be processed before the screen is updated, so if you are changing cells on a spreadsheet by holding down one of the arrow keys on the keyboard, then the cell change event will keep firing before the main function finishes. A UserForm will not be refreshed in some cases, because DoEvents will fire the events; however, Repaint will update the UserForm and the user will see the changes on the screen even when another event immediately follows the previous event. In the UserForm code it is as simple as: Me.Repaint
This worked for me: ActiveWindow.SmallScroll down:=0 or more simply: ActiveWindow.SmallScroll 0
I couldn't gain yet the survey of an inherited extensive code. And exact this problem bugged me for months. Many approches with DoEnvents were not helpful. Above answer helped. Placeing this Sub in meaningful positions in the code worked even in combination with progress bar Sub ForceScreenUpdate() Application.ScreenUpdating = True Application.EnableEvents = True Application.Wait Now + #12:00:01 AM# Application.ScreenUpdating = False Application.EnableEvents = False End Sub
This is not directly answering your question at all, but simply providing an alternative. I've found in the many long Excel calculations most of the time waiting is having Excel update values on the screen. If this is the case, you could insert the following code at the front of your sub: Application.ScreenUpdating = False Application.EnableEvents = False and put this as the end Application.ScreenUpdating = True Application.EnableEvents = True I've found that this often speeds up whatever code I'm working with so much that having to alert the user to the progress is unnecessary. It's just an idea for you to try, and its effectiveness is pretty dependent on your sheet and calculations.
On a UserForm two things worked for me: I wanted a scrollbar in my form on the left. To do that, I first had to add an Arabic language to "Change administrative language" in the Language settings of Windows 10 (Settings->Time & Language->Change Administrative Language). The setting is actually for "Change the language of Non-Unicode Programs," which I changed to Arabic (Algerian). Then in the properties of the form I set the "Right to Left" property to True. From there the form still drew a partial ghost right scrollbar at first, so I also had to add an unusual timed message box: Dim AckTime As Integer, InfoBox As Object Set InfoBox = CreateObject("WScript.Shell") 'Set the message box to close after 10 seconds AckTime = 1 Select Case InfoBox.Popup("Please wait.", AckTime, "This is your Message Box", 0) Case 1, -1 End Select I tried everything to get the screen to redraw again to show the first text box in it's proper alignment in the form, instead of partially underneath or at least immediately adjacent to the scrollbar instead of 4 pixels to the right where I wanted it. Finally I got this off another Stackoverflow post (which I now can't find or I would credit it) that worked like a charm: Me.Frame1.Visible = False Me.Frame1.Visible = True
In my case the problem was in trying to make one shape visible and another one invisible on a worksheet. This is my approach to "inactivating" a button [shape] once the user has clicked it. The two shapes are the same size and in the same place, but the "inactive" version has dimmer colors, which was a good approach, but it didn't work, because I could never get the screen to update after changing .visible = FALSE to = TRUE and vice versa. None of the relevant tricks in this thread worked. But today I found a solution that worked for me, at this link on Reddit Essentially you just call DoEvents twice in immediate succession after the code that makes the changes. Now why? I can't say, but it did work.
I've been trying to solve this Force a screen update on a Worksheet (not a userform) for many years with limited success with doevents and scrolling etc.. This CH Oldie solutions works best with a slight mod. I took out the Wait and reset ScreenUpdating and EnableEvents back to true. This works office excel 2002 through to office 365 Sub Sheet1Mess(Mess1 As String) Sheet1.Range("A6").Value = Mess1 ForceScreenUpdate End Sub Sub ForceScreenUpdate() Application.ScreenUpdating = True Application.EnableEvents = True ' Application.Wait Now + #12:00:01 AM# Application.ScreenUpdating = False Application.EnableEvents = False Application.ScreenUpdating = True Application.EnableEvents = True End Sub
How to fix Excel-2016 not running Excel-2010 VBA code for printing correctly?
This loop runs typically 4 to 8 times. It updates 30+ textboxes and prints. Has been working great. But we updated from office 2010 to 2016 (and to Office 365), so now it runs but all the textboxes on the printed pages have the same value from iteration 1. Happens on all printers including PDFcreator. But afterwards the sheet is in the state I expect for the last iteration. It's like the code outruns the printer. But adding a delay of even 10 sec does not help. Oddly, as I try different things I see on the first iter that the textboxes update (from the previous runs last iter). Seems like it should update every iter. Sub printParamSheets() On Error GoTo errHandler Dim Bin1_Matl, Bin1_Parts 'Declaring types won't help or speed up Dim iCond, conditions Application.EnableEvents = True 'Is not helping with issue For iCond = 1 To conditions With Sheet2 'Assign from sheet2 to variables Bin1_Matl = .Range("A64").Offset(0, iCond * 2).Value2 Bin1_Parts = .Range("B64").Offset(0, iCond * 2).Value2 'about 30 more of these End With With Sheet8 'Assign Sheet8 named ranges from variables above. 'Could skip intermed vars but nice for debugging. 'ALL LINKED TO ACTIVEX TEXT BOXES on Sheet8, atop an image. .Range("Bin1Matl").Value2 = Bin1_Matl .Range("Bin1Parts").Value2 = Bin1_Parts 'about 30 more of them .Calculate 'Is not helping with issue Dim ctrl As OLEObject 'Is not helping with issue For Each ctrl In .OLEObjects If TypeName(ctrl.Object) = "TextBox" Then 'ctrl.Update 'error, but was not in 2010 anyway End If Next ctrl Application.Wait (Now + TimeValue("00:00:05")) 'Is not helping Application.ScreenUpdating = True 'Never toggled off , no help DoEvents 'Is not helping with issue .PrintOut from:=1, To:=1, Copies:=1, Collate:=True End With Next iCond Exit Sub errHandler: Application.EnableEvents = True 'Don't need but cannot hurt Resume Next End Sub I will try skipping the extra intermediate assignments, going straight from sheet2 to the textboxes. But I'd like to know the cause because I have other code 'in the wild' that doesn't necessarily use any activex objects that may be affected. Hoping the symptom is not unique to our site and so others may benefit from an answer.
Click event on one control sometimes does not fire
I have two buttons on a sheet. On occasion, clicking on one of the buttons does not "fire" the event. Floating the cursor over the button changes the cursor from a cross to an arrow The button does not appear to "depress" as it would if the event fired. Breakpoint or Stop statement in the code do not get reached. The other button never demonstrates this problem. If the code is started manually, the code will run as designed. In Designer Mode, the code appears to be attached to the button. Closing and re-opening the worksheet, and then triggering the button that normally works will restore functionality to this button. Trouble shooting advice would be appreciated. The button code is below as is the properties window, but I think the problem lies elsewhere; just don't know where to look. Again, another very similar button, which calls different code, seems to work OK all the time. Thanks for any guidance. Option Explicit Private Sub cbDeleteViewed_Click() With cbDeleteViewed .Width = .Width End With Application.ScreenUpdating = False Select Case Environ("COMPUTERNAME") Case "RON-DODIER" sDrive = "F:\" sBasePath = "Videos" ProtectDisable Sheet1 UnProtectEnable Sheet3 Sheet3.Select Case "RONBP" sDrive = "Z:\" sBasePath = "" ProtectDisable Sheet3 UnProtectEnable Sheet1 Sheet1.Select Case Else MsgBox "Cannot Run on This Computer" Exit Sub End Select SetUpZ DeleteViewedShows Application.ScreenUpdating = True End Sub
Shapes.Visible True and False within Loop VBA
I have this piece of code which I would like to show and hide some Shape objects one by one, in order to make a little animation. However, nothing happens as the code executes, all images are shown by once when the code stops running. Sub test() For i = 1 To 4 Sheets("Game").Shapes("North" & i).Visible = True Sleep 500 'Sheets("Game").Shapes("North" & i).Visible = False 'by setting it to false i'd like to achieve the animation effect Debug.Print i DoEvents Next i End Sub
DoEvents allows other code (e.g. Excel's own) to run and handle things like user clicking on another worksheet (which invokes any Worksheet.Change or Workbook.WorksheetChange handler)... or just repainting itself. By invoking DoEvents once per loop, Excel doesn't get a chance to repaint between the visibility toggles: it's already busy running your loop. So you need to toggle visibility on, let Excel repaint (DoEvents), sleep for your animation delay (500ms seems a tad slow IMO), then toggle visibility off and let Excel repaint again, i.e. invoke DoEvents one more time. If the Game worksheet is in ThisWorkbook, then I'd warmly recommend you give it a CodeName - select it in the Project Explorer, then look at its properties (F4) and change its (Name) to, say, GameSheet. This gives you a global-scope object variable so that you don't need to dereference the same worksheet twice per iteration - heck you could even dereference its Shapes collection only once: Private Const ANIMATION_DELAY As Long = 100 Sub test() With GameSheet.Shapes For i = 1 To 4 Dim currentShape As Shape Set currentShape = .Item("North" & i) currentShape.Visible = True DoEvents Sleep ANIMATION_DELAY currentShape.Visible = False DoEvents Debug.Print i Next End With End Sub
Amended the code by setting DoEvents after toggling True and Falseand now it works: Sub test() For i = 1 To 4 Sheets("Game").Shapes("North" & i).Visible = True DoEvents Sleep 100 Sheets("Game").Shapes("North" & i).Visible = False DoEvents 'by setting it to false i'd like to achieve the animation effect Debug.Print i Next i End Sub
DisplayFullScreen and DisplayFormulaBar Not Playing Well Together
I was making an application and I went to hide the formula bar and make it full screen in my Workbook_Activate event, then show the formula bar and make it windowed(is that a word?) in my Workbook_Deactivate event. I actually struggled with this for a while. I kept having a problem with the formula bar showing up when it wasn't supposed to or disappearing when I wanted it there. I finally got it to work by making sure I used the DisplayFullScreen first and only then using the DisplayFormulaBar method. Does anyone know why you would need to put these in a specific order for them to work together? I couldn't find anything when I was looking for it. I'm using Excel 2010. EDIT: Here's my code. Private Sub Workbook_Activate() Application.ScreenUpdating = False ThisWorkbook.Sheets(2).Activate ActiveWindow.DisplayGridlines = False ActiveWindow.DisplayHeadings = False ThisWorkbook.Sheets(1).Activate ActiveWindow.DisplayGridlines = False ActiveWindow.DisplayHeadings = False Application.DisplayFullScreen = True Application.DisplayFormulaBar = False Application.ScreenUpdating = True End Sub Private Sub Workbook_Deactivate() Application.DisplayFullScreen = False Application.DisplayFormulaBar = True End Sub
This is from the MSDN documentation: "Toolbars, the status bar, and the formula bar maintain separate display settings for full-screen mode and normal mode." https://msdn.microsoft.com/en-us/library/office/ff838060.aspx I was searching for mostly DisplayFormulaBar topics since that was what was showing up strangely for me. Hopefully this helps anyone who searches for it in the future.