Inputbox selection causing: "Text is not valid." Error [duplicate] - excel

The current function I use to collect text InputBox can't accept more than 255 characters apparently, and I need to be able to collect more than that? Is there a parameter or different function I can use to increase this limit?

To be pedantic, the Inputbox will let you type up to 255 characters, but it will only return 254 characters.
Beyond that, yes, you'll need to create a simple form with a textbox. Then just make a little "helper function" something like:
Function getBigInput(prompt As String) As String
frmBigInputBox.Caption = prompt
frmBigInputBox.Show
getBigInput = frmBigInputBox.txtStuff.Text
End Function
or something like that...

Thanks BradC for the info that. My final code was roughly as follows, I have a button that calls the form that I created and positions it a bit as I was having some issues with the form being in the wrong spot the everytime after the first time I used.
Sub InsertNotesAttempt()
NoteEntryForm.Show
With NoteEntryForm
.Top = 125
.Left = 125
End With
End Sub
The userform was a TextBox and two CommandButtons(Cancel and Ok). The code for the buttons was as follows:
Private Sub CancelButton_Click()
Unload NoteEntryForm
End Sub
Private Sub OkButton_Click()
Dim UserNotes As String
UserNotes = NotesInput.Text
Application.ScreenUpdating = False
If UserNotes = "" Then
NoteEntryForm.Hide
Exit Sub
End If
Worksheets("Notes").ListObjects("Notes").ListRows.Add (1)
Worksheets("Notes").Range("Notes").Cells(1, 1) = Date
Worksheets("Notes").Range("Notes").Cells(1, 2) = UserNotes
Worksheets("Notes").Range("Notes").Cells(1, 2).WrapText = True
' Crap fix to get the wrap to work. I noticed that after I inserted another row the previous rows
' word wrap property would kick in. So I just add in and delete a row to force that behaviour.
Worksheets("Notes").ListObjects("Notes").ListRows.Add (1)
Worksheets("Notes").Range("Notes").Item(1).Delete
NotesInput.Text = vbNullString
NotesInput.SetFocus ' Retains focus on text entry box instead of command button.
NoteEntryForm.Hide
Application.ScreenUpdating = True
End Sub

I don't have enough rep to comment, but in the sub form_load for the helper you can add:
me.AutoCenter = True
Outside of that form, you can do it like this:
NoteEntryForm.Show
Forms("NoteEntryForm").AutoCenter = True
My Access forms get all confused when I go from my two extra monitors at work to my one extra monitor at home, and are sometimes lost in the corner. This AutoCenter has made it into the form properties of every one of my forms.

Related

Problem with multiline string in userform

I am having a issue with multiline stings in a userform.
When a user selects a option, the code checks if the selected answer matches the correct answer and then shows if right or wrong. But in either case the code says it is wrong.
Example of a option is:
If you see the string, brush it off sideways
Place icepack/cold flannel to reduce swelling
Elevate area to reduce bloodflow
Private Sub OptionButton1_Click()
rowNum = Selection.Row - Selection.ListObject.Range.Row
DeclareVars
Column = examtable.ListColumns("Right ans").DataBodyRange(rowNum)
CorrectAns = examtable.ListColumns("Right ans").DataBodyRange(rowNum).Offset(0, Column)
RightWrong.Visible = True
If OptionButton1.Caption = CorrectAns Then
RightWrong.BackColor = &HFF00&
RightWrong.Caption = "Right"
Else
RightWrong.BackColor = &HFF&
RightWrong.Caption = "Wrong"
End If
End Sub
What i am expecting is that if correct, shows right, or wrong if incorrect
If I understand correctly you have multiple option buttons below one another? Option button one will always have the same caption. If it is selected it can become true (indicated by the black dot), but the caption will not change.
Lets say it looks like this
two option buttons
Then the upper is called OptionButton1, the lower OptionButton2.
You can check
If OptionButton1 then
RightWrong.BackColor = &HFF00&
RightWrong.Caption = "Right"
Else
RightWrong.BackColor = &HFF&
RightWrong.Caption = "Wrong"
End if
You could use a combobox (these are the drop down menus).
You could populate it with an array of the strings you want to test and ask if the answers concur.
When loading the user form use
Private Sub UserForm_Activate()
ComboBox1.list = Array("brush it off sideways", "Place icepack/cold flannel to reduce swelling", "Elevate area to reduce bloodflow")
End Sub
this will look as followed
The user form with the list open
Then you can say combobox.value = CorrectAns

Pass subroutine name as string to use subs as general purpose input validation

I read through several posts about similar problems and tried many solutions offered by this and other communities. I cannot tailor any of these to my specific needs.
I have an Excel workbook that generates a timesheet and a detailed job report based on the information provided in a userform.
The job report and the timesheet are exported to an Access table (or imported from said table to be edited or deleted).
I have a working version with repetitive code for validating the userform inputs.
There are eight inputs that must meet criteria.
i) must be a number
ii) must not be less than a minimum value
iii) must not be greater than a maximum value
I have a subroutine for each of these inputs that checks these criteria using BeforeUpdate, and calls another subroutine to make visible changes to the userform to alert the user of an invalid entry (alter the label color and caption, textbox or dropbox color, etc.).
Using AfterUpdate, I have a subroutine for each of the eight inputs that reverts these changes once a valid entry has been made.
This means I have 24 subroutines with basically the same code, where I feel there should only be three subroutines that can be used more generally.
Here is the code I have for these subroutines, as it is being used for one specific input:
Sub #1
Private Sub NumberOfTechs_BeforeUpdate(ByVal CAncel As MSForms.ReturnBoolean)
If Not IsNumeric(numberOfTechs) Then
Call NumberOfTechsInvalid("Must be a number!", numberOfTechsLabel, numberOfTechs)
CAncel = True
Else
If numberOfTechs < 1 Then
Call NumberOfTechsInvalid("Cannot be less than 1!", numberOfTechsLabel, numberOfTechs)
CAncel = True
ElseIf numberOfTechs > 6 Then
Call NumberOfTechsInvalid("Cannot exceed 6!", numberOfTechsLabel, numberOfTechs)
CAncel = True
End If
End If
End Sub
Sub #2
Private Sub NumberOfTechsInvalid(errorCaption As String, targetLabel As Object, targetControl As Object)
targetLabel.caption = errorCaption
targetLabel.ForeColor = rgbRed
targetControl.BackColor = rgbPink
targetControl.SelStart = 0
targetControl.SelLength = Len(targetControl)
End Sub
Sub # 3
Private Sub NumberOfTechs_AfterUpdate()
numberOfTechsLabel.ForeColor = Me.ForeColor
numberOfTechsLabel = "Number Of Techs"
numberOfTechs.BackColor = rgbWhite
' Call next subroutine
End Sub
I have a comment at the bottom of sub#3 that says "call next subroutine". This is where I am having difficulty.
I can pass the minimum and maximum values as variables, as well as specify the target control and label based on which user input triggers the call to sub#1.
The issue is passing the next subroutine as a string.
I tried placing these subroutines in their own module and using Application.Run. I tried using CallByName with these subs within the userform code.

VBA best practices for modules relative to modeless userforms

I came across this similar issue and read the replies: Modeless form that still pauses code execution
I have been attempting to apply in my own situation the suggestion provided by David Zemens. In my situation, I cannot seem to find an approach that incorporates Mr. Zemen's suggestion without also utilizing a GoTo.
I am wondering if there is a better or more elegant solution.
Here is an outline of what I am doing:
I have a UserForm with a Command Button that begins the code execution that will perform several actions on multiple Excel workbooks. As such, there are a number of blocks of code and the successful completion of one block of code allows for the execution of the subsequent block of code.
At a certain point, depending on the situation, the code might require User input; in other situations, the needed data is obtainable from an Excel. If input is needed from the User, another UserForm is displayed.
The User may need to view several different Excel sheets before entering the input, so the UserForm is modeless. So the code comes to a stop until the User enters the needed input and clicks another Command Button.
It is at this point I am having trouble: how to resume the program flow. Is the only way to 'pick-up where it left-off' is by using a GoTo statement? Or is there some way to organize the modules so there is a single consistent program flow, defined in one spot and not duplicated from the point at which User input might be needed?
Here is my take on the problem . Hope I understood the problem correctly.
Assumptions:
There are two user forms.
UserForm1 with a button to start the processing.
UserForm2 with a button to supply intermediate input.
A sub inside a module to start/ launch UserForm1.
VBA Code (for the sub routine)
Sub LaunchUserForm1()
Dim frm As New UserForm1
'/ Launch the main userform.
frm.Show vbModeless
End Sub
VBA Code (for UserForm1)
Private Sub cmdStart_Click()
Dim i As Long
Dim linc As Long
Dim bCancel As Boolean
Dim frm As UserForm2
'/ Prints 1 to 5 plus the value returned from UserForm2.
For i = 1 To 5
If i = 2 Then
Set frm = New UserForm2
'/ Launch supplementary form.
frm.Show vbModeless
'<< This is just a PoC. If you have large number of inputs, better way will be
' to create another prop such as Waiting(Boolean Type) and then manipulate it as and when User
' supplies valid input. Then validate the same in While loop>>
'/ Wait till we get the value from UserForm2.
'/ Or the User Cancels the Form with out any input.
Do While linc < 1 And (linc < 1 And bCancel = False)
linc = frm.Prop1
bCancel = frm.Cancel
DoEvents
Loop
Set frm = Nothing
End If
Debug.Print i + linc
Next
MsgBox "User Form1's ops finished."
End Sub
VBA Code (for UserForm2)
Dim m_Cancel As Boolean
Dim m_prop1 As Long
Public Property Let Prop1(lVal As Long)
m_prop1 = lVal
End Property
Public Property Get Prop1() As Long
Prop1 = m_prop1
End Property
Public Property Let Cancel(bVal As Boolean)
m_Cancel = bVal
End Property
Public Property Get Cancel() As Boolean
Cancel = m_Cancel
End Property
Private Sub cmdlinc_Click()
'/Set the Property Value to 10
Me.Prop1 = 10
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'/ Diasble X button
Me.Cancel = True
Me.Hide
Cancel = True
End Sub
OK so here are my thoughts.
You have a userform frmSelectUpdateSheet which you wish to use in order to allow the user to select the sheet, when the sheet can't be determined programmatically. The problem is that if you do .Show vbModeless (which allows the user to navigate the worksheet/s), then code continues to execute which either leads to errors or otherwise undesired output.
I think it's possible to adapt the method I described in the previous answer. However, that's out of the question here unless you're paying me to reverse engineer all of your code :P
Assuming you have a Worksheet object variable (or a string representing the sheet name, etc.) which needs to be assigned at this point (and that this variable is Public in scope), just use the CommandButton on the form to assign this based on the selected item in the frmSelectUpdateSheet list box.
This is probably a superior approach for a number of reasons (not the least of which is trying to avoid application redesign for this sort of fringe case), such as:
This keeps your form vbModal, and does prevent the user from inadvertently tampering with the worksheet during the process, etc.
Using this approach, the thread remains with the vbModal displayed frmSelectUpdateSheet, and you rely on the form's event procedures for control of process flow/code execution.
It should be easier (and hence, cheaper) to implement; whether you're doing it yourself or outsourcing it.
It should be easier (and hence, cheaper) to maintain.
NOW, on closer inspection, it looks like you're already doing this sort of approach with the cmdbtnSelect_Click event handler, which leads me to believe there's a related/follow-up problem:
The sheet names (in listbox) are not sufficient for user to identify the correct worksheet. So if the user needs the ability to "scroll" the sheet (e.g., to review data which does not fit in the window, etc.), then add some spinner buttons or other form controls to allow them to navigate the sheet.

Excel VBA Userform How to add and subtract variables?

I have a Userform, on Page 1 of the Userform the user enters some text into a TextBox called as_1 and another TextBox called annualsaving1.
On Page 5 of the Userform I would like to add both variables up in Textbox called 'TextBox36'.
This is the code I have been using:
Private Sub Page5a()
I = as_1 + annualsaving1
TextBox36.Value = I
End Sub
When I use this code, only the value of as_1 appears. It doesn't add 'annualsaving1'.
Any help would be appreciated, Thank you :)
as_1 and annualsaving1 are of type TextBox. To add their values, you need to access their .Value property and convert it to type Integer:
I = CInt(as_1.value) + CInt(annualsaving1.value)
Be careful though: if user enters something that cannot be parsed as number, your program will crash.
try below
Private Sub Page5a()
TextBox36.Text = Val(as_1.Text) + Val(annualsaving1.Text)
End Sub

Disable button on a userForm

I'm trying to figure out how to disable a button within my userForm if a certain cell within my spreadsheet equals a certain number. I tried the code stated below, but it isn't working.
Private Sub UserForm_Initialize()
Label2 = Sheets("DATA").Range("AM2").Value
Label4 = Sheets("DATA").Range("AO2").Value
Label7 = Format(Sheets("DATA").Range("R8").Value, "Currency")
If Sheets("DATA").Range("AL10").Value = 10 Then
ActiveSheet.Shapes("CommandButton1").Select
UserFormact_Upgrade.CommandButton1.Enabled = False
Else
End If
End Sub
Your code should be working, as you're on the right path.
To test it, simply create a new form and add this code, you'll see it should work. Maybe you're having problems within the IF clause?
Besides, you don't need to select the shape prior to disabling it; just disable it right away.
Private Sub UserForm_Initialize()
CommandButton1.Enabled = False
End Sub
I know this is old, but got to this thread trying to solve my problem, and found a solution that wasn't mentioned here. So in case someone gets here like I did, and this didn't quite get them where they needed to go, I thought this might help.
I had a userform with a drop down box called cmdADAMFields, and I didn't want my submit button called FieldsSubmitButton to be enabled until I selected something from the dropdown box.
I had to break up my argument into two different private subs vs one larger If-Then-Else statement.
First, I put:
Private Sub UserForm_Activate()
If cmbADAMFields.ListIndex = -1 Then FieldsSubmitButton.Enabled = False
End Sub
Then when for my pulldown's private sub when it's value changed I wrote:
Private Sub cmbADAMFields_Change()
FieldsSubmitButton.Enabled = True
End Sub
The proper place for setting Enabled property is in Activate event (associated with Show method) and not Initialize event (associated with Load instruction).
The below code disable the button CommandButton1 when AL10 cell >= 10.
Private Sub UserForm_Activate()
CommandButton1.Enabled = ( Sheets("DATA").Range("AL10") < 10 )
End Sub
For buttons you can choose between normal buttons (property Enabled=False and property Visible=true), disabled buttons (property Enabled=False and property Visible=true) and invisible buttons (property Enabled=False and property Visible=False), that it is a cleaner interface, in most cases.
Concerning text boxes, besides normal, disabled and invisible status, there is a locked status, that is enabled and visible, but cannot be user edited. (property Locked = True)
A locked control only can be changed by VBA code. For instance, someone can includes date text boxes, that it's filled using a secondary popup date form with Calendar control.

Resources