Say you have aUserForm with TextBox1, TextBox3, TextBox3 and an OK Button.
To only allow the UserForm to close if all three TextBox have data I would use the following script assigned to the OK Button:
Private Sub CommandButton1_Click()
If Len(TextBox1.Value) >= 1 And _
Len(TextBox2.Value) >= 1 And _
Len(TextBox3.Value) >= 1 Then
Me.Hide
Else
MsgBox "Please Complete All Fields!"
End If
End Sub
Is there another way to do this besides an If statement?
Direct User Before Errors Are Made
Preferable to informing a user after an invalid action has been made is to prevent the user from performing that invalid action in the first place[1]. One way to do this is to use the Textbox_AfterUpdate event to call a shared validation routine that controls the Enabled property of your OK button, and also controls the display of a status label. The result is a more informative interface that only allows valid actions, thereby limiting the nuisance of msgbox popups. Here's some example code and screenshots.
Private Sub TextBox1_AfterUpdate()
RunValidation
End Sub
Private Sub TextBox2_AfterUpdate()
RunValidation
End Sub
Private Sub TextBox3_AfterUpdate()
RunValidation
End Sub
Private Sub RunValidation()
If Len(TextBox1.Value) = 0 Or Len(TextBox2.Value) = 0 Or Len(TextBox3.Value) = 0 Then
CommandButton1.Enabled = False
Label1.Visible = True
Else
CommandButton1.Enabled = True
Label1.Visible = False
End If
End Sub
Private Sub CommandButton1_Click()
Me.Hide
End Sub
The If Statement
As far as the If statement is concerned, there are a ton of ways that can be done, but I think anything other than directly evaluating TextBox.Value leads to unnecessary plumbing and code complexity, so I think it's hard to argue for anything other than the If statement in the OP. That being said, this particular If statement can be slightly condensed by capitalizing on its numeric nature, which allows for
Len(TextBox1.Value) = 0 Or Len(TextBox2.Value) = 0 Or Len(TextBox3.Value) = 0
to be replaced with
Len(TextBox1.Value) * Len(TextBox2.Value) * Len(TextBox3.Value) = 0
Although that doesn't gain you much and is arguably less readable code, it does allow for a condensed one liner, especially if the textboxes are renamed...
If Len(TB1.Value) * Len(TB2.Value) * Len(TB3.Value) = 0 Then
.Value vs .Text
Lastly, in this case, I think .Value should be used instead of .Text. .Text is more suited for validating a textbox entry while its being typed, but in this case, you're looking to validate a textbox's saved data, which is what you get from .Value.
More User feedback - Colorization
I almost forgot, I wanted to include this example of how to include even more user feedback. There is a balance between providing useful feedback and overwhelming with too much. This is especially true if the overall form is complicated, or if the intended user has preferences, but color indication for key fields is usually beneficial. A lot of applications may present the form without color at first and then colorize it if the user is having trouble.
Private InvalidColor
Private ValidColor
Private Sub UserForm_Initialize()
InvalidColor = RGB(255, 180, 180)
ValidColor = RGB(180, 255, 180)
TextBox1.BackColor = InvalidColor
TextBox2.BackColor = InvalidColor
TextBox3.BackColor = InvalidColor
End Sub
Private Sub TextBox1_AfterUpdate()
RunValidation Me.ActiveControl
End Sub
Private Sub TextBox2_AfterUpdate()
RunValidation Me.ActiveControl
End Sub
Private Sub TextBox3_AfterUpdate()
RunValidation Me.ActiveControl
End Sub
Private Sub RunValidation(ByRef tb As MSForms.TextBox)
If Len(tb.Value) > 0 Then
tb.BackColor = ValidColor
Else
tb.BackColor = InvalidColor
End If
If Len(TextBox1.Value) * Len(TextBox2.Value) * Len(TextBox3.Value) = 0 Then
CommandButton1.Enabled = False
Label1.Visible = True
Else
CommandButton1.Enabled = True
Label1.Visible = False
End If
End Sub
Private Sub CommandButton1_Click()
Me.Hide
End Sub
As I said in my comment, that is an ok way to do it. But i'll post this just so you have an example of another way. This would allow you to evaluate what is going into the text boxes as they are set.
Option Explicit
Dim bBox1Value As Boolean
Dim bBox2Value As Boolean
Dim bBox3Value As Boolean
Private Sub TextBox1_Change()
If Trim(TextBox1.Text) <> "" Then
bBox1Value = True
End If
End Sub
Private Sub TextBox2_Change()
If Trim(TextBox2.Text) <> "" Then
bBox2Value = True
End If
End Sub
Private Sub TextBox3_Change()
If Trim(TextBox3.Text) <> "" Then
bBox3Value = True
End If
End Sub
Private Sub CommandButton1_Click()
If bBox1Value = True And bBox2Value = True And bBox3Value = True Then
Me.Hide
Else
MsgBox "Please Complete All Fields!"
End If
End Sub
You can use a loop:
Private Sub CommandButton1_Click()
Dim n as long
For n = 1 to 3
If Len(Trim(Me.Controls("TextBox" & n).Value)) = 0 Then
MsgBox "Please Complete All Fields!"
Exit Sub
End If
Next n
Me.Hide
End Sub
You can use the below code
Private Sub CommandButton1_Click()
If Trim(TextBox1.Value & vbNullString) = vbNullString And _
Trim(TextBox2.Value & vbNullString) = vbNullString And _
Trim(TextBox3.Value & vbNullString) = vbNullString Then
Me.Hide
Else
MsgBox "Please Complete All Fields!"
End If
End Sub
I got the answer from this question
VBA to verify if text exists in a textbox, then check if date is in the correct format
Related
I've written the following code so that if a certain text exists in my listbox and "ok" button is clicked a certain thing is done.
Private Sub CommandButton3_Click()
If (Me.ListBox2.Text) <> ("PA") Then
Call macro1
ElseIf (Me.ListBox2.Text) <> "menu" Then
Sheets("menu").Visible = xlSheetVisible
Worksheets("menu").Activate
Else
MsgBox "Nothing is selected"
End If
End Sub
The problem is that when "ok" is clicked all events are still carried out even if the specified text isn't in the textbox.
You probably want to use = operator, and not <> operator. Also note that ListBox.List(i) is the correct way of getting selected item for single selection mode:
Private Sub CommandButton3_Click()
Dim SelectedItem = ListBox1.List(ListBox1.ListIndex)
If SelectedItem = "PA" Then
Call macro1
ElseIf SelectedItem = "menu" Then
Sheets("menu").Visible = xlSheetVisible
Worksheets("menu").Activate
Else
MsgBox "Nothing is selected"
End If
End Sub
Edit
Following your comment, you can create a function that looks for the existence of that item:
Private Function TextExists(text as String) as Boolean
Dim i as Long
For i = 0 To ListBox1.ListCount - 1
If ListBox1.List(i) = text Then
TextExists = True
Exit Function
End If
Next
TextExists = False
End Function
And then use this function in the main code like this:
Private Sub CommandButton3_Click()
If TextExists("PA") Then
Call macro1
ElseIf TextExists("menu") Then
Sheets("menu").Visible = xlSheetVisible
Worksheets("menu").Activate
Else
MsgBox "Nothing is selected"
End If
End Sub
N.B. I have written this manually here, without an IDE. Please check for indexes and other little things.
I'm trying to create some cue banners for my user form in Word. I got about half way through before I become stuck. I have it where the cue banner will disappear and clear the textbox once it has focus. If the user types their own text, that will be retained as well.
However, if the user doesn't type anything in the textbox once it has been cleared, I want to replace the cue banner along with its attributes (gray text and italicized). I can't seem to get it to work. Here's the code with everything related to this textbox below. The trouble is with the Leave event I think.
Private Sub UserForm_Initialize()
Me.txbShipToName1.Text = "name"
Me.txbShipToName1.Font.Italic = True
Me.txbShipToName1.ForeColor = &H80000006
End Sub
Private Sub txbShipToName1_Enter()
If Me.ActiveControl Is Me.txbShipToName1 And Me.txbShipToName1.Text = "name" Then
txbShipToName1.Font.Italic = False
txbShipToName1.ForeColor = &H80000008
txbShipToName1.Text = ""
End If
End Sub
Private Sub txbShipToName1_Leave()
If Me.ActiveControl Is Not txbShipToName1 And Me.txbShipToName1.Text = "" Then
txbShipToName1.Font.Italic = True
txbShipToName1.ForeColor = &H80000006
txbShipToName1.Text = LCase(txbShipToName1.Text)
txbShipToName1.Text = "name"
End If
End Sub
Private Sub txbShipToName1_Change()
If Me.ActiveControl Is Me.txbShipToName1 And Me.txbShipToName1 <> "name" Then
txbShipToName1.Text = UCase(txbShipToName1.Text)
End If
End Sub
I'm putting this here for anyone else who would like to know the solution. After a few days of messing with it, I finally realize I was over complicating things when I was messing with another piece of code. I eliminated the ActiveControl test because this is essentially already built into the Enter and Exit events. Here's the updated and working code.
Private Sub UserForm_Initialize()
'Populates cue banners
Me.txbShipToName1.Text = "Name"
Me.txbShipToName1.Font.Italic = True
Me.txbShipToName1.ForeColor = &H80000011
End Sub
Private Sub txbShipToName1_Enter()
'Removes cue banner upon entering textbox
If Me.txbShipToName1.Text = "Name" Then
txbShipToName1.Font.Italic = False
txbShipToName1.ForeColor = &H80000012
txbShipToName1.Text = ""
End If
End Sub 'txbShipToName1_Enter()
Private Sub txbShipToName1_Change()
'Converts textbox to uppercase
If Me.txbShipToName1.Text <> "Name" Then
txbShipToName1.Text = UCase(txbShipToName1.Text)
End If
End Sub 'txbShipToName1_Change()
Private Sub txbShipToName1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
'Replaces cue banner upon exiting textbox
If Me.txbShipToName1.Text = "" Then
txbShipToName1.Font.Italic = True
txbShipToName1.ForeColor = &H80000011
txbShipToName1.Text = "Name"
End If
End Sub 'txbShipToName1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
My User-Form gives the user an option of 3 colors[red, green, blue] for "High Values" and "Low Values". However, the user must choose one color for each and not the same color for both High and Low Values of course. The colors highlight low and high values within the data in order to differentiate them. I have attached the picture of my User Form and the part of my code where I'm unable to assign different options different colors in order for it work. Any help on how to correct my IF Logic would be greatly appreciated.
Public Function ShowInputsDialog(LowColor As Long, HighValue As Single, HighColor As Long, LowValue As Single)
Call Initialize
Me.Show
If Not Cancel Then
If optRed1.Value Then '<-- Assigning the 3 colors to the Low Values
LowColor = vbRed
ElseIf optGreen1.Value Then
LowColor = vbGreen
Else
LowColor = vbBlue
End If
If optRed2.Value Then HighColor = vbRed '<-- Assigning the 3 colors to the High Values
ElseIf optGreen2.Value Then HighColor = vbGreen
Else
HighColor = vbBlue
End If
End If
HighValue = txtHigher.Value
LowValue = txtLower.Value
ShowInputsDialog = Not Cancel
Unload Me
End Function
I'd go as follows:
add a BeforeUpdate() event for every radio button
have that event handler let the control assume the user input value if compatible with its "counterpart" control one
this, by means of a sub that check the active control value against its "counterpart" one
for instance you could add in your userform code pane the following code:
Private Sub optBlue1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub optBlue2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub OptGreen1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub OptGreen2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub OptRed1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub OptRed2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
CrossCheck
End Sub
Private Sub CrossCheck()
Dim optNr As String, optName As String
With Me
If .ActiveControl.ActiveControl.Value Then
optName = .ActiveControl.ActiveControl.name
optNr = Mid(optName, Len(optName), 1)
.ActiveControl.ActiveControl.Value = Not (.ActiveControl.ActiveControl.Value = .Controls(Replace(optName, optNr, IIf(optNr = "1", "2", "1"))).Value)
End If
End With
End Sub
of course a Class approach could relieve the burden of writing all those Private Sub optXXXX_BeforeUpdate() event handlers and give you more flexibility for both current coding and future code enhancements, but if you are sticking to only have three radio buttons it could be a little overkill
BTW all what above means that your ShowInputsDialog() sub must not care about option buttons compatibility and can do its plain value assigning work, for which I'd use a Select Case syntax instead of the If Then - Else If Then - End If one:
If Not Cancel Then
Select Case True
Case OptRed1.Value
LowColor = vbRed
Case OptGreen1.Value
LowColor = vbGreen
Case Else
LowColor = vbBlue
End Select
Select Case True
Case OptRed2.Value
HighColor = vbRed
Case OptGreen2.Value
HighColor = vbGreen
Case Else
HighColor = vbBlue
End Select
End If
or you could use a helper function:
Function GetColor(opt1 As MSForms.OptionButton, opt2 As MSForms.OptionButton) As Long
Select Case True
Case opt1.Value
GetColor = vbRed
Case opt2.Value
GetColor = vbGreen
Case Else
GetColor = vbBlue
End Select
End Function
and simply write
If Not Cancel Then
LowColor = GetColor(OptRed1, OptGreen1)
HighColor = GetColor(OptRed2, OptGreen2)
End If
I have a Userform that includes Text Boxes with multiple formats. I have the Initialize as blank ("") and then format them using afterupdate(). This all works fine, my issues come from the possibility of the user miss keying the data the first go around or just clicking aimlessly on there screen. After you input a value it formats it correctly when you move from the text box. But if you reselect the text box then move away again, it clears the value. And if you do this with the text box that is formatted as a percent it actually bugs out with a mismatch error.
Here is a slice of my current code:
Private Sub UserForm_Initialize()
ValueAnalysisTextBox.Value = ""
CapRateTextBox.Value = ""
End Sub
Private Sub ValueAnalysisTextBox_AfterUpdate()
ValueAnalysisTextBox.Value = Format(Val(ValueAnalysisTextBox.Value), "$#,###")
End Sub
Private Sub CapRateTextBox_AfterUpdate()
CapRateTextBox.Value = Format(Val(CapRateTextBox.Value) / 100, "Percent")
End Sub
Any thoughts on how to clean this up would be great.
Is this what you are trying?
Private Sub ValueAnalysisTextBox_AfterUpdate()
Dim amt As Double
amt = Val(Replace(ValueAnalysisTextBox.Value, "$", ""))
ValueAnalysisTextBox.Value = Format(amt, "$#,###")
End Sub
Private Sub CapRateTextBox_AfterUpdate()
Dim Perct As Double
Perct = Val(Replace(CapRateTextBox.Value, "%", "")) / 100
CapRateTextBox.Value = Format(Perct, "Percent")
End Sub
Note: I am not doing any other error handling. For example, user typing "Blah Blah" or pasting something else in the textbox. I am sure you can handle that.
I'd store the underlying values in the .Tag property of the TextBox, then use it to change the formatting back and forth in the Enter and Exit events:
Private Sub UserForm_Initialize()
ValueAnalysisTextBox.Value = vbNullString
ValueAnalysisTextBox.Tag = vbNullString
End Sub
Private Sub ValueAnalysisTextBox_Enter()
ValueAnalysisTextBox.Value = ValueAnalysisTextBox.Tag
End Sub
Private Sub ValueAnalysisTextBox_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If IsNumeric(ValueAnalysisTextBox.Value) Then
ValueAnalysisTextBox.Tag = Val(ValueAnalysisTextBox.Value)
ValueAnalysisTextBox.Value = Format$(ValueAnalysisTextBox.Tag, "$#,###")
Else
ValueAnalysisTextBox.Tag = vbNullString
End If
End Sub
Using: Access 2013 with ADO connection to SQL Server back-end database
A form in my Access database is dynamically bound at runtime to the results of a SELECT stored-procedure from SQL Server, and allows the user to make changes to the record.
It has 2 buttons: Save and Cancel.
It is shown as a pop-up, modal, dialog form, and it has a (Windows) Close button at the top right corner.
I've put VBA code to ask the user whether he wants to Save, Ignore or Cancel the close action.
But there are problems and it gives the aforementioned error if Cancel is clicked. There are also other problems, like, after the error occurs once, then any further commands (Save or Cancel or closing the form) don't work - I think this is because the VBA interpreter has halted due to the earlier error. Another complication is that arises - I now need to end the MS-Access process from Windows Task Manager, doing this and then restarting the database and then opening this form will give an error and the form won't load. When the form is then opened in Design mode, I can see the connection string for the form is saved in the Form's Record Source property (this happens only sometimes), and which looks something like this:
{ ? = call dbo.tbBeneficiary_S(?) }.
Here is my code:
Dim CancelCloseFlag As Boolean
Dim SavePrompt As Boolean
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim a As Integer
If SavePrompt Then
a = MsgBox("Do you want to save changes?", vbQuestion + vbYesNoCancel, "Changes made")
Select Case a
Case vbNo:
Me.Undo
CancelCloseFlag = False
Case vbYes:
'do nothing; it will save the changes
CancelCloseFlag = False
Case vbCancel:
Cancel = True
CancelCloseFlag = True
End Select
End If
End Sub
Private Sub Form_Dirty(Cancel As Integer)
SavePrompt = True
End Sub
Private Sub Form_Error(DataErr As Integer, Response As Integer)
If DataErr = 2169 Then
Response = acDataErrContinue
End If
End Sub
Private Sub Form_Load()
LoadBeneficiaryDetails
End Sub
Private Sub Form_Unload(Cancel As Integer)
If CancelCloseFlag Then
Cancel = True
End If
End Sub
Private Sub btCancel_Click()
If Me.Dirty Then
SavePrompt = True
End If
DoCmd.Close
End Sub
Private Sub btSave_Click()
SavePrompt = False
DoCmd.Close
End Sub
I'm stuck and would like to know how others go about this issue? Basically I want to offer the user the choice Save, Ignore, Cancel when the user attempts to close the form with either Cancel button or the (Windows) close button. If the user chooses Cancel, then it should just return to the form without changing or undoing any changes to the data. The solution may be simple but it escapes my overworked mind.
Thanks in advance!
Please try the following code - I tested against all six scenarios and the proper action is taken.
Option Compare Database
Option Explicit
Dim blnAction As Integer
Dim blnBeenThereDoneThat As Boolean
Private Sub Form_BeforeUpdate(Cancel As Integer)
If blnBeenThereDoneThat = True Then Exit Sub
blnBeenThereDoneThat = True
blnAction = MsgBox("Do you want to save changes?", vbQuestion + vbYesNoCancel, "Changes made")
Select Case blnAction
Case vbNo:
Me.Undo
Case vbYes:
'do nothing; it will save the changes
Case vbCancel:
Cancel = True
End Select
End Sub
Private Sub Form_Error(DataErr As Integer, Response As Integer)
If DataErr = 2169 Then
Response = acDataErrContinue
End If
End Sub
Private Sub Form_Load()
LoadBeneficiaryDetails
End Sub
Private Sub Form_Unload(Cancel As Integer)
If blnAction = vbCancel Then
blnBeenThereDoneThat = False
Cancel = True
End If
End Sub
Private Sub btCancel_Click()
If Me.Dirty Then
Form_BeforeUpdate (0)
End If
If blnAction = vbCancel Then
blnBeenThereDoneThat = False
Exit Sub
ElseIf blnAction = vbYes Then
DoCmd.Close
Else
DoCmd.Close
End If
End Sub
Private Sub btSave_Click()
If Me.Dirty Then
Form_BeforeUpdate (0)
End If
If blnAction = vbCancel Then
Exit Sub
Else
DoCmd.Close
End If
End Sub