VBA anyway to execute _MouseDown before _Enter in TextBox? - excel

My intention is to open "birthdate" userform when textbox "bdayBox" is entered. And also want to be able to open "birthdate" userform again, if it was closed, by clicking with mouse inside the box, but only if textbox "bdayBox" is currently active.
My problem is, that if the textbox in not active and i click it with mouse, _Enter is executed before _MouseDown. This opens "birthdate" userform twice.
Is there a way to execute _MouseDown before _Enter or any other way to do, what i want without double openings?
*EDIT Before the user even gets to the put in birth date, they select how many people should be added. The form 'birthdate' should only show up, if there are more than 1 person to be added and it should appear automatically, when user is entering the 'bdayBox' textbox. If there is only 1 person, no extra form will appear and user enters birth date in the textbox itself. I want to take the decision from the user away to open the 'birthdate' form or not.
Current code
Public bdayBoxAct As Boolean
Public Sub bdayBox_Enter()
birthdate.Show
bdayBoxAct = True
End Sub
Public Sub bdayBox_Exit(ByVal Cancel As MSForms.ReturnBoolean)
bdayBoxAct = False
End Sub
Private Sub bdayBox_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If bdayBoxAct = True Then
birthdate.Show
End If
End Sub
Public Sub UserForm_Initialize()
bdayBoxAct = False
End Sub

I feel this approach is not intuitive for the user, and suffers from the symptoms you are experiencing. My suggestion is to place a button next to the bdayBox textbox, with an appropriate caption telling the user what the button will do, which they can press whenever they need to form to appear. The button replaces the MouseDown logic. Your code then becomes:
Option Explicit
Private Sub bdayBox_Enter()
birthdate.Show
End Sub
Private Sub ShowBirthdate_Click()
birthdate.Show
End Sub

Related

How to prevent a Boolean value from being reset?

360 separate sheets make up this workbook.
The first sheet is the "Main Page," which is opened when the workbook is first opened, and has a userform that pops up containing the instructions for the main page.
For about 350 of the other sheets, there is a copy & pasted userform which contains instructions for those sheets, which all operate in the same manner.
Within those 350 userforms, there is a toggle button that flags a public variable as true, which prevents them from popping up again when the sheet is opened.
I want that toggle button to prevent those userforms from ever popping up again as long as the button is depressed on one of those pages.
If sheetOpenned = False Then
SubPageInstructions.Show
End If
sheetOpenned = True
End Sub
sheetOpenned is the public boolean declared at the top of the page. Is there a way to make this flag as true for every page?
The public Boolean is reset when the workbook is closed.
How can I
prevent the public Boolean from being reset?
prevent the toggle button in my Userforms from being auto toggled to their default value on workbook close?
Just for the record, I leave here the solution using the Windows Registry, registering the value of ToggleButton1 in it. In this example, I used a CommandButton, where the code evaluates whether there is a negation to display the Userform, written to the Registry. In the Userform itself, the code for registering the value of ToggleButton1 is saved, once clicked. The "VB & VBA Program Settings" subkey exists for each logged in user. So, for a new logged in user, the Userform will be displayed and, if the ToggleButton1 is clicked, it will be disabled for future reopenings of the Workbook.
On the Userform code pane:
Private Sub ToggleButton1_Click()
If Me.ToggleButton1.Value = True Then
'don't show Me again.
Me.ToggleButton1.Value = False
fToggleButton1_State = False
SaveSetting "My350Sheets", "SavedState", "ToggleButton1", False
Else
'uncomment the line below if you want the Userform to be shown again in the *NEXT* WB_open event
'SaveSetting "My350Sheets", "SavedState", "ToggleButton1", True
End If
End Sub
On a standard Module code pane:
Option Explicit
Public fToggleButton1_State As Boolean
On a CommandButton code:
Option Explicit
Private Sub CommandButton1_Click()
If fToggleButton1_State = True Then
UserForm1.Show
End If
End Sub

Excel Userform Run-Time Error when trying to launch a 2nd time after initial 'Cancel' or close on red 'X'

Problem: I am building a Userform that has a 'Submit' and 'Cancel' button. I want the entire form to clear any entered data and close the form if the user hits the 'Cancel' button but I also am trying to build in the same functionality if the user hits the red 'X' in the top right corner. I'm unclear where I need to unload the form. I currently have it placed within the btnCancel_Click() method and I'm able to launch the form, enter some data and hit Cancel and it will close the form down.
But when I try to re-launch the form a 2nd time I get an error (I attached a picture of that message) that says
"Run-Time error '-2177418105 (80010007): Automation Error - The Callee (server [not server application]) is not available and disappeared; all connections are invalid. The call may have executed.
If I remove Unload Me from btnCancel_Click() then the form can close and re-open just fine, but any data I entered the first time will still be on the form and isn't cleared properly. I'm wondering if this is an Unload Me error or do I need to reset all form controls when I initialize the form?
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' how was the form closed?
' vbFormControlMenu = X in corner of title bar
If CloseMode = vbFormControlMenu Then
' cancel normal X button behavior
Cancel = True
' run code for click of Cancel button
btnCancel_Click
End If
End Sub
'******************************************************************
Private Sub btnCancel_Click()
mbCancel = True
Me.Hide
Unload Me
End Sub
'*********************************************************************
Private Sub UserForm_Initialize()
'Populate values for 2 combo boxes
lastEmp = Sheets("Form_Ref").Cells(Rows.Count, 1).End(xlUp).Row
Me.cmbBoxEmpName.List = Sheets("Form_Ref").Range("A2:A" & lastEmp).Value
lastBld = Sheets("Form_Ref").Cells(Rows.Count, 2).End(xlUp).Row
Me.cmbBoxBuildingName.List = Sheets("Form_Ref").Range("B2:B" & lastBld).Value
End Sub
'******************************************************************
Public form As New CheckOutForm
Sub testFormOptions()
'Button pressed within Excel will start program and show the userform
form.Show
End Sub
This is the easiest quick and dirty solution:
Delete Public form As New CheckOutForm from the code. Then add it in the testFormOptions():
Sub testFormOptions()
Dim form As New CheckOutForm
form.Show
End Sub
Some not-that-good VBA books/tutorials would even go a bit like this, but this is brutal:
Sub testFormOptions()
CheckOutForm.Show
End Sub
Anyway, now the problem with the predefined values in the form is solved.
For the clean and not-so-easy solution, consider writing a MVC framework around the form:
https://codereview.stackexchange.com/questions/154401/handling-dialog-closure-in-a-vba-user-form
this blogpost (disclaimer - mine!), which pretty much says what the above link proposes, but it does not have the errors from the question.
the old StackOverflow tutorial for UserForms
If you execute Unload, you destroy the form object. With other words, your (global) variable form gets invalid and if you issue a new form.show, you get the runtime error.
When, on the other hand, you just unhide the form, the form-object stays valid (it's just currently not visible) and all controls keep their value.
Either you do some housekeeping by resetting all controls when a form is displayed (use the UserForm_Activate-event), or you have to create a new form-object every time you want to display it (I would strongly advice not to use the name form as variable name to avoid confusion).
Sub testFormOptions()
dim myForm as CheckOutForm
myForm.Show
End Sub

Option to Enable/Disable Right Click

I want to limit people viewing hidden parts of my excel. To still allow my VBA to hide/unhide columns/sheets as the user clicks through the file I've opted not to protect the workbook structure. The below code works, but I want to put a button in to a hidden sheet to disable this macro if a user knows that it is there (i.e. I want some users to have full control, without having to go change the VBA) - any ideas?
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Cancel = True
MsgBox ("Sorry Right Click is Disbaled for this Workbook")
End Sub
Thanks,
James
you could place an ActiveX checkbox in the worksheet and use this in your sheet's module:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
If CheckBox1 Then
Cancel = True
MsgBox ("Sorry Right Click is Disbaled for this Workbook")
End If
End Sub

How to select the contents of a textbox once it is activated?

I have this simple Userform, where I only have TextBox1 and TextBox2. I enter some text in both of them. Assume the focus is on (the cursor is in) the TextBox2. When I click on TextBox1, I want the whole text in this control to be highlighted (selected). Thus I use this code:
Private Sub TextBox1_Enter()
With TextBox1
.SetFocus
.SelStart = 0
.SelLength = Len(.Text)
End With
MsgBox "enter event was fired"
End Sub
There is a MsgBox at the end which is loaded, that means the event works. However, the text is not highlighted. How to fix this?
I use the Enter event and don't want to use the MouseDown event, because I need the code to also work when the TextBox1 is activated programatically, so I feel the Enter event to be the best choice, as it's fired in both cases! Another drawback of the MouseDown event is: when I click for the second time on the TextBox1, I would not expect the whole text to be highlighted anymore, because the focus was set on the first click and it was not changed after I clicked on the same control for the second time; so in this case I would like the cursor to act normally (not to keep the text marked).
Update
When I click once on the TextBox1, I expect to have this result:
If clicked again, the highlight would be removed and the cursor would be placed in the place where it was clicked.
Can't be more simple than this I guess...
Private Sub TextBox1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
With TextBox1
.SelStart = 0
.SelLength = Len(.Text)
End With
End Sub
Whether you click on the textbox or you tab into it, it will do what you want. To deselect the text, use the arrow keys.
Explanation
If you debug the code you will see that even though you have said .SetFocus, the focus is not on the Textbox. .SetFocus doesn't work in TextBox1_Enter() and you need to have focus for the rest of the code to work. And hence my alternative...
Alternative
You may also like this version :) This overcomes the limitation of using the mouse in the TextBox
Dim boolEnter As Boolean
Private Sub TextBox1_Enter()
boolEnter = True
End Sub
Private Sub TextBox1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
If boolEnter = True Then
With TextBox1
.SelStart = 0
.SelLength = Len(.Text)
End With
boolEnter = False
End If
End Sub
Pff, took me a while. Actually, your code works, but it highlights the text BEFORE the click event happens. So you clicking in the box instantly overrides the selection created by the code.
I have used a delayed selection, and it works, though it is a bit disgusting...
The code for the textboxes:
Private Sub TextBox1_Enter()
Application.OnTime Now + TimeValue("00:00:01"), "module1.SelectText1"
End Sub
Private Sub TextBox2_Enter()
Application.OnTime Now, "module1.SelectText2"
End Sub
Note that it works even withouth the {+ TimeValue("00:00:01")} part, but it might theoretically stop it from working at times. Hmm, on a second thought, just leave it out. I doubt it would ever cause a problem.
Now the code in module1:
Sub SelectText1()
UserForm1.TextBox1.SelStart = 0
UserForm1.TextBox1.SelLength = Len(UserForm1.TextBox1.Text)
End Sub
Sub SelectText2()
UserForm1.TextBox2.SelStart = 0
UserForm1.TextBox2.SelLength = Len(UserForm1.TextBox2.Text)
End Sub
Hope this works for you too. Ineresting problem. :) Cheers!
I couldn't manage to select/highlight text in the Enter event as the the mousedown and mouseup events coming after are somewhat resetting the selection.
I think the most proper way of achieving what you want is this :
' if you want to allow highlight more then once, reset the variable LastEntered prior to call SelectTboxText:
' LastEntered = ""
' SelectTboxText TextBox2
Dim LastEntered As String
' Button to select Textbox1
Private Sub CommandButton1_Click()
SelectTboxText TextBox1
End Sub
' Button to select Textbox2
Private Sub CommandButton2_Click()
SelectTboxText TextBox2
End Sub
Private Sub TextBox1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
SelectTboxText TextBox1
End Sub
Private Sub TextBox2_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
SelectTboxText TextBox2
End Sub
Public Sub SelectTboxText(ByRef tBox As MSForms.TextBox)
If LastEntered <> tBox.Name Then
LastEntered = tBox.Name
With tBox
.SetFocus
.SelStart = 0
.SelLength = Len(.Text)
End With
End If
End Sub
So each time you want to activate one of the textbox programmatically, you should call the sub SelectTboxText, which is not really annoying IMO. I made 2 buttons for this as an example.
This is somewhat an enhancement of what #vacip posted. The benefit you get is that you don't need to add a separate method in the Module for each new textbox.
The following code in your User Form:
'===== User Form Code ========
Option Explicit
Private Sub TextBox1_Enter()
OnTextBoxEnter
End Sub
Private Sub TextBox2_Enter()
OnTextBoxEnter
End Sub
Private Sub TextBox3_Enter()
OnTextBoxEnter
End Sub
The following code goes in a Module:
'===== Module Code ========
Sub SelectAllText()
SendKeys "{HOME}+{END}", True
End Sub
Sub OnTextBoxEnter()
Application.OnTime Now + 0.00001, "SelectAllText", Now + 0.00002
End Sub
Private Sub UserForm_Initialize()
TextBox1.SetFocus
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
End Sub
Add this to the form's code
I know this is well out of date but I'm leaving this here in case it helps someone in my position.
What I want is:
If I click on the box for the first time: select all the text
If I click on it a subsequent time: put the cursor where the mouse is and allow me to use the mouse to select a substring
Firstly it is important to know that "Select all the text" is the default behaviour when tabbing into a TextBox and that "Put the cursor here" is the default behaviour when clicking on a TextBox so we only need to worry about what the mouse is doing.
To do this, we can keep track of the Active Control, but only while the mouse is moving over our TextBox (ie. before the Click)
Code:
Private m_ActiveControlName As String
Private Sub Text1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
m_ActiveControlName = Me.ActiveControl.Name
End Sub
Private Sub Text1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If m_ActiveControlName <> Me.Text1.Name Then
Call Text1_Enter 'we don't have to use Text1_Enter for this, any method will do
Exit Sub 'quit here so that VBA doesn't finish doing its default Click behaviour
End If
End Sub
Private Sub Text1_Enter()
With Text1
.SelStart = 0
.SelLength = Len(.Text)
End With
End Sub
There's another solution than the one given by Siddharth.
EDIT: but there's this bug of SendKeys, so the solution I propose below is a lot worse than Siddharth one. I keep it in case one day the bug is corrected...
It relies on the property EnterFieldBehavior of the TextBox field. This property works only when the Tab key is pressed to enter that field, and if this property is fmEnterFieldBehaviorSelectAll (0) the whole field text is automatically selected.
So a dummy caret movement between fields when the form is shown, will activate the feature automatically. For instance this movement can be achieved by pressing Tab to move to the next field, and pressing Shift+Tab to move to the previous field (so back to the original field):
Private Sub UserForm_Activate()
SendKeys "{TAB}+{TAB}"
End Sub
The (very little) advantage of this solution is that you can tune your user form by editing manually the properties EnterFieldBehavior, TabIndex, TabKeyBehavior and TabStop without changing the VBA code anymore to set "select all" on the field with the initial focus.
In short, the VBA code above tells to consider the property EnterFieldBehavior of the field which has the initial focus when the user form is displayed (provided that it's a TextBox or ComboBox field of course).
use this
Private Sub TextBox1_Enter()
With TextBox2
.ForeColor = vbBlack
.Font.Bold = False
End With
With TextBox1
.ForeColor = vbRed
.Font.Bold = True
End With
End Sub
Private Sub TextBox2_Enter()
With TextBox1
.ForeColor = vbBlack
.Font.Bold = False
End With
With TextBox2
.ForeColor = vbRed
.Font.Bold = True
End With
End Sub
The behavior you're trying to implement is already built in to the TextBox. When you move the mouse over the left side of the text box, the mouse pointer will point to the right. If you click, it will select all the text in the field. Clicking anywhere else will deselect the text.
I will try a few other strategies to see if I can get this to work in one Sub.
Try the same code with TextBox1_MouseDown. It should work.
Private Sub TextBox1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
With TextBox1
.SetFocus
.SelStart = 0
.SelLength = Len(.Text)
End With
MsgBox "Text in TextBox1 is selected"
End Sub

How to create Back button in Excel to move user in the position he came from?

Let me know please if the scenario feasible: user clicks on Sheet1!A1 cell with "Go Details" button and it moves him to Sheet2!B1, where he may click "Back" button in the same row to be moved back to Sheet1!A row he came from. Same way he can click Sheet1!J1 cell with "Go Details" button which moves him to the same Sheet2!B1 row, but this time "Back" button leads him back to Sheet1!J row, so that it remembers the position user came from.
Insert this code in the ThisWorkbook routine:
Private rngLastLink As Range
Private Sub Workbook_SheetFollowHyperlink(ByVal Sh As Object, ByVal Target As Hyperlink)
If UCase(Target.Parent.Value) = "BACK" Then
If rngLastLink Is Nothing Then
Application.EnableEvents = False
Target.Follow
Application.EnableEvents = True
Else
rngLastLink.Worksheet.Activate
rngLastLink.Activate
End If
Else
Set rngLastLink = Target.Parent
End If
End Sub
It will store the cell from any pressed hyperlink that is not called "Back". If "Back" is pressed, it will reactivate this cell.
If the user, selects Excel Options > Quick Acccess Toolbar > All Commands. Add "Back" and "Foward" they will have back and forward navigation after jumping links, just like on a web page.
First you will need to create a new button, then the assign macro screen will pop up once the button is successfully created.
Copy this code
Sub Button1_Click()
Worksheets("sheet2").Activate
End Sub
as macro
change sheet2 as appropriate for your scenario.
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Sheets.Select
ActiveCell.EntireRow.Select
ActiveSheet.Select
will synchronize entire row between worksheets - will only fire when you change worksheets...
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target
As Range)
Sheets.Select
ActiveCell.EntireRow.Select
ActiveSheet.Select
End Sub
will fire every time user change cells in active sheet.
I used Alt-Left arrow to go back.
Sub GoBackToWhereverYouCameFrom()
Application.SendKeys ("%{LEFT}")
End Sub

Resources