Excel form - go to last selected value - excel

I'm trying to get a listbox from an excel form to set focus on the last selected value.
Because the listbox is multiselect, if I use the code below, it will deselect things that i want to remain selected:
With Me.listName
For lsti=0 To .listCount-1
If .selected(lsti) Then
lastSelectedIndex = lsti
End If
Next
If lastSelectedIndex >-1 Then
.listindex = lastSelectedIndex 'this causes other items to become deselected
End If
End With
Is there a way i can set focus to the last selected item, without items being deselected?

In what "event" you put this code? If it is in _click or _change?
This line of code will re-trigger the event
.listindex = lastSelectedIndex
Use this technique:
Declare a module level variable
Option Explicit
Public mbCancel As Boolean
Then use this code
Private Sub myListName_Change()
If mbCancel Then Exit Sub
With myListName
'
If lastSelectedIndex >-1 Then
mbCancel=true
.listindex = lastSelectedIndex
mbCancel=false
End If

Thanks M.R for your idea, but it made me realise i was not calling the function correctly.
I have a second list box that i use as a filer with unique values, and i should change the focus only when using the filter. I was also changing the focus on MouseUp on the main listbox, whitch now with a clear head, makes no sense.
So i used the following code separatelly, to change the focus:
Private Sub lstFilter_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'filterMainList
'addIdsToSelected
setFocusToLast
End Sub
Sub setFocusToLast()
Dim indexI, lastSelectedId, currentId
Dim lstI
With Me.lstFilter
indexI = .listIndex
lastSelectedId = .LIST(indexI, 0)
End With
With Me.lstIds
For lstI = .listCount - 1 To 0 Step -1
currentId = .LIST(lstI, 0)
If currentId = lastSelectedId Then
.listIndex = lstI
Exit For
End If
Next
End With
End Sub

Related

Multiple Checkboxes Conditions in VBA - No Two or more Checkboxes to be selected

I need the help with the selection of multiple checkboxes by user. I have a userform in vba that has 8 checkboxes. I want user to select only ONE checkbox at a time. What is the shortest coding possible?
Please, try the next way:
Create a Sub able to untick all the check boxes, except the active one:
Public Sub UnTickCheckBoxes() 'Public to be also called from outside if use a class for event allocation
Dim i As Long
For i = 0 To Me.Controls.count - 1
If TypeName(Me.Controls(i)) = "CheckBox" And Not ActiveControl.Name = Me.Controls(i).Name Then
Application.EnableEvents = False
Me.Controls(i).value = False
Application.EnableEvents = True
End If
Next i
End Sub
Call the above sub from the Click event of all check boxes. You can use Change event for doing something else. The call can be done in this way:
Private Sub CheckBox1_Click()
If ActiveControl.value = True Then
UnTickCheckBoxes
End If
End Sub
2 bis. Automatic event allocation to all check boxes:
2.1 Create a class named "chkBoxesClickEvent". Copy and paste in it the next code:
Option Explicit
Public WithEvents chkBEvent As MSForms.CheckBox
Private Sub chkBEvent_Click()
If chkBEvent.value = True Then
chkBEvent.Parent.UnTickCheckBoxes
End If
End Sub
2.2 Declare on top of the form (in the declarations area) the next variable:
Private chkB() As New chkBoxesClickEvent
2.3 Place the next code in the UserForm_Initialize event:
Private Sub UserForm_Initialize()
Dim C As MSForms.Control, k As Long
ReDim chkB(Me.Controls.count): k = 1
For Each C In Me.Controls
If TypeOf C Is MSForms.CheckBox Then
Set chkB(k).chkBEvent = C: k = k + 1
End If
Next
ReDim Preserve chkB(k - 1)
End Sub
Do not forget to clear all check boxes Click event, if any code inside...
Show the form a see how it works, then send some feedback.

Listbox_Click run command before selecting new entry

Is it possible to run a command inbetween the moment a user clicks on a listbox item (in a userform) and the item being selected?
I have a mask with a listbox as index and want to make it possible when a user goes to another item, the entries are automatically saved. At the moment I am doing it with a button but I want to prevent the situation that a user goes to a different item and loses all the information put in because he forgot to press the button.
my code if you click on the listbox:
Private Sub ListBox1_Click()
Dim lrow As Long
Values_delete 'Sub that clears all text boxes
If ListBox1.ListIndex >= 0 Then
lrow = 2
Do While Trim(CStr(Tabelle10.Cells(lrow, 1).Value)) <> ""
If ListBox1.Text = Trim(CStr(Tabelle10.Cells(lrow, 1).Value)) Then
Values_read (lrow) 'values get read from the excel cells and written into the text boxes
Exit Do
End If
lrow = lrow + 1
Loop
End If
End Sub
As well as my code for the save button:
Public Sub SaveButton_Click()
Dim lrow As Long
If ListBox1.ListIndex = -1 Then Exit Sub
If Trim(CStr(Abteilung.Text)) = "" Then
MsgBox "Error", vbCritical + vbOKOnly, "Error"
Exit Sub
End If
lrow = 2
Do While Trim(CStr(Tabelle10.Cells(lrow, 1).Value)) <> ""
If ListBox1.Text = Trim(CStr(Tabelle10.Cells(lrow, 1).Value)) Then
Values_write (lrow) 'Values get written into the cells
If ListBox1.Text <> Trim(CStr(Abteilung.Text)) Then
Call UserForm_Initialize
If ListBox1.ListCount > 0 Then ListBox1.ListIndex = 0
End If
Exit Do
End If
lrow = lrow + 1
Loop
End Sub
Is this even possible?
Try MouseDown event that fires before updating anything. When you click on a new item,
.Selected or .Listindex points to the previous item within Mousedown. Then you can use MouseMove event to process the lately selected item.
I'm assuming you are using either an ActiveX control on your sheet OR a UserForm control so;
You can use the MouseDown event; below will print the text of the previously selected item (based on the user clicking a new item)
Private Sub ListBox1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Debug.Print ListBox1.Text
End Sub
MS Documentation breifly mentions this event: See here

VBA: ListBox Change event firing twice

I have a User Form in Excel in which questions are indexed in a Listbox control. Clicking on an item in the Listbox calls a Change event which populates other controls' values according to which item has been selected.
The user may change values within the text boxes. Upon changing them, a "Saved" flag gets set to False for that question. The user may then save the question into memory; or navigate away from the question.
If the user navigates away without saving (by means of clicking a different item in the Listbox), I want to present them with a warning - giving the option to either abandon their unsaved changes; or to remain with the current selection, and revert the Listbox selection which they just clicked.
If "Abandon changes" is selected, it works fine. However it runs into trouble when I try to revert the Listbox selection. I use an "EventsOn" Boolean to handle when the Change procedure should proceed, to avoid it calling itself. This seems to work, at the correct point in the code. However after EventsOn is reinstated, and after Exit Sub, it seems that the Change event is called again.
I do not know why the event is firing again. This results in the user being presented with the option a second time.
A lot of the following code has been stripped out because it relates to details of other form controls; loading/saving data from a database; and handling classes and dictionaries. However I have retained the relevant logic of the form controls:
Option Explicit
Dim NumberOfQuestions As Long
Dim EventsOn As Boolean
Dim SelectedListIndex As Long, CurrentQuestion As Long, QuestionSaved As Variant
Private Sub UserForm_Initialize()
' Stripped out lots of code here. Basically opens a recordset and loads values
ReDim QuestionSaved(1 To NumberOfQuestions) As Boolean
'
For X = 1 To NumberOfQuestions
lbox_QuestionList.AddItem "Question " & X ' Populate the listbox with items
QuestionSaved(X) = True ' Flag the initial state as saved, for each question
If Not X = rst.RecordCount Then rst.MoveNext
Next X
'
' Select the first question by default. Note that the Listbox ListIndex starts at 0, whereas the questions start at 1
SelectedListIndex = 0
CurrentQuestion = 1
EventsOn = True
lbox_QuestionList.ListIndex = SelectedListIndex
End Sub
Private Sub lbox_QuestionList_Change()
' Ensure this event does NOT keep firing in a loop, when changed programmatically
If Not EventsOn Then Exit Sub
'
If Not QuestionSaved(CurrentQuestion) Then
If MsgBox(Prompt:="Abandon changes to current question?", Title:="Current question not saved", Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
' Abandon changes = Yes
' Mark as saved
QuestionSaved(CurrentQuestion) = True
' Then proceed to change as normal
' (If the user comes back to this question, it will be re-loaded from memory in its original form)
' This works okay
Else
' Abandon changes = No
EventsOn = False ' So this sub is not called again
' REVERT the ListBox selection. Do this by recalling the current question number, and applying it to the ListIndex
SelectedListIndex = CurrentQuestion - 1 ' Remember that the index will be minus 1, due to indexing starting at 0
lbox_QuestionList.ListIndex = SelectedListIndex
EventsOn = True
Exit Sub ' This should be the end of it. But somehow, it's not...
End If
End If
' Proceed with loading a new question according to the new selected ListIndex
SelectedListIndex = lbox_QuestionList.ListIndex ' Recognise the current selection
' ListIndex starts at zero, so we need to add 1
CurrentQuestion = SelectedListIndex + 1
ShowQuestion CurrentQuestion
End Sub
Private Sub ShowQuestion(QuestionNumber As Long)
' Stripped out code for brevity. Basically loads details from a dictionary of classes, and populates into textboxes
End Sub
Private Sub cb_Save_Click()
' Stipped out code. Takes values of current text boxes and saves them into a class in a dictionary
' Mark the current question as saved:
QuestionSaved(CurrentQuestion) = True
End Sub
''''''''''' Event handlers ''''''''''''''
Private Sub tb_Question_Change()
DoChange
End Sub
' Several other form controls have similar events: all calling "DoChange" as below
Private Sub DoChange()
If Not EventsOn Then Exit Sub
QuestionSaved(CurrentQuestion) = False ' Flag the current question as NOT saved, if any changes are made to form values
End Sub
Naturally, I have searched for this problem - but there are no answers so far which have assisted me:
Listbox events firing strangely - relates to C# and not VBA
listbox selected item changed event fired two times - relates to C# and not VBA
vba listbox event fires twice - suggests that a SetFocus method of the Listbox could solve the issue. However I have tried this, and the problem remains
The logic of my code seems sound. The mystery is why the Change event is being called a second time, even after Exit Sub.
(curses to OP for getting this problem in my brain!)
In my testing, I used the following UserForm:
The code below uses the ListBox1_AfterUpdate event, and I believe it may work for you.
Option Explicit
Private Const TOTAL_QUESTIONS As Long = 3
Private qSaved As Variant
Private selectedDuringTextboxChange As Long
Private eventsInProgress As Long
Private Sub ListBox1_AfterUpdate()
Debug.Print "listbox clicked, item " & (ListItemSelected() + 1) & " selected"
If eventsInProgress > 0 Then
Debug.Print " ... event in progress, exiting"
eventsInProgress = eventsInProgress - 1
Exit Sub
End If
If Not qSaved(selectedDuringTextboxChange) Then
Dim answer As VbMsgBoxResult
answer = MsgBox("Abandon changes?", vbYesNo + vbDefaultButton2)
If answer = vbYes Then
Debug.Print "yes, abandon the changes"
qSaved(selectedDuringTextboxChange) = True
Else
Debug.Print "nope, keep the changes"
'--- return to the previously selected list item
eventsInProgress = eventsInProgress + 1
UnselectAll
ListBox1.Selected(selectedDuringTextboxChange - 1) = True
ListBox1.ListIndex = selectedDuringTextboxChange - 1
End If
End If
End Sub
Private Sub QuitButton_Click()
Me.Hide
End Sub
Private Sub SaveButton_Click()
qSaved(ListBox1.ListIndex + 1) = True
End Sub
Private Sub TextBox1_Change()
selectedDuringTextboxChange = ListBox1.ListIndex + 1
qSaved(selectedDuringTextboxChange) = False
Debug.Print "changed text for question " & selectedDuringTextboxChange
End Sub
Private Sub UserForm_Initialize()
ReDim qSaved(1 To TOTAL_QUESTIONS)
selectedDuringTextboxChange = 1
With ListBox1
Dim i As Long
For i = 1 To TOTAL_QUESTIONS
.AddItem "Question " & i
qSaved(i) = True
Next i
.Selected(0) = True
End With
eventsInProgress = False
End Sub
Private Sub UnselectAll()
eventsInProgress = eventsInProgress + 1
With ListBox1
Dim i As Long
For i = 0 To .ListCount - 1
.Selected(i) = False
Next i
End With
eventsInProgress = eventsInProgress - 1
End Sub
Private Function ListItemSelected() As Long
ListItemSelected = -1
With ListBox1
Dim i As Long
For i = 0 To .ListCount - 1
If .Selected(i) Then
ListItemSelected = i
End If
Next i
End With
End Function
Private Sub WhichListItem_Click()
With ListBox1
Dim i As Long
For i = 0 To .ListCount - 1
Debug.Print "listbox item(" & i & ") = " & .Selected(i)
Next i
End With
Debug.Print "eventsInProgress = " & eventsInProgress
End Sub
After looking into it for awhile, it appears that having the listbox set its own listindex from within its own change event (effectively recursively calling it) causes some weird backend issues. Fortunately, it's easy enough to deal with by migrating that bit out to its own function. After some experimenting, the best way to do it would be to create a function that clears and repopulates the listbox, so create this function in your UserForm code:
Private Function PopulateListbox(Optional ByVal arg_lSelected As Long = -1)
Me.lbox_QuestionList.Clear
Dim X As Long '
For X = 1 To NumberofQuestions
lbox_QuestionList.AddItem "Question " & X ' Populate the listbox with items
QuestionSaved(X) = True ' Flag the initial state as saved, for each question
'If Not X = rst.RecordCount Then rst.MoveNext
Next X
Me.lbox_QuestionList.ListIndex = arg_lSelected
End Function
Now adjust your Initialize event to look like this (note that you need to define NumberofQuestions here, and then call the new function at the end to populate the listbox and select the first entry):
Private Sub UserForm_Initialize()
' Stripped out lots of code here. Basically opens a recordset and loads values
NumberofQuestions = 3 'This is where NumberofQuestions gets defined
ReDim QuestionSaved(1 To NumberofQuestions)
ReDim aAnswers(1 To NumberofQuestions)
'
' Select the first question by default. Note that the Listbox ListIndex starts at 0, whereas the questions start at 1
SelectedListIndex = 0
CurrentQuestion = 1
EventsOn = True
PopulateListbox SelectedListIndex 'Call the new function and set the 1st selection
End Sub
Lastly, update your listbox_change event to look like this (basically just outsourcing the setting of the listbox entry to the new function):
Private Sub lbox_QuestionList_Change()
' Ensure this event does NOT keep firing in a loop, when changed programmatically
If Not EventsOn Then Exit Sub
'
If Not QuestionSaved(CurrentQuestion) Or aAnswers(CurrentQuestion) <> Me.tb_Question.Text Then 'I added the second condition for testing purposes, may not be necessary in your full code
If MsgBox(Prompt:="Abandon changes to current question?", Title:="Current question not saved", Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
' Abandon changes = Yes
' Mark as saved
QuestionSaved(CurrentQuestion) = True
' Then proceed to change as normal
' (If the user comes back to this question, it will be re-loaded from memory in its original form)
' This works okay
Else
' Abandon changes = No
EventsOn = False ' So this sub is not called again
' REVERT the ListBox selection. Do this by recalling the current question number, and applying it to the ListIndex
SelectedListIndex = CurrentQuestion - 1 ' Remember that the index will be minus 1, due to indexing starting at 0
PopulateListbox SelectedListIndex 'Call your new function here
EventsOn = True
Exit Sub ' This should be the end of it. But somehow, it's not...
End If
End If
' Proceed with loading a new question according to the new selected ListIndex
SelectedListIndex = lbox_QuestionList.ListIndex ' Recognise the current selection
' ListIndex starts at zero, so we need to add 1
CurrentQuestion = SelectedListIndex + 1
ShowQuestion CurrentQuestion
End Sub
I had a problem with a Private Sub ListBox_Click() running twice.
When I cleared the ControlSource in the list box properties it fixed the problem. I had to add a line of code to specifically write the value from the listbox to the cell in the worksheet. At first the cell would not display the data so I set the range name to another cell and that was OK. So, I then dragged and dropped the new cell into the original location.
I don't understand where the problem originated, but the fix worked.
I had a similar unexpected issue, so maybe someone will find this result helpful.
Within a multi-selection-enabled Listbox_Change event, I checked the value of the currently-selected item to see whether it had been checked or unchecked.
Private Sub lstBox_Change()
With lstBox
If .Selected(.ListIndex) Then
' Call Method A.
Else
' Call Method B.
End If
End With
End Sub
When the list was checked, it would properly detect the selection and call A--but then, when stepping through the code and reaching the Change event's End Sub, the checkbox would automatically become unselected, and the Change event would fire again. Note that I wasn't setting any value in the ListBox itself; I was only checking to see whether the current item was checked or unchecked. But, somehow, that triggered it to unselect itself. (Also, this only seemed to happen on the first call to the Change event. Thereafter it behaved normally.)
I tried some of the other fixes, but BeforeUpdate and AfterUpdate never seemed to fire at all. The problem went away when I moved the selection test outside of the If statement and put the result into a Boolean instead:
Private Sub lstBox_Change()
With lstBox
BooleanResult = (.Selected(.ListIndex) = True)
If BooleanResult Then
' Call Method A.
Else
' Call Method B.
End If
End With
End Sub
After that, the ListBox consistently behaved as expected.

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

Excel Userform Textbox Behavior

I have a textbox on a userform. It is the only textbox on the form. There are three labels and two buttons in addition to this textbox. Basically, I want the focus to remain on this textbox under all scenarios, other than the moment that one of the buttons would be clicked, but then I want the focus to come right back to the text box. Both buttons have "TakeFocusOnClick" and "TabStop" set to False. I was having problems with getting the focus set to the textbox, which is why I changed these two settings.
Once I changed these settings, the Enter key in the textbox stopped having any effect. I have events written for _AfterUpdate and _KeyPress for the textbox, but they don't fire. As you can see in the code, I have commented out the lines to set the focus to this textbox. Since it is now the only object that can take focus, these lines are not needed (theoretically). When I allowed the other objects to take focus, these lines weren't having any effect (focus was switching to the buttons despite these SetFocus lines).
Here is the code. It is very simple, except that the Enter key isn't triggering the event. Can anyone see why? Thanks.
Private Sub btnDone_Click()
Application.Calculation = xlCalculationAutomatic
formMath.Hide
'Clear statistics
Range("attempts").Value = 0
Range("correct").Value = 0
Sheet5.Range("A2:W500").ClearContents
End Sub
Private Sub btnSubmit_Click()
recordAnswer
'formMath.txtAnswer.SetFocus
End Sub
Private Sub txtAnswer_AfterUpdate()
recordAnswer
'formMath.txtAnswer.SetFocus
End Sub
Private Sub txtAnswer_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If KeyAscii = 13 Then
recordAnswer
End If
End Sub
Private Sub UserForm_Initialize()
'Initialize manual calculation
Application.Calculation = xlCalculationManual
Application.Calculate
'Initialize statistics
Range("attempts").Value = 0
Range("correct").Value = 0
Sheet5.Range("A2:W500").ClearContents
'Initialize first problem
newProblem
End Sub
Sub recordAnswer()
'Update statistics
Dim attempts, correct As Integer
attempts = Range("attempts").Value
correct = Range("correct").Value
Range("results").Offset(attempts, 0).Value = attempts + 1
Range("results").Offset(attempts, 1).Value = lblTopNum.Caption
Range("results").Offset(attempts, 2).Value = lblBotNum.Caption
Range("results").Offset(attempts, 3).Value = lblBop.Caption
Range("results").Offset(attempts, 4).Value = Range("Answer").Value
Range("results").Offset(attempts, 5).Value = txtAnswer.Text
If (Range("Answer").Value = txtAnswer.Text) Then
Range("results").Offset(attempts, 6).Value = 1
Else
Range("results").Offset(attempts, 6).Value = 0
End If
'Update attempts and success
Range("attempts").Value = attempts + 1
Range("correct").Value = correct + 1
newProblem
End Sub
Sub newProblem()
Application.Calculate
formMath.lblTopNum.Caption = Range("TopNum").Value
formMath.lblBotNum.Caption = Range("BotNum").Value
formMath.lblBop.Caption = Range("ProbType").Value
formMath.txtAnswer.Value = ""
'formMath.txtAnswer.SetFocus
End Sub
To start off
You can either in the design mode, set the TabIndex property of the Textbox to 0 or you can set the focus on the textbox in the UserForm_Initialize()
Private Sub UserForm_Initialize()
TextBox1.SetFocus
End Sub
Similarly after any operation that you perform, simply call the TextBox1.SetFocus to revert to the textbox.
Option Explicit
Private Sub UserForm_Initialize()
TextBox1.SetFocus
End Sub
Private Sub CommandButton1_Click()
MsgBox "Hello from Button 1"
TextBox1.SetFocus
End Sub
Private Sub CommandButton2_Click()
MsgBox "Hello from Button 2"
TextBox1.SetFocus
End Sub
Let me know if this is not what you want?
I found a way to accomplish this. In the code above I took out the _KeyPress and _AfterUpdate events and replaced them with:
Private Sub txtAnswer_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Select Case KeyCode
Case 13: recordAnswer
End Select
End Sub
Not sure why the other methods didn't work, but this does.
Also not sure why simply setting the focus directly didn't work. I suspect that the focus was being set, but then something else was happening subsequently that was changing the focus off of the textbox. Just a guess.
Thanks for the help. I appreciate it.

Resources