I am running a lot of code for creating some reports.
To see how far the creation of reports has proceeded, I want to show a loading bar on my control sheet.
Therefore I use conditional formatting to create a loading bar in a cell. When a report (=a worksheet) is finished with calculating, the loading bar has reached 100% (full).
The problem I have now shows in updating these loading bars.
When running my code, different macros are executed. The loading bars work for 4-5 Macros, after that the screen freezes until all macros are finished. Where is the problem here?
The filling of the progress bar is done by another macro that turns on screenupdating, sets a new progress value on the sheet and turns screenupdating off.
I recently added a Progress Indicator form to one of my files.
I created a UserForm, added a frame, and a label inside that frame, and a couple of other labels that I could update with specifics later.
I load the userform at the start, and pass the % that has completed and what I want the labels to say to this
Public Sub ProgressUpdate(ByVal dCalc As Double, ByVal Process As String)
With ProgIndi
If .Visible Then
.Process.Caption = Process & "..."
.PercentCompleted.Caption = Int(dCalc * 100) & "% Completed."
.Bar.Width = dCalc * 200
DoEvents
End If
End With
End Sub
At the end of the macro, unload the form. You can leave the ScreenUpdating at False and the form still updates.
Put DoEvents in your loop so that Excel 'catches up' with any actions
Put .Repaint in your loop for your form so that the form itself updates
Related
I have a requirement for VBA script in Microsoft Word to pause so that the user can select text that will be copied to the clipboard so that it can exported to an Excel file. The user will make a number of selections and finally indicate he/she is done when the contents of the clipboard will be copied to a template Excel file.
I have the code working to copy each selection to the clipboard and then all rows to the Excel file. But I need assistence in figuring out how to pause the code to allow the user to make the selection and then restart the code to copy the selection to the clipboard. I am able to get the userform with toggle switch to switch states and labels when pressed. But have not figured out how to pause the VBA code to allow the user to navigate to the next section of the Word document for the next selection.
The Stakeoverflow question/answer below appears to address this requirement but I have not been able to get it to work. It appears that the code is incomplete.
Pause VBA macro, allow user to make a selection, and restart where it left off
Can someone provide example VBA code that accomplishes this?
Your assistence is much appreciated as I have been beating my head against the wall and it is starting to hurt!
There's no way in VBA to "pause" a macro. Code must run to completion... Unless there's a command for user input.
Input can be requested via the InputBox and MsgBox methods, but those block access to the document because they're modal. A UserForm, however, can be set to display as non-modal, meaning it stays on top, but doesn't block access to the document or the application features. Since you're already working with a UserForm, this can be implemented relatively easily.
In the small example below, the Continue button runs the code to perform an action on the user selection. When Done is clicked the entire code is exited and the form unloaded.
Code behind the user form
Option Explicit
Private Sub cmdContinue_Click()
Debug.Print Selection.Range.Text
End Sub
Private Sub cmdDone_Click()
Me.Hide
End Sub
Private Sub UserForm_Activate()
'Position the form near the top-left of the window
'So that the user can work with the document
Me.Top = Application.ActiveWindow.Top + 50
Me.Left = Application.ActiveWindow.Left + 50
End Sub
Code in a regular module
Option Explicit
Sub DisplayModeless()
Dim frm As frmModelessForInput
Set frm = New frmModelessForInput
frm.Show False 'Display as non-modal
Set frm = Nothing
End Sub
I have a checkbox "Check_Group" in a worksheet "FA 2.0 Pricing Worksheet". I have a subroutine defined in said worksheet that changes some values elsewhere when the user clicks on it:
Private Sub Check_Group_Click()
Active = ActiveSheet.Name
Sheets("FA 2.0 Pricing Worksheet").Activate 'This activate has to happen here because there are some steps in a different routine where this checkbox has to be clicked programmatically
If ActiveSheet.OLEObjects("Check_Group").Object.Value = True Then
Sheets("FA 2.0 Risk Checklist").Range("UI_GROUP_RISK").Value = "No"
Else
Sheets("FA 2.0 Risk Checklist").Range("UI_GROUP_RISK").Value = "Yes"
End If
Sheets(Active).Select
End Sub
This works like a charm, except I've noticed a particularly annoying side-effect - the keyboard seems to lock after you click on it!
I can click into cells and navigate around the sheet with no problem, but when I try to type into (an unprotected) cell it doesn't react to my key presses at all.
I have confirmed with the debugger that EnableEvents and ScreenUpdating are both on.
I can skirt around the issue by clicking to another tab and back - typing works as normal then... in the exact same cells where it failed to type before! I'd still like to find a solution rather than just this cheap workaround though.
This is in Excel 2016.
Why does my subroutine lock the keyboard?
There's no need to activate the sheet for your code to execute.
And, when you're in a sheet code module you can refer to the sheet using Me
Private Sub Check_Group_Click()
Me.Range("UI_GROUP_RISK").Value = IIf(Me.Check_Group.Value, "No", "Yes")
End Sub
All:
Thank you in advance, you all have been a tremendous resource!!!
I have a couple of spreadsheets where the sheet is protected, but users can still use filters. I'm processing most of the sheets automatically, but what I need to do, is present the user with the sheets that need to be filtered, then have them select a "Finish" type button or toolbar entry, which I already have.
What I need to be able to do, is to bring this sheet up, pause the macro, if possible, while they make their changes (could be up to 5 filters that they select before the sheet is ready.
Then, copy the visible cells only to a specific sheet and then resume the macro.
I don't think that Worksheet change event will do this.
I'm thinking more on the lines of maybe setting a flag on a spare sheet, firing up the next macro and then see if it can find the original macro and pick up where it is flagged?
I thought about a modeless userform that the user could click OK on and then call the next macro, but that does not work.
The calling code is:
UserForm3.Show
CopyToDisplay "AEP"
LastPos = LastPos + 1
Where AEP is the sheet name to copy the filtered rows from.
Userform displays, but clicking ok does nothing and of course, the macro keeps on going.
Any suggestions would be greatly appreciated!
Thanks,
Jeff
Jeff let's try this. Your current code:
UserForm3.Show
CopyToDisplay "AEP"
LastPos = LastPos + 1
When we display a UserForm, the default behavior is vbModal, which essentially freezes the application and the user cannot interact with anything but the UserForm, that is not what you want. What you need is a way to display the form, and then just wait for the user to signal that s/he is finished with the input.
So we need to modify a few things:
The UserForm needs to effectively "pause" while also allowing the user to interact with the worksheet. A vbModal form can't do this (it pauses, without interaction), and really neither can a vbModeless (it continues execution AND allows interaction).
Conundrum? No. we can simulate a pause with the vbModeless form, and preserve the user's ability to interact with the sheet. The best of both worlds!!
We will show the form with the optional vbModeless, this allows the user to interact with the rest of the Application /worksheets/etc. Since a modeless form continues code execution, we need to simulate a pause and we can do this with a Loop. The loop will run indefinitely, and only break once the UserForm is closed, at which point the rest of the code will continue to execute.
UserForm3.Show vbModeless
Do While UserForm3.Visible
DoEvents
Loop
LastPos = LastPos + 1
'You MAY need to reset some variables, if the Filter/Autofilter has affected these/etc.
Design-wise, give your form a single Label control and set its .Caption property (and the form's .Caption property) in some useful/instructive way. You could add a command button but that seems unnecessary, since all the button would do is invoke the Terminate event (which can always be done with the red "X")
For your issue with copying (apparent failure to paste), try changing this:
Sheets("AEP").Select
With ActiveSheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy _
Destination:=Sheets("Display").range("A" & LastPos)
.AutoFilterMode = False
Application.CutCopyMode = False
End With
To this:
With ActiveSheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy
Sheets("Display").range("A" & LastPos).PasteSpecial
.AutoFilterMode = False
Application.CutCopyMode = False
End With
I couldn't observe the number of rows completed during run time. Hence I was thinking why not just come out few line of codes to display the number of rows completed in run time.
Follow are my implementation:
Dim ownCntrl As Control
Set ownCntrl = UserForm1.Controls.Add("Forms.TextBox.1")
With ownCntrl
.Name = "RowNoTextBox"
.Value = "test"
.Width = 150
.Height = 25
.Top = 10
.Left = 10
.ZOrder (0)
End With
UserForm1.Show
Following are my queries:
1)Beside TextBox Object, is there any other Object like label in Java to display information in VBA?
2)Without Created a UserForm called "UserForm1" initially, can I create the TextBox Object or other suitable Object Directly during run time? Cause, without called the UserForm1.Show function, the TextBox Object won't display
3)How can I remain the UserForm or TextBox to display during run time without users' interaction to close it to proceed?
I'm just start to learn VBA. Appreciate you guys' suggestions. Thanks a lot.
According to some additional information placed in comments the simplest idea to keep information about progress of your subroutine is to use Excel status bar. Therefore we put within our subroutine something like this:
Application.StatusBar = "Current progress- Row No.: " & rowNum
where rowNum is a variable (loop variable) representing current row.
Which is important- you need to return standard status bar behaviour calling this line before end of your procedure:
Application.StatusBar = False
I have a macro that takes roughly five seconds to execute and I'm using the code Application.ScreenUpdating = False so it looks like Word has locked up when it's running.
I use a message box at the end to tell them it's complete, but I'd like to display an image during its execution that tells the user the macro is running (primarily so they don't get worried, but also because it's nicer to look at).
There might be an easier way to go about this, but I decided to create an image, create a very basic user form, and simply set my image as its background. Then, I have that user form pop up as soon as the code starts to run.
The problem I'm having is that unless I close it, the rest of the code won't execute. It just hangs until I do something. Is there a way I can get the user form to be displayed without stopping the rest of the process? If not, is there another approach I can take to display an image while the macro is running?
You can show the userform as non-modal:
Userform1.Show False
'do more stuff (doesn't wait for form to close)
Userform1.Hide
I too have found that if you .show a userform at the beginning of the process, images contained within userform take a while to display and the result is less than smooth.
I have solved it by inserting a 1 second "wait" immediately after the userform.show instruction. In that split second, the image loads and displays successfully and the result is seamless.
Here the code. I have 2 macros: 1 to display and 1 to hide. I call the first macro at the beginning of the process just before freezing the screen and the second at the end of the process just before unfreezing the screen
Userform name is "myuserform"
Sub message_show()
userform.Show False
Application.Wait (Now + TimeValue("0:00:01"))
End Sub
Sub message_hide()
Application.Wait (Now + TimeValue("0:00:02"))
userform.Hide
End Sub