I'm having a problem validating three drop down's at once. Here are the test cases:
cbo_fac1 - user must select this as a pre-requisite for cbo_fac1 and cbo_fac2
cbo_fac2 - user must select cbo_fac1 as a pre-requisite
cbo_fac3 - user must select cbo_fac1 and cbo_fac2 as a pre-requisite
Here is the code I'm using so far. Unfortunately in some cases i.e. if the user tries to select cbo_fac3 first, it seems to be looping several of the errors. Is there any way I can merge these functions and have it only display the error once in each case?
Private Sub cbo_fac2_Enter()
If Len(cbo_fac1.Value) = 0 Then
MsgBox ("Please select a first preference before selecting a second preference")
cbo_fac1.SetFocus
Exit Sub
End If
End Sub
Private Sub cbo_fac3_Enter()
If Len(cbo_fac2.Value) = 0 & Len(cbo_fac3.Value) = 0 Then
MsgBox ("Please select a first preference before selecting a second/third preference")
cbo_fac1.SetFocus
Exit Sub
End If
If Len(cbo_fac2.Value) = 0 Then
MsgBox ("Please select a second preference before selecting a third preference")
cbo_fac2.SetFocus
Exit Sub
End If
End Sub
This approaches the problem from the other end, enabling or disabling the comboboxes based on whether the ones previous are filled in. Here the cascading events are your friend in the case of the 3rd combobox, as clearing the 1st one triggers a change in the 2nd, which clears the 3rd. Add this code to your userform:
Private Sub cbo_fac1_Change()
With cbo_fac2
.Enabled = Len(cbo_fac1.Value) > 0
If Not .Enabled Then
.ListIndex = -1
End If
End With
End Sub
Private Sub cbo_fac2_Change()
With cbo_fac3
.Enabled = Len(cbo_fac2.Value) > 0
If Not .Enabled Then
.ListIndex = -1
End If
End With
End Sub
Just a note that if you end up with a much longer series of these, you'll want to look into a WithEvents control class.
Also, as much as possible, try to avoid error messageboxes when a user enters a control. Try to either guide them with visual cues - disabled, empty controls in this case - or wait until they hit OK and then tell them what they need to change.
If anybody else wants to try this out, create a form with the 3 comboboxes, and then add this initialization code to your form:
Private Sub UserForm_Initialize()
Dim i As Long
With Me
With .cbo_fac1
For i = 1 To 10
.AddItem .Name & i
Next i
End With
With .cbo_fac2
For i = 1 To 10
.AddItem .Name & i
Next i
.Enabled = False
End With
With .cbo_fac3
For i = 1 To 10
.AddItem .Name & i
Next i
.Enabled = False
End With
End With
End Sub
Related
I have a Useform which shows a list, see picture, i have forced the userform to always begin by having ComboBox1.text = "Please Select Item".
However, how do i force the user to actually select an item before hitting the Ok Button??
My understanding is that i'll use a ComboBox1_BeforeUpdate sub - however, can't figure out the details.
On activation i disable the OK button
Private Sub UserForm_Activate()
OK.Enabled = False
...
End Sub
Then on Change i reactivate the button
Private Sub ComboBox1_Change()
If ComboBox1.Text <> "Please Select Item" Then
OK.Enabled = True
Else
OK.Enabled = False
End If
End Sub
I have add a ListBox from Active X Controls in my Excel File and made it a multi select box with checkboxes.
I have also added a selection change event in the VB script against this list box.
Sub lstMultiSelectBox_Change()
If blnCheck = False Then
CheckAll
End If
End Sub
Now what I am struggling to find is that which item was last checked. With this information I want to implement Select All and Un Select All feature in this list box.
In order to make ListBox1_Change event returning the last selected list box value, you can use the solution. It can detect the selected value, independent of its position in the list:
Create a Private variable on top of the sheet module where the list box exists (in the declarations area):
Private colS As New Collection
Then copy the next adapted event code:
Private Sub ListBox1_Change()
Dim i As Long
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
If colS.Count = 0 Then
colS.Add ListBox1.List(i), ListBox1.List(i)
Else
If Not itExists(colS, ListBox1.List(i)) Then
colS.Add ListBox1.List(i), ListBox1.List(i)
End If
End If
Else
If itExists(colS, ListBox1.List(i)) Then
colS.Remove ListBox1.List(i): Exit Sub
End If
End If
Next i
If colS.Count > 0 Then MsgBox colS(colS.Count)
End Sub
If you want it triggering only if the selected value is "Select All", then replace the last event code line with something like:
If colS.Count > 0 Then
If colS(colS.Count) = "Select All" then
'do whatever you need in such a case
'but, if you try selecting all of lines, in order to avoid the event
'being triggered again, you should use 'Application.EnableEvents = False`, before selecting and 'Application.EnableEvents = True` after
End If
End If
The simplest solution should be the one suggested in the first comment:
If Listbox1.Selected(1) = True Then
'do whatever you need
End If
But, in order to make it working as it should, the line "Select All" should be the second of the list...
Hi I try this code in my Userform to check if Data entered in textbox1 is a number and if is not show a msgbox to user and select text in textbox1, but below code doesn't select text in textbox1 when Userform is vBModeless
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1) Then
MsgBox " only number"
TextBox1.SetFocus
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
End If
End Sub
is any solution?
The root of the problem isn't a selection, since it there and works as expected:
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1) Then
MsgBox " only number"
TextBox1.SetFocus
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
Debug.Print TextBox1.SelText
End If
End Sub
I think the fundamental problem here is that MSForms controls aren't real windows, but "windowless" entity without window handle (of course, there's exceptions like listbox, tabstrip, multipage), which easily can be tested via hidden method:
'Which obviously returns a zero.
Debug.Print TextBox1.[_GethWnd]
In other hand there's the Window's message-passing model where each control is a window (hence Windows OS) with a proper window handle and with ability to send and recive messages like WM_SETFOCUS/WM_KILLFOCUS and act appropriately.
And back to MSForms - the UserForm manages all the interaction between outer world and child controls internally.
Let's start by declaring WIN API function GetFocus:
Public Declare Function GetFocus Lib "user32.dll" () As Long
And let's add some of Debug.Print's to see what is happening:
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1) Then
Debug.Print "--"
Debug.Print GetFocus
MsgBox " only number"
Debug.Print GetFocus
TextBox1.SetFocus
Debug.Print GetFocus
Debug.Print "--"
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
End If
End Sub
Which yields this sequence:
--
<userform hwnd>
<outer hwnd>
<outer hwnd>
--
As you can see - the SetFocus has no effect, because the Userform has no idea that focus is lost (hence there's no Exit event either). To overcome this problem you should explicitly lose your focus by transferring focus to another child control or by switching Enabled (or even Visible) property:
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1) Then
Debug.Print "--"
Debug.Print GetFocus
TextBox1.Enabled = False
'or use CommandButton1.SetFocus or something
MsgBox " only number"
TextBox1.Enabled = True
Debug.Print GetFocus
TextBox1.SetFocus
Debug.Print GetFocus
Debug.Print "--"
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
End If
End Sub
Which yields a desired appearance and a proper sequence:
--
<userform hwnd>
<outer hwnd>
<userform hwnd>
--
As a conclusion, the cause is internal and external focus states got out of sync, which stems from a slightly different managment model between MSForms and WinForms/WinAPI plus a non-modal regime of work, that mixes them both, giving an opportunity to lose focus to something non-MSForms.
In my version of Excel A msgbox is always vbModal, it cannot be vbModeless, you can only set its Modal scope property to be as application level or system level
At Application level, it stops the application until it is responded
At system level it suspends all applications until the user responds
to it
In order to do what you intend to do; I have created a Modeless UserForm and use it as a message box
The code becomes
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1) Then
UserForm2.Label1 = "Only Number is Allowed"
UserForm2.Show
'At this point TextBox1 has lost focus,
'to set the focus again you have to setfocus on something else
'and then again set focus on textbox1 (a way to reinitialize it).
'I have added a hidden textbox2 and will set focus on it
TextBox2.Visible = True
TextBox2.SetFocus
TextBox2.Visible = False
TextBox1.SetFocus
TextBox1.SelStart = 0
TextBox1.SelLength = Len(TextBox1.Text)
End If
End Sub
The screenshot is only a test, you can do the formatting etc according to your application.
I vote for usmanhaq and CommonSense!
just something to add: I've tried to implement similiar thing on one of my projects, I end up avoiding pop up another window. Just use a label to alert.
And after i implement this i find this more userfriendly!
Hope this helps!
userform:
Private Sub TextBox1_Change()
If Not IsNumeric(TextBox1.Value) Then
Label1.Caption = "NUMBER ONLY!"
UserForm1.TextBox1.SetFocus
UserForm1.TextBox1.SelStart = FirstNonDigit(TextBox1.Value) - 1
UserForm1.TextBox1.SelLength = Len(TextBox1.Text)
Else
Label1.Caption = ""
End If
End Sub
this function is funded online that would help highlight starting from the first non number
Public Function FirstNonDigit(xStr As String) As Long
Dim xChar As Integer
Dim xPos As Integer
Dim I As Integer
Application.Volatile
For I = 1 To Len(xStr)
xChar = Asc(Mid(xStr, I, 1))
If xChar <= 47 Or _
xChar >= 58 Then
xPos = I
Exit For
End If
Next
FirstNonDigit = xPos
End Function
I have a combo box in a Excel Userform that consist of User Group Types.
Depending on the user access level, I would like to have some Option\item disable or not visible.
I don't want to use Removeitem, Because I would have to repopulate the list every time!
sub ComboBox_Enter()
accessLvl = 1
ComboBox.AddItem "0-Show"
ComboBox.AddItem "1-Hide or disable"
ComboBox.AddItem "2-Show"
ComboBox.AddItem "3-Show"
For i = 0 To 3
if accessLvl = 1 Then ComboBox.List(1).Hidden = True ' This not does work!! ''
Next
End sub
I just want it to be disabled\grayed out or not visible but still in the Combobox list!*
AFAIK, you can't do that but there is an alternative. The user will not be able to select certain items (whichever you specify) even though it will be visible and not disabled.
For this try this code
Dim boolC As Boolean
'~~> Add Sample data
Private Sub UserForm_Initialize()
ComboBox1.AddItem "Please Choose Again"
For i = 1 To 10
ComboBox1.AddItem i
Next i
End Sub
'~~> This will not let the user select items in 2nd
'~~> 3rd and 4th items
Private Sub ComboBox1_Change()
If Not boolC Then
boolC = True
Select Case ComboBox1.ListIndex
Case 1, 2, 3: ComboBox1.ListIndex = 0
End Select
boolC = False
End If
End Sub
Screenshot
Let's say your form looks like this on form start up.
The moment you select the 2nd ,3rd or the 4th item, you will get Please Choose Again
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.