Hiding the form while printing - strange behavior - excel

I have created a form with a button that allows the user to print the selected sheet. Her absolutely minimized form is below.
Option Explicit
Private Sub btnPrint_Click()
lblHideCounter = lblHideCounter + 1
Me.Hide
Application.Dialogs(xlDialogPrint).Show
Me.Show False
lblShowCounter = lblShowCounter + 1
MsgBox "bntHide_Click, Show Counter: " & lblShowCounter
End Sub
Private Sub UserForm_Terminate()
MsgBox "UserForm_Terminate, Show Counter: " & lblShowCounter
End Sub
So that the form does not disturb the user when the printing dialog box is displayed, I decided to hide it and then show it back after printing. However, I noticed a rather unexpected behavior. Well, if the form is displayed in modal mode, the code that follows the Show command of the form will be run only after the form is finally closed (even after UserForm_Terminate event). If, on the other hand, the form is displayed in modeless mode, such a problem does not occur.
For some reasons, however, I would like my form to be displayed in modal mode. However, I am concerned that this behavior may result in problems (e.g. stack utilization?), if the user repeatedly presses print without closing the mold.
Can this be solved in some other way? For example, is it possible somehow (without using the Hide method) to hide the form while the print dialog is displayed and then display it again (without using the Show method)? Or maybe I shouldn't worry about this problem?
The file with example code can be downloaded from here..

Related

Get the name of the Command Button I just clicked [duplicate]

I have a bunch of TextBox-Button pairs on a form. When the button is clicked I want to insert the value of the text box into a database. The name TextBoxes and Buttons follow a naming standard, for example Value1Tb - Value1Cmd and Value2Tb - Value2Cmd.
My problem is that since I want to do the same for every button I would like the possibility to write a Sub like:
Private Sub AnyButton_Click(sender As CommandButton)
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
End Sub
But I cannot find a way to point the Click-event of a Button to a different sub than the standard Name_Click().
Anybody know a way around this, that doesn't involve me writing 50 or so different Name_Click() subs?
If you are OK to use Form Controls rather that ActiveX, as it looks as though you may be at the moment, then Chris' solution seems good.
However if you need ActiveX CommandButtons then you are unable (as the VBA compiler will tell you, "Procedure declaration does not match...") to have parameters in the callback for the click event, and you are unable to raise the event from multiple objects, although you do of course know which button raised the event (since the relationship is 1 CommandButton = 1 Sub).
So... I would go with something like:
Private Sub Value1Cmd_Click()
Call TheMethod(Value1Cmd)
End Sub
Private Sub Value2Cmd_Click()
Call TheMethod(Value2Cmd)
End Sub
Private Sub TheRealMethod(sender As CommandButton)
' Do your thing '
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
' Etcetera... '
End Sub
Requires a stub for each button, so some copying and pasting to begin with, but then easy to maintain etcetera as all _Click event callbacks are pointing at the same method...
Edit:
E.g.
Sub AutoWriteTheStubs()
Dim theStubs As String
Dim i As Long
For i = 1 To 10
theStubs = theStubs & "Private Sub Value" & CStr(i) & "Cmd_Click()" & vbCrLf _
& " Call TheMethod(Value" & CStr(i) & "Cmd)" & vbCrLf _
& "End Sub" & vbCrLf & vbCrLf
Next i
Debug.Print theStubs
End Sub
It seems that what you want is to get the name of the clicked button. If you are creating buttons like this:
(where 'i' increments in a loop)
Set btn = Sheet1.Buttons.Add( , , , ,)
With btn
.OnAction = "btnSub"
.Caption = "Upadate"
.Name = "btn" & CStr(i) & "Cmd"
End With
and then defining a generic "private sub btnSub()" for all the buttons, you could at least get the name of the button that was clicked using Application.Caller. Something like:
Private Sub btnSub()
Dim ButtonName As String
ButtonName = Application.Caller
MsgBox ("Hello:" & ButtonName)
End Sub
Hope it helps!
I decided to make this an answer because I am doing something similar and I confirmed that it works.
You can store the OLEobjects in a Collection, of arbitrary size, containing Custom Class Objects that include the OLEobjects and associations and the events that you need. Thus you can completely avoid any code stubs.
Create a Custom Class to bind the Button and TextBox pairs.
Declare the Button object WithEvents.
Include your call-back in the exposed button event handler in the Class Module.
Put a Public routine in a Standard Module to initialise a Collection of these Custom Class objects by spanning the Form Controls. You can also use this to Add the controls programmatically as a 'reBuild' option. The Collection can be inside another Class Module with all of the management routines, but it needs to be Instantiated and loaded in a Standard Module.
Put a public routine in a standard module to receive the call-backs with whatever context you need. This can also be in a Worksheet Module if it makes for better encapsulation. You can use late binding to reference the callback or CallByName.
You need to bear in mind that the Module of the Form will recompile every time you add a control, so you have to be careful where you put your code.
My application has the controls directly on the Worksheet Surface, so I can't put the the Collection Class in, or source any initialisation of the Collection from the Worksheet module. This would amount to self modifying code and it grinds excel to a halt.
I dreamed this idea up through bloody-minded idealism (not necessarily a good thing) but, of course, I was not the first one to think of it as you can see here. #Tim Williams explains it in his answer. You can also google VBA Control Array Events to see plenty of similar examples including an excellent article by #SiddharthRout. In line with the VB6 analogy, he uses an Array instead of a Collection to achieve the same result.
I'll try to post some code later. My application is a bit different so it will take a lot of work to trim it down, but the principle is the same.
The other thing to bear in mind is that VBE really struggles with this type of thing so don't worry if it is loading up you processors. After you re-start with VBE off, all will be fine.
I have this same situation, and I just have a click event for every button that is a wrapper to the function I want to call. This also allows you to pass sheet-specific parameters if you need to.
Example:
Public Sub StoreButton_Click()
' Store values for transaction sheet 3/27/09 ljr
Call StoreTransValues(ActiveSheet)
End Sub
I just published (Open Source) the Event Centralizer for MSForms.
Citation: "The Event Centralizer for MSForms is a VBA programming tool that allows all sorts of custom grouping when writing handlers for the events occurring in UserForms.
With the Event Centralizer for MSForms, it is easy for example to have all TextBoxes react the same way when the Enter event occurs, or all except one, or only those with a particular Tag value.
Thanks to its events logs system, the Event Centralizer for MSForms is a powerful learning and debugging help."
I can't explain here how it works. I tried to do it on the site.
Set the event to =FunctionName(parameter).
A bit late but this may help someone else:
If you have a function called OpenDocumentById(docID as integer), then each control calling the function would have in the event the following:
cmd1's On Click event:
=OpenDocumentById([DocID])
cmd2's On Click event:
=OpenDocumentById([DocID])
etc...

Get text from alert message from web with Selenium and VBA

I am currently creating an automation to fill-in field in the browser (Chrome) with VBA and selenium. Which is currently doing fine. (the site is internal)
However, I came across with this "message from web" prompt after saving / submitting the information. Sometimes, this pop-up box gives me a prompt that it was saved successfully and sometimes, if there are errors, the message box contains some text.
In order for me to handle this pop-up message box and put some validation. In this case, I would like to get the text inside this message box. (Please refer to the red arrow).
The code below is what I used in order to accept / click the "OK" button. And doesn't have any problems. However, as mentioned, I would like to get the text inside that box to put some validation.
What I tried so far:
driver.SwitchToAlert
driver.SwitchToAlert.Text <<<--- this is where I attempt to get the text however, it doesn't work.
driver.SwitchToAlert(20).accept
Also, I have check below code:
driver.switchTo().alert().getText();
However, this seems not to be working in VBA / selenium.
Try such a code (Change to suit your issue)
Dim bot As Selenium.ChromeDriver
Sub Test()
Dim dlg As Selenium.Alert
Set bot = New Selenium.ChromeDriver
With bot
.Get "http://the-internet.herokuapp.com/javascript_alerts"
.FindElementByCss("#content > div > ul > li:nth-child(2) > button").Click
Set dlg = .SwitchToAlert(Raise:=False)
Debug.Print dlg.Text
.Wait 2000
dlg.Dismiss
End With
End Sub

How to show userform, then hide, then show again without running the userform activate code again

I have a progress using a userform. I show the userform when a worksheet button is clicked. The button calls startProgressIndicator()
Sub startProgressIndicator()
UserForm1.Show
End Sub
I have this as my userform code:
Private Sub UserForm_Activate()
Call myCode
End Sub
I then want to hide the progress bar and ask for user input. This occurs in myCode. I inlcude UserForm1.Hide in beginning of myCode.
After getting the user input, I want to show the progress indicator again.
I try UserForm1.Show, however, this just calls myCode all over again. I just want the same userform to be visible again.
I tried using UserForm1.Visible = False, but then I get this error
Function or interface marked as restricted, or the function uses an
Automation type not supported in Visual Basic
Short answer is to rewrite myCode to not include .Hide. Break myCode into logical chunks.
However, you should separate the logic from the display (see Rubberduck UserForm1.Show (*)). By doing so - you would only call .Hide from the form when you want to (e.g. on the 'Close' button click).
#ChrisNeilsen suggested using _Initialize and this will solve the immediate problem but will not set you up for better programming practices in the future. lso, if you decide to modify myCode you may get bugs that are harder to identify.
#ChrisNeilsen: Use 'UserForm_Initialize' rather than 'UserForm_Activate'
(*) No disclaimer required, I am in no way affiliated with Rubberduck, but it does make good reading!

When using "Ctrl+P" or "File - Print" the Workbook.BeforePrint event (Excel) is not called before the print preview is shown

I want to use the BeforePrint event in Excel in order to edit the header and footer of the final printout depending on various parameters. In order for the user to check whether the page setup is right, this code needs to be called before the print preview within Excel is shown. When calling the print preview with the VBA code ThisWorkbook.PrintPreview, the event fires before the preview is shown, but when using "Ctrl+P" or "File - print" the event is not called until the document is sent to the printer, so the print preview does not show the final printout. The code Application.CommandBars.ExecuteMso "PrintPreviewAndPrint" also leads to the same unsatisfying result.
I've noticed, that when using ThisWorkbook.PrintPreview the preview is shown in full screen mode while the other methods show the 'normal' print preview. To test if the event is called, I used the following short piece of code.
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Debug.Print "BeforePrint"
End Sub
I need a foolproof way of showing the correct print preview, so using a button with the VBA code mentioned above is no option. Any suggestions?
Thanks in advance, mk
Even though you have not disclosed which Excel version you are using (it might be vital in this case). I am afraid that getting .BeforePrint event called before the Printing preview when using Ctrl+P might not be possible in your case since "Excel 2013 and later doesn't run Workbook_BeforePrint on Print Preview." (user OzRoz, 2017)
The above answer is taken from a thread which was asking similar question. In the answer, OzRoz also mentions that "you can get it to run for testing by adding the 'Print Preview Full Screen' icon to your QAT (Quick Access Toolbar)"
Perhaps this provides a definitive answer to your problem.

How do I detect that the ESC key is pressed using Lotus Notes?

When a user presses the ESC key, I need to display a prompt.
How can I do this in Lotus Notes client?
Can you elaborate? Is this for one application, one form or the whole Lotus client? Why would you want to disable the esc key?
In the Queryclose event you can get a handle to the close event. Continue = false will prevent the form from closing:
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
msgbox "the message"
End Sub
** The original question changed (by a moderator) from it's original intent, which asked to detect AND ignore ESC key press in Lotus Notes. **
You need to use the "QueryClose" event (as others have mentioned), but how do you identify a "legitimate" way to close the form ? We need some logic to distinguish between someone actually clicking a button to "legitimately" close the form, and if someone hits the escape key or the "X" button in the window bar.
So, you need to use 2 form events, and an action button to do this.
In the QueryOpen event of the form
Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
Dim session As New NotesSession
Call session.SetEnvironmentVar("CloseDoc",0,True)
End Sub
Your QueryClose event needs to look like this
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
Dim session As New NotesSession
Dim iCloseDoc As Integer
iCloseDoc = session.GetEnvironmentValue("CloseDoc", True)
If iCloseDoc <> 1 Then
continue = False
End If
End Sub
And you need to have an action button called "Close", on the form with this in it.
#SetEnvironment("CloseDoc";"1");
#PostedCommand([FileCloseWindow])
The LotusScript alternative looks like this
Sub Click(Source As Button)
Dim ws As New notesUIWorkspace
Dim session As New NotesSession
Call session.SetEnvironmentVar("CloseDoc",1,True)
Call ws.CurrentDocument.Close
End Sub
Now what's going on ? When you open the form, I set an environment variable for "CloseDoc", (QueryOpen event). If the user hits the "ESC" key or even clicks the "X" on the form to close the window the QueryClose event triggers.
When a request to close the form is detected, the QueryClose event then runs. I retrieve the "CloseDoc" environment variable which, in this case is still 0 (zero). If it's not equal to 1 then the close form will be aborted. You can add a messagebox there if you like.
Now the only way for the user to successfully close the form is for them to press the "Close" button. Which first sets the CloseDoc environment variable to 1, then call NotesUIDoc.close(). Although I am not a big fan of environment variables, it is handy in the case as you are able to record user activity without modifying the current document or another document. (That would be even messier, because you would have to clean up temporary documents, or you are forced to make changes to the document which won't work if the user only has reader access to the database or the current document).
This is relatively clean as the control is managed via a discrete envrionment variable that adds no overhead to the database performance and will not get in the way of any other functionality of applications. Some suggestions in the comments advises the use of global variables instead of environment variables and this quite is appropriate if you're using LotusScript exclusively, but if you have a mixture of formula and lotusScript, environment variables are common to both. But it's upto the developer to determine which.
This is one way I have over the years forced the user to click a specific button to close the document, and ignore "ESC" keys or the "X" button int he form window bar without any annoying messages and is free of performance issues.

Resources