When showing a userform (running its Show method) it not only shows up on the screen but also takes the focus (the destination of e.g. keystrokes).
Say, the userform is a custom made toolbar. Its Show fires in Workbook_Open() but the form itself is used relatively rarely so we want the focus to go back to the main application window right after its appearance.
Unfortunately, it seems SetFocus method is not valid for application objects.
So how is this done?
I suppose the solution for my example comes after
Private Sub Workbook_Open()
[...]
UserForm1.Show
i use this one :
AppActivate Application.caption
this resets the focus from a userform to your Excel Sheet.
For me
AppActivate ThisWorkbook.Application
right after the Show statement seems to work fine.
In other cases
AppActivate "Microsoft Excel"
may also be ok.
This is a bit tricky, but this is what can do.
In the subroutine “Private Sub UserForm_Initialize()”, add this as the last line:
Private Sub UserForm_Initialize()
. . . . . . . . . .
Application.OnTime Now(), "MoveFocusToWorksheet"
End Sub
In any of the general code modules (add one if you have none), declare an API function:
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
In any of the general code modules (could be the one with the API declaration, of course), add this subroutine:
Public Sub MoveFocusToWorksheet()
Dim Dummy As Long
ThisWorkbook.Worksheets("Sheet1").Activate
' "Sheet1" here is the tab name of the sheet you want to move focus to. _
Or simply use then: With shtABC.Activate _
where "shtABC" being the worksheet's CodeName, _
same as ThisWorkbook.Worksheets("Sheet1").CodeName, _
same as the sheets module name showing in the Project Explorer panel.
Dummy = SetForegroundWindow(Application.hwnd)
End Sub
Both AppActivate Application.Caption and (the better) AppActivate ActiveWindow.Caption mentioned in the other answers do their job in focusing back on the application window itself ... but they do NOT focus on the actual cell/range where one typically wants the focus to be. For that, use:
ActiveCell.Activate
which has the benefit of not requiring an additional click on the cell area of the sheet where you want to return focus - an additional click that can potentially change the previous selection.
I create an object for the application e.g. Outlook, then change the WindowSate to Maximised (OlMaximized), then when I want to remove focus I minimise (olMinimized)
Set OutlookObj = GetObject(, "Outlook.Application")
OutlookObj.ActiveExplorer.WindowState = olMinimized
OutlookObj.ActiveExplorer.WindowState = olMaximized
Or you change the state from inside the application, you can also change its location and size etc for more info see: https://msdn.microsoft.com/en-us/library/office/ff838577.aspx
Application.WindowState = xlMaximized
An other form is:
AppActivate ThisWorkbook.Name
I use
AppActivate ActiveWindow.Caption
because
AppActivate Application.Caption
can focus the wrong window if multiple windows are opened for the same workbook.
As a footnote to this excellent discussion, in some circumstances you may avoid the focus problem by skipping the call to .Show, so the focus never moves in the first place. For example with Word, if you are updating a modeless form or dialog box, just update the required area and omit the call to .Show, e.g.:
Sub ShowProblems(ByVal ProbLoc)
EditBox2.TextBox.Text = "Here is the problem location: " & ProbLoc
' not needed: EditBox2.Show vbModeless
End Sub
Private Sub UserForm_Activate()
RefRangeIn.SetFocus
End Sub
it work for me, in excel 2013 VBA
Add a dummy form and add codes as below:
Private Sub SomeButton_Click()
frm_Dummy.Show vbModeless
Unload frm_Dummy
End Sub
OR
Sub SomeSub()
frm_Some.Show vbModeless
frm_Dummy.Show vbModeless
Unload frm_Dummy
End Sub
I created a floating menu with a user form and use this code to make my cursor leave the user form and jump/focus back to my worksheet. It works at the end of each command button code and with the initiation code of the user form as well.
AppActivate ThisWorkbook.Application
Just place the above line of code before the "End Sub" line of any command button code and the initial show userform code.
Related
I must be missing something basic (no pun intended), but I can't figure this out.
I have a MS Word VBA form called frmChooseMacros. I want to execute a subroutine on an Open or Load event for that form before the user interacts with the form.
From this topic https://learn.microsoft.com/en-us/office/vba/api/access.form.open, I came up with this code as a test and added it to the code window for frmChooseMacros:
Private Sub Form_Open(Cancel As Integer)
MsgBox "Running open event"
End Sub
But it never executes when the form loads.
From this topic, https://learn.microsoft.com/en-us/office/vba/api/access.form.load, I also tried the Load event, like this, but still no joy:
Private Sub Form_Load()
MsgBox "Running open event"
End Sub
I'm running the form through this sub-routine...
Sub DocFix_00_RunMultipleMacros()
frmChooseMacros.Show
End Sub
...which executes when a button tied is clicked on the menu.
This image may help provide additional context:
Any help is appreciated. Thank you!
(Credit goes to #braX for helping me find the answer here.)
For Word, you have to use the UserForm_Initialize or UserForm_Activate events. There is no Form_Load or Form_Open events for Word.
I was able to solve this by first defining a UserForm object and setting it to my frmChooseMacros user form:
Sub DocFix_00_RunMultipleMacros()
Dim oForm As UserForm
Set oForm = New frmChooseMacros
frmChooseMacros.Show
End Sub
Then I use the UserForm_Activate event here:
Private Sub UserForm_Activate()
MsgBox "Form has activated."
End Sub
Is it possible to have multipe buttons, lets say 'Button 1' and 'Button 2' run the same VBA code but yield a different result based on the button that was pressed?
For instance when I press button 1 I want it to go to a website, load data and put it on to Sheet 1. But when I press button 2, it goes to the same site and loads it to Sheet 2.
I know I can have multiple instances of the same VBA code (with different names) however I am hoping to simplify the code and prevent it from being overly complicated.
If you are using a Forms button you can assign the same macro and use Application.Caller to return the name/id of the calling button.
Sub Test()
MsgBox Application.Caller & " was pressed"
End Sub
Create one sub to do the work and pass the sheetname as an argument to that sub. I did it with a string variable, but you can do it with a worksheet variable as well.
Sub Button1_Click()
LoadWebsiteToSheet "Sheet1"
End Sub
Sub Button2_Click()
LoadWebsiteToSheet "Sheet2"
End Sub
Sub LoadWebsiteToSheet(sName as String)
'... code to load website to Worksheets(sName)
End Sub
I was leaning more towards brettdj's solution as well
If you assign the same macro to many buttons, you will get different results based on the button name.
You could name the button to the sheet you wanted. Practice with this. Add several buttons and assign this code to them. Click each button to see what happens.
Sub GetButtonName()
Dim Btn As String
Btn = ActiveSheet.Shapes(Application.Caller).Name
MsgBox Btn
End Sub
Does anyone know if it's possible to have a textbox in a userform in Excel 2010 work like the formula editor?
In other words when the userform is up and the textbox is the focused control allow me to type say
=AND(
Then click cell D2 and have the text box then be
=AND($D$2
Then type a =
=AND($D$2=
Then click cell E2
=AND($D$2=$E$2
Then type )
=AND($D$2=$E$2)
I've played around with the RefEdit control but it just overwrites any custom text as soon as a range is selected on the sheet, I basically need it to append to the text box when I click on a range.
Put this code in the ThisWorkbook module:
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
On Error GoTo EndOfSub
If UserForm1.ActiveControl.Name = "TextBox1" Then
UserForm1.TextBox1.Value = UserForm1.TextBox1.Value & Target.Address
End If
EndOfSub:
End Sub
This way when your userform is loaded (UserForm1 here) and your textbox is active (TextBox1 here), the address of the selection is appended to it. If you need to add the worksheet's name too, change the 3rd line above:
UserForm1.TextBox1.Value = UserForm1.TextBox1.Value & sh.Name & "!" & Target.Address
If you need to use it on only one sheet, put the code to that Sheet's code instead of the ThisWorkbook's code.
It is not as sophisticated as the formula editor, as it always appends the selection. (Changing the just inserted reference is doable too if you need it, just takes a bit more checking.)
Do note that putting code into the ThisWorkbook's SheetChange method takes its toll: you will not be able to use Undo in this workbook.
A bit more elegant way to check if a Userform is loaded can be found (here)[http://www.ozgrid.com/forum/showthread.php?t=152892]. You can use this instead of the On Error Goto part.
Yes, this is possible in a TextBox, but the problem you have is that you'll need to show the UserForm modelessly because you need to select cells in a worksheet while the Userform is on show. If you want to do exactly as you describe, ie the very next keystroke is '=', then you will need to re-activate the UserForm otherwise you'll simply add an '=' into your selected cell.
I believe the Show command won't re-activate a modeless UserForm that is already on show and I'm not aware of an Activate command for a UserForm. Others may well know of one, but I'd use a couple of APIs to do the job. So the code in your Userform could be as follows (you may need to adjust the API declarations if you have Win64 and/or VB7 https://msdn.microsoft.com/en-us/library/office/ee691831(v=office.14).aspx):
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetFocusAPI Lib "user32" Alias "SetForegroundWindow" _
(ByVal hwnd As Long) As Long
Private mHwnd As Long
Private mTextBoxHasFocus As Boolean
Public Property Get TextBoxHasFocus()
TextBoxHasFocus = mTextBoxHasFocus
End Property
Public Sub AppendText(appendedText As String)
'Add the text to textbox
TextBox1.Text = TextBox1.Text & appendedText
'Activate this userform window
SetFocusAPI mHwnd
'API doesn't trigger UserForm_Activate event so call it
Me.ActivateByCode
End Sub
Private Sub TextBox1_Enter()
mTextBoxHasFocus = True
End Sub
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
mTextBoxHasFocus = False
End Sub
Public Sub ActivateByCode()
'Set focus on any another control then back to textbox
btnSave.SetFocus
'Set cursor at end of text
TextBox1.SelStart = Len(TextBox1.Text)
TextBox1.SelLength = 0
TextBox1.SetFocus
End Sub
Private Sub UserForm_Initialize()
'Acquire the handle for this window
mHwnd = FindWindow("ThunderDFrame", Me.Caption)
End Sub
You'd then call the AppendText routine from a WorkSheet_Change event or, as in this case, a Workbook_SheetSelectionChange event (which caters for selections of a cell in any WorkSheet):
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
If UserForm1.TextBoxHasFocus Then
UserForm1.AppendText Target.Address
'Uncomment the line below if you want sheet name as well as address
'UserForm1.AppendText Sh.Name & "!" & Target.Address
End If
End Sub
And remember to show your UserForm modelessly:
UserForm1.Show False
If you want to cursor to be in the TextBox when the UserForm opens, then add this line too:
UserForm1.ActivateByCode
(Posted on behalf of the question author).
Checking for the selection changed was what I was missing. I was SO focused on trying to make the UserForm behave I completely spaced on ALL the other events Excel has to work with!
In the end I went with a check in the SelectionChanged sub to see if a simpler UserForm (minimal size with textbox and restore button) was visible and the textbox was active.
I added a little more logic to handle inserting into the cursor location of the textbox as well as straight appending. Might add some support for arrow keys in the future but I have the original functionality I set out to have.
Microsoft, if you're listening, would it have been possible to give us a RefEdit control that looks and behaves like the one built into Excel? Just a thought.
I'm using a VBA Userform. The MassPrompt userform has a set of six GroupNames and some text boxes. Each GroupName contains two or more radio buttons.
I'd like the following code to be triggered anytime any element within the GroupName "GROnly" changes. If the user made an inappropriate button choice in "GROnly" based on the choice in another group, I'd like to display a warning message and present the MassPrompt userform again.
Right now, I've assigned the code to the one button "GROnly_yes". It works, but only when that one button is clicked. How do I position this within the UserForm code to trigger anytime a button with the GroupName "GROnly" is clicked? Thanks for looking at this.
Private Sub GROnly_yes_Click()
'Prompt if the GROnly is true and it's inappropriate for the GSetting choice
If GROnly_yes = True And GSetting_renewal = True _
Then
GROnly_yes = False
GROnly_no = True
MsgBox ("The GROnly can't be chosen with a Renewal." & vbNewLine & _
"The GROnly button has been changed to 'NO'.")
UserForm_Initialize
End If
'Other IF statements here.
End Sub
If I understood well, GRonly is the GroupBox that contains (let's say) the radio_button_1 and radio_button_2.
The reason why the code doesn't trigger is that when he/she changes the value of one radio-button is not clicking on the GroupBox, but rather changing the value of that single radio-button.
You will have to add the code to the _Change event of the radio button objects. This is an example:
Sub myFunctionalCode()
'your code here
End Sub
Private Sub radio_button_1_Change()
myFunctionalCode
End Sub
Private Sub radio_button_2_Change()
myFunctionalCode
End Sub
Thanks for the reply. That would work.
In the meantime, I came up with another alternative as well. I removed the code from:
Private Sub GROnly_yes_Click()
And I moved it to the button that the user clicks to submit the form:
Private Sub ContinueButton_Click()
In my original code I had to change UserForm_Initialize to Exit Sub to make the code work in the "submit" button.
Thanks again for your help.
Phil
I have been searching the internet for this all day. But no luck.
Can you setup events for when you enter/leave a tab.
I.E.
OnExit(Tab1)
Do something
Thanks
Depending on your program flow, you might try:
Private Sub Combo3_Exit(Cancel As Integer)
If IsNull(Me.Combo3) Then
MsgBox "No exit"
Cancel = True
End If
End Sub
Private Sub Form_Current()
Me.Combo3.SetFocus
''Or to refer to a subform from the main form
Me.subformcontrolname.Form.Combo3.SetFocus
End Sub
Does the tab contain a subform or only controls from the main form?
A subform has an Exit event, so if you are only concerned that once you have entered the subform you should not leave without completing data, you could:
Private Sub subformcontrolname_Exit(Cancel As Integer)
If IsNull(Me.subformcontrolname.Form.Combo3) Then
Me.subformcontrolname.Form.Combo3.SetFocus
MsgBox "No exit"
Cancel = True
End If
End Sub
While this is not exactly what you want, you could instead of handling the exit simply prevent the user from clicking somewhere else. To do this, attach some code to your combobox1 that bascially set .Enabled=Xfor all elements outside your tab view - where X is determined by the state of the combobox...