How to allow user to review worksheet using excel vba - excel

My macro generates a series of reports that are 60 columns wide. I want users to be able to review the reports on screen before printing them or going on to another segment of the macro.
it there a way to set a scrollarea, have user review it, and then have the respond to a message box to continue the routine?
I tried this:
Sub reviewdata()
' Application.ScreenUpdating = False
Worksheets("Fin. Stmts").ScrollArea = ""
Application.Goto Reference:="monthlydata"
ActiveCell.Offset(2, 1).Select
ActiveWindow.FreezePanes = True
Worksheets("data. Stmts").ScrollArea = "monthlydata"
If MsgBox("End Review", vbOKOnly) = vbOK Then
End If
ActiveWindow.FreezePanes = False
Worksheets("data. Stmts").ScrollArea = ""
End Sub
the problem is that once the if, then statement is executed the user can not move around the worksheet since the routine needs a response to continue.
any insights are most appreciated.
thanks.

You can Use a Dummy Variable:
Dim dummy As Range
Set dummy = Application.InputBox("Scroll and Check. After That Select Ok!", "This is Specially created so that you can", Default:="A1", Type:=8)
Input Box that Takes in Range Allows you to Scroll in Background. Keep hitting Ok in and nothing will change, code will run as it is running at the moment.

This is a little clumsy but it sort of gets what you want. Instead of using a MsgBox use and InputBox as a range, which will allow the user to click around and scroll, as you describe. Whenever they hit okay/cancel, the macro will continue.
So probably replace your MsgBox line of code with....
Dim boom As Variant
boom = Application.InputBox("When you're done hit ""Cancel""... (""OK"" might lead to problems...)", _
"Scroll around and look at stuff", _
, , , , , 8)
I would recommend doing two macros instead, but this probably does what you need.

You can show that message in a small userform and call that userform in modeless state as shown below.
UserForm1.Show vbModeless
This way you will be able to navigate in the sheet with that message still showing.
You can also put the rest of the code in the button click event as shown below.
Option Explicit
Private Sub CommandButton1_Click()
ActiveWindow.FreezePanes = False
Worksheets("data. Stmts").ScrollArea = ""
Unload Me
End Sub

Related

Sheet.activate not activating specified sheet

So I have officially run into a brick wall. I have written a macro that will allow the user, upon password entry, to unlock and unhide all sheets except for the one containing the password. At the end of the macro, I want to go to a specific sheet.
I have tried every reference to that sheet known to mankind, including both sheet code names and indexes, but the "sheet.activate" event just will not trigger. I have tried setting screen updating to true, both before and after the sheet.activate command. Events are enabled. I have disabled all Excel addins. I have tried everything I can possibly think of and everything I've found in forums all over the web. As you'll see from the code, I've even added basic time delays to the code at each step of the activating sequence, all to no avail. I've tried the activate sequence at different parts of the code. At the completion of the code, the best I get is activate the first sheet in my workbook. I even have Option Explicit enabled, and it sheds no light.
Now, the weird thing is, if I take everything out of the code except for the activate events, it works perfectly. If I step through the code line by line, the activate lines work. But if I compile the code and run the whole sub - nada. What the heck am I missing here?
Here is my complete code for this sub:
Option Explicit
Sub UnProtectAll()
Application.ScreenUpdating = False
Dim pPrompt As String
Dim bkPswrd As String
inputPass_box.Show
pPrompt = inputPass_box.passInput.Value
bkPswrd = Worksheets("Password List").Cells(3, 2)
If pPrompt = "" Then
MsgBox "You didn't enter anything...", vbInformation, "No password"
UnProtectAll
ElseIf pPrompt = bkPswrd Then
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
ws.Unprotect bkPswrd
Next ws
ThisWorkbook.Worksheets("Folder History").Visible = xlSheetVisible
ThisWorkbook.Worksheets("Master List").Visible = xlSheetVisible
ThisWorkbook.Worksheets("File Formats").Visible = xlSheetVisible
Unload inputPass_box
Worksheets("Change Sheet").Shapes("Button 1").Visible = False
Application.ScreenUpdating = True
Application.Wait (Now + TimeValue("0:00:03"))
ThisWorkbook.Worksheets("Folder History").Select
ThisWorkbook.Worksheets("Folder History").Activate
Application.Wait (Now + TimeValue("0:00:03"))
ThisWorkbook.Worksheets("Change Sheet").Select
ThisWorkbook.Worksheets("Change Sheet").Activate
Else
MsgBox "You have entered an incorrect password. Please check your password and try again.", vbCritical, "Wrong Password!"
UnProtectAll
End If
End Sub
Nevermind. I just figured it out, but here's my answer in case someone else needs help with the same issue. The line
Unload inputPass_box
was throwing it off for some reason ("inputPass_box" is a UserForm used as an input box). If someone would be so kind as to set me straight on this, I would be much obliged, but to my very limited knowledge, it seems that this line triggers the UserForm_QueryClose event, as in my project this event contains and "End" command to prevent the userform from passing any data back to the sub, as this would trigger several other events when I want closing or cancelling the user form to stop rather than displaying any of the error messages contained in the code block above.
pPrompt = InputBox("please input password")
bkPswrd = Worksheets("Password List").Cells(3, 2)
If pPrompt = "" Then
MsgBox "You didn't enter anything...", vbInformation, "No password"
UnProtectAll
ElseIf pPrompt = bkPswrd Then
use "InputBox" no problem,There may be a problem with the inputPass box

How I make excel show a await message while execute a macro?

I have a macro that consulte to Database (in this case is a excel workbook), but when the macro consulte to database, it take 30 sec, and the user thinks that the program is broke. I tried with "Application.StatusBar = "Refresh File. "", but the user canĀ“t see the sentence, the best way is a msgbox with the message "Await a moment", and, when the macro is finished, this message is closed. Do you help me with the code? I think the solution is this way:
Sub Button1_Click()
'Call msgbox with message "Await a moment"
Dim x As Long
For x = 0 To 2000000000 Step 1
Next
'The msgbox is closed
End Sub
I think the solution is a msgbox but it is a another option, Go Ahead!
A Msgbox wont work, because it would stop execution of everything until the user clicks OK.
The statusbar would work, if you put a DoEvents right after you set it so the system has a moment to update the screen.
Your only other option would be to create a special form that you show while it's processing, but you would also need a DoEvents after you show it as well.
Sub Test()
' create a userform and name it frmWait
' put whatever you want on that form
' that is what will be displayed while the user waits
frmWait.Show vbModeless
DoEvents
' do your processing here
Unload frmWait
End Sub
Or:
Sub Test()
Application.StatusBar = "Refreshing File. "
DoEvents
' do your processing here
' reset the status bar to normal
Application.StatusBar = ""
End Sub

Drawing shapes to screen falling behind other code

I am trying to create a small tutorial for one of my Excel Applications and I am running into the issue where I'm trying to draw a text shape to the screen to give advice on what to enter into an InputBox but the InputBox gets displayed before the text shape, however, when running in debug mode and stepping through the code it all works fine.
There is one userform ufNext which only contains one button, ufNext. The click event code for this button contains a Select Case clause to determine what to do each time it is clicked. The value the clause is checking is a Public variable, tutSectionsRun
Option Explicit
Private Sub btnNext_Click()
Select Case tutSectionsRun
Case 1
Call Section2
Case 2
Call Section3
Call MPFilterString
' Case N
' ...
End Select
End Sub
The code starts in Section1 which just sets the position of ufNext and shows the form then sets the global variable tutSectionsRun to 1.
The user clicks the "Next" button on the ufNext form and it calls Section2 which re-positions the form (there would normally be other code in these "Section" procedures), and sets the global variable to 2.
Again, the user clicks the "Next" button but this time there is the issue where before the shapes are drawn to the screen, I get the InputBox popping up first and only after it closes the text shape tutText is drawn to the screen.
Option Explicit
Public tutSectionsRun As Long
Sub Section1()
ufNext.Left = 550
ufNext.Top = 450
ufNext.Show
tutSectionsRun = 1
End Sub
Sub Section2()
ufNext.Left = 910
ufNext.Top = 350
tutSectionsRun = 2
End Sub
Sub Section3()
Dim tutText As Shape
Set tutText = ActiveSheet.Shapes.AddLabel(msoTextOrientationHorizontal, 600, 300, 200, 100)
tutText.TextFrame2.TextRange.Text = "Enter the string ""gr"" into the input box."
tutText.Locked = False
ufNext.Hide
tutSectionsRun = 3
End Sub
Sub MPFilterString()
Dim s As Variant
Application.ScreenUpdating = False
s = Application.InputBox("Enter string to filter out.", "Filter String.")
If s = False Then Exit Sub
End Sub
**Edit : I forgot to mention that the userform is non-modal. Otherwise execution would pause on the call to ufNext.Show and clicking next would call the event handler before the tutSectionsRun variable had been set to 1
Thanks to #BrakNicku who confirmed my suspicions in the comments, saying that the InputBox was preventing the screen from being refreshed for the text shape from ActiveSheet.Shapes.AddLabel to be displayed.
Their link to this answer offered some suggestions.
What I found was that adding either of these before the InputBox was called would force the screen to refresh but only if Application.ScreenUpdating = False was removed, or at least just moved further down in the code.
- ActiveSheet.Calculate
- ActiveWindow.SmallScroll
- Application.WindowState = Application.WindowState
I always like to call Application.ScreenUpdating = False at the top of my procedures, so I went with a different approach, thinking that the problem was that the time to refresh the screen with shapes drawn was longer than the time between the instruction to do so and the instruction to draw the InputBox to screen. So, I thought a slight delay before calling the InputBox might be a better choice for me, probably not for everyone but I felt better about doing it this way. (Application.ScreenUpdating = False is a good friend of mine and I didn't want to see her go, or even be relocated) So I just made a small wait procedure.
Sub Wait(secs As Single)
Dim finishTime As Single
finishTime = Timer + secs
Do While Timer < finishTime
DoEvents
Loop
End Sub
And called it at the top of the MPFilterString procedure. It only takes about 50ms for the shapes to show on screen but I give it 100ms to be safe.
Sub MPFilterString()
Dim s As Variant
WaitFor (0.1)
Application.ScreenUpdating = False
s = Application.InputBox("Enter string to filter out.", "Filter String.")
If s = False Then Exit Sub
' more code ...
End Sub

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

Print Range User Selected

I was hoping to create a module that would basically operate like so:
Define 4 or 5 print ranges;
Prompt a user an input box;
Allow the user to select, from a drop down in that input box, the range they wish to print;
After selecting the range, they hit OK, and are prompted by a "are you sure?" box to prevent mistaken clicks.
I'm fairly lost on this and I honestly feel like the code I've been writing will be less help than just articulating the problem.
I have had it work by the user defining the range (manually selecting the columns they wish to print), but that's not what I'm looking for.
One step further, would it be possible to allow for the customization of the print format (landscape vs portrait, and paper type) even further?
Thanks so much for the help in advance, I'll do my best to answer questions and provide samples of the code I referenced above (just a prompt that allows you to select the columns. I need it to be a defined range, by name, range1=a2:c14 or something like that, because the end user is not a great excel user.
See below:
Sub SelectPrintArea()
Dim PrintThis As Range
ActiveSheet.PageSetup.PrintArea = ""
Set PrintThis = Application.InputBox _
(Prompt:="Select the Print Range", Title:="Select", Type:=8)
PrintThis.Select
Selection.Name = "NewPrint"
ActiveSheet.PageSetup.PrintArea = "NewPrint"
ActiveSheet.PrintPreview
End Sub
As a follow-up:
Assume the document has hidden sections, would it be able to unhide those sections if they are part of a user defined range (like if it was part of a grouping). Would this work on a protected document?
In order to present a list of names to the user, you'll need a UserForm similar to this:
The code behind that form would look like the below. I've used Print preview in favor of an "Are you sure" message, because it's a more elegant UX.
Option Explicit
Private Sub UserForm_Initialize()
With Me.cboPrintAreas
.MatchRequired = True
'Add named ranges to the listbox
.AddItem "Report_1"
.AddItem "Report_2"
.AddItem "Report_3"
.AddItem "Report_4"
.AddItem "Report_5"
'Set the default report
.Value = "Report_1"
End With
End Sub
Private Sub btnCancel_Click()
Unload Me
End Sub
Private Sub btnPrint_Click()
Dim rng As Range
Set rng = Range(Me.cboPrintAreas.Value)
With rng.Worksheet
'Do a crude assignment of paper orientation
If rng.Height > rng.Width Then
.PageSetup.Orientation = xlPortrait
Else
.PageSetup.Orientation = xlLandscape
End If
.PageSetup.PrintArea = rng.Address
Me.Hide
.PrintOut Preview:=True, IgnorePrintAreas:=False
Unload Me
End With
End Sub
And you'd display the form from a Standard module with code like:
Sub test()
UserForm1.Show
End Sub
If you want to unhide hidden rows/columns, you'll need to ensure you have the range's sheet suitably unprotected.

Resources