Restrict what someone types in a textbox - forms

Here's what I want to do and I have a problem.
I want to restrict what an user types in certain textboxes. I want to leave him type only numbers but after 3 numbers to add a ";". (eg. 007;123;003;005;).
The problem is that my textbox Controls are generated through a bunch of code. So I can't or I don't know how to set an action to these controls.
The code I'm using to generate the controls is:
Set cControl = form.Controls("io" & masina).Add(
"Forms.Label.1", "lreper" & l & pagina, True)
With cControl
.Caption = "Reper"
.Width = 35
.Height = 9
.Top = 25 + k
.Left = 5
End With
Any ideas?
Thanks a lot!

You can use the key press event to restrict only numbers and the ";". Along with check conditions.
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
'// Numbers 0-9
Case 48 To 57
If Len(TextBox1.Text) = 3 And Right(TextBox1.Text, 3) Like "###" Then
KeyAscii = 0
GoTo DisplayFormatError
End If
'// Key ;
Case 59
If Len(TextBox1.Text) < 3 Or Not Right(TextBox1.Text, 3) Like "###" Then
KeyAscii = 0
GoTo DisplayFormatError
End If
Case Else
KeyAscii = 0
GoTo DisplayFormatError
End Select
Exit Sub
DisplayFormatError:
MsgBox "Please enter serial number in the format '000;000;000'", vbInformation, "Alert!"
End Sub
A better way would be to use a regular expression instead of the like method.
If you need help adding the events for your controls at runtime have a look at:
Add controls and events to form at runtime
EDIT (REQUEST BY TIAGO)
Dynamic creation of Userform and Textbox with keypress event. Uses modified example of above link. Credit to original author.
Add reference - Under Available References, click "Microsoft Visual Basic for Applications Extensibility" and click OK.
Option Explicit
Sub MakeForm()
Dim TempForm As Object ' VBComponent
Dim FormName As String
Dim NewTextBox As MSForms.TextBox
Dim TextLocation As Integer
Dim TextBoxName As String
'** Additional variable
Dim X As Integer
'Locks Excel spreadsheet and speeds up form processing
Application.VBE.MainWindow.Visible = False
Application.ScreenUpdating = False
'Create the UserForm
Set TempForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
'Set Properties for TempForm
With TempForm
.Properties("Caption") = "Temporary Form"
.Properties("Width") = 200
.Properties("Height") = 100
End With
FormName = TempForm.Name
TextBoxName = "MyTextBox"
'Add a CommandButton
Set NewTextBox = TempForm.Designer.Controls _
.Add("Forms.TextBox.1")
With NewTextBox
.Name = TextBoxName
.Left = 60
.Top = 40
End With
'Add an event-hander sub for the CommandButton
With TempForm.CodeModule
'** Add/change next 5 lines
'This code adds the commands/event handlers to the form
X = .CountOfLines
.InsertLines X + 1, "Private Sub " & TextBoxName & "_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)"
.InsertLines X + 2, "KeyAscii = KeyPress(" & TextBoxName & ".Text, KeyAscii)"
.InsertLines X + 3, "End Sub"
End With
'Show the form
VBA.UserForms.Add(FormName).Show
'Delete the form
ThisWorkbook.VBProject.VBComponents.Remove VBComponent:=TempForm
End Sub
Public Function KeyPress(ByVal strText As String, ByVal KeyAscii As Integer) As Integer
Select Case KeyAscii
'// Numbers 0-9
Case 48 To 57
If Len(strText) = 3 And Right(strText, 3) Like "###" Then
GoTo DisplayFormatError
End If
'// Key ;
Case 59
If Len(strText) < 3 Or Not Right(strText, 3) Like "###" Then
GoTo DisplayFormatError
End If
Case Else
GoTo DisplayFormatError
End Select
KeyPress = KeyAscii
Exit Function
DisplayFormatError:
KeyPress = 0
MsgBox "Please enter serial number in the format '000;000;000'", vbInformation, "Alert!"
End Function
ANOTHER METHOD (Using an event handler class)
Code in Userform:
Private colEventHandlers As Collection
Private Sub UserForm_Initialize()
'// New collection of events
Set colEventHandlers = New Collection
'// Add dynamic textbox
Set tbxNewTextbox = Me.Controls.Add("Forms.TextBox.1", "MyNewTextbox", True)
With tbxNewTextbox
.Top = 25
.Left = 5
End With
'// Add the event handler
Dim objEventHandler As TextboxEventHandler
Set objEventHandler = New TextboxEventHandler
Set objEventHandler.TextBox = tbxNewTextbox
colEventHandlers.Add objEventHandler
End Sub
And add a class module and rename it too "TextBoxEventHandler", then add the following code:
Private WithEvents tbxWithEvent As MSForms.TextBox
Public Property Set TextBox(ByVal oTextBox As MSForms.TextBox)
Set tbxWithEvent = oTextBox
End Property
Private Sub tbxWithEvent_Change()
End Sub
Private Sub tbxWithEvent_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
'// Numbers 0-9
Case 48 To 57
If Len(tbxWithEvent.Text) = 3 And Right(tbxWithEvent.Text, 3) Like "###" Then
GoTo DisplayFormatError
End If
'// Key ;
Case 59
If Len(tbxWithEvent.Text) < 3 Or Not Right(tbxWithEvent.Text, 3) Like "###" Then
GoTo DisplayFormatError
End If
Case Else
GoTo DisplayFormatError
End Select
Exit Sub
DisplayFormatError:
KeyAscii = 0
MsgBox "Please enter serial number in the format '000;000;000'", vbInformation, "Alert!"
End Sub

Try Dataannotations / metadata
More here: http://msdn.microsoft.com/en-us/library/ee256141.aspx

AFAIK and if i understood well, there is no way to handle this before user input.
Yet, you can use the TextBox_Exit event to format it afterwards. You can adapt this sample of code.

Although I'd never use dynamic controls unless strictly required, I got puzzled by this question... so I'm thinking of it as a challenge. :-)
Googled around and most answers falls into the same solution, however most of them comes with a 'I couldn't make it work' comment as well, including this one here in SO Assign on-click VBA function to a dynamically created button on Excel Userform.
Here's the code I built... which obviously does not work, otherwise I'd say it could be a solution. The problem on it is that the keypress method it creates dynamically is not called when should be. To test it, just paste the code into a VBA form named 'myForm'.
I kept the TextBox1_KeyPress only for testing purposes, to prove the usability of the text field validator (I'm sorry #Readfidy, your code didn't work for me as expected. I was able to add more than 3 numbers in a row).
In case anyone else is interested in making this code works... I'd be happy to thank ;-)
Option Explicit
Private Sub UserForm_Activate()
Dim sTextBoxName As String
Dim cControl As MSForms.TextBox
Dim sMetaFunction As String
Dim CodeModule
sTextBoxName = "lreper"
Set cControl = myForm.Controls.Add("Forms.TextBox.1", sTextBoxName, True)
With cControl
.Top = 25
.Left = 5
End With
sMetaFunction = "Private Sub " & sTextBoxName & "_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)" & vbCrLf & _
vbCrLf & _
vbTab & "Set KeyAscii = EvaluateText(myForm.Controls(" & sTextBoxName & "), KeyAscii)" & vbCrLf & _
vbCrLf & _
"End Sub"
Set CodeModule = ActiveWorkbook.VBProject.VBComponents.VBE.ActiveCodePane.CodeModule
CodeModule.InsertLines 60, sMetaFunction
End Sub
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Set KeyAscii = EvaluateText(myForm.Controls("TextBox1"), KeyAscii)
End Sub
Private Function EvaluateText(ByRef oTextBox As MSForms.TextBox, ByVal KeyAscii As MSForms.ReturnInteger) As MSForms.ReturnInteger
If ((Len(oTextBox.Text) + 1) / 4 = CInt((Len(oTextBox.Text) + 1) / 4)) Then
If KeyAscii <> 59 Then KeyAscii = 0
Else
If KeyAscii < 48 Or KeyAscii > 57 Then KeyAscii = 0
End If
If KeyAscii = 0 Then
MsgBox "Please enter serial number in the format '000;000;000'", vbInformation, "Alert!"
End If
End Function

USE THIS CODE : NO CLASS NEEDED !
USERFORM CODE
Private Function QNumber(ByRef oTextBox As MSForms.TextBox, ByVal KeyAscii As MSForms.ReturnInteger) As MSForms.ReturnInteger
On Error Resume Next
Select Case KeyAscii
Case 45 '"-"
If InStr(1, oTextBox.Text, "-") > 0 Or oTextBox.SelStart > 0 Then
KeyAscii = 0
End If
Case 46 '"."
If InStr(1, oTextBox.Text, ".") > 0 Then
KeyAscii = 0
End If
Case 48 To 57 '0-9"
Case Else
KeyAscii = 0
End Select
End Function
TEXTBOX CODE
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Set KeyAscii = QNumber(Me.Controls("TextBox1"), KeyAscii)
End Sub

Related

Word Userform Bookmark

I got this code off of YouTube to be able to fill out a user form and it transfer it over to a Microsoft word doc but I am having issues with the bookmark section of the code, I am unsure if I have to declare which bookmark is going to start off with or if the way it has it currently just looks at all of the bookmarks in the documents but for some reason is just not working
My bookmarks are
Part_Num
QTY
Return_add
RMA_Num
RTV
stock
Option Explicit
Dim doc As Document
Private Sub CommandButton1_Click()
'// data validations
'// RMA check
If Me.RMA_Number.Text = vbNullString Then
MsgBox "RMA Number is required.", vbInformation
Me.RMA_Number.SetFocus
Exit Sub
'// Stock Code Check
ElseIf Me.Stock_Code.Text = vbNullString Then
MsgBox "Stock Codeis required.", vbInformation
Me.Stock_Code.SetFocus
Exit Sub
'// Part Number Check
ElseIf Me.Part_Number.Text = vbNullString Then
MsgBox "Part Number required.", vbInformation
Me.Part_Number.SetFocus
Exit Sub
'// RTV Check
ElseIf Me.RTV.Text = vbNullString Then
MsgBox "RTV Number required.", vbInformation
Me.RTV.SetFocus
Exit Sub
'// QTY
ElseIf Not IsNumeric(Me.QTY.Text) Then
MsgBox "QTY is invalid.", vbInformation
Me.QTY.SetFocus
Exit Sub
'// Return Address Check
ElseIf Me.Return_Address.Text = vbNullString Then
MsgBox "Return_Address required.", vbInformation
Me.Return_Address.SetFocus
Exit Sub
End If
'// RMA
Call update_field("RMA_Num", Me.RMA_Number.Text)
'// Stock code
Call update_field("Stock", Me.Stock_Code.Text)
'// Part Num
Call update_field("Part_Num", Me.Part_Number.Text)
'// QTY
Call update_field("QTY", Me.QTY.Text)
'// Return Address
Call update_field("Return_add", Me.Return_Address.Text)
'// RTV
Call update_field("RTV_Num", Me.RTV.Text)
End Sub
Private Sub update_field(ByVal field_name As String, ByVal field_value As String)
Dim bk_mark As Bookmark
Dim start_location As Long, extended_distance As Long
'// bookmark name
Set bk_mark = doc.Bookmarks(field_name)
'// select bookmark
bk_mark.Select
start_location = bk_mark.Start
extended_distance = Selection.EndKey
If extended_distance > 0 Then
doc.Range(start_location, start_location + extended_distance).Delete
End If
'// update bookmark value
bk_mark.Range = field_value
Set bk_mark = Nothing
End Sub
Private Sub CommandButton2_Click()
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "TextBox" Then
ctrl.Text = ""
End If
Next ctrl
End Sub
Private Sub UserForm_Terminate()
Set doc = Nothing
End Sub
I am having issues with this section of the code, i am not too sure why or if i need to declare a starting Bookmark but I am not too sure where, got the code from Youtube and modified it
Private Sub update_field(ByVal field_name As String, ByVal field_value As String)
Dim bk_mark As Bookmark
Dim start_location As Long, extended_distance As Long
'// bookmark name
Set bk_mark = doc.Bookmarks(field_name)
'// select bookmark
bk_mark.Select
start_location = bk_mark.Start
extended_distance = Selection.EndKey
If extended_distance > 0 Then
doc.Range(start_location, start_location + extended_distance).Delete
End If
'// update bookmark value
bk_mark.Range = field_value
Set bk_mark = Nothing
End Sub

Microsoft Access - Loop through all forms and controls on each form

Okay so when I press a specific button I want to loop through all forms, then find every control in each form with the tag 'TESTING'. If the tag = 'TESTING' then I want to change the caption of the object to 'abc123'.
The only objects with the tag 'TESTING' will be labels, so they will have the caption property.
So far I have this as the function:
Public Function changelabel()
On Error Resume Next
Dim obj As AccessObject, dbs As Object
Dim ctrl as Control
Set dbs = Application.CurrentProject
For Each obj In dbs.AllForms
DoCmd.OpenForm obj.Name, acDesign
For Each ctrl In Me.Controls
If ctrl.Tag = "TESTING" Then
ctrl.Caption = "abc123"
End If
Next ctrl
Next obj
End Function
Then this as the button code:
Public Sub TestButton_Click()
Call changelabel
End Sub
So it executes the first for loop and opens all the forms in design view correctly. The problem lies with the second for loop. None of the label captions that have the tag property as 'TESTING' are changed to 'abc123'.
So what do I need to change to get the second for loop to work?
Public Sub GetForms()
Dim oForm As Form
Dim nItem As Long
Dim bIsLoaded As Boolean
For nItem = 0 To CurrentProject.AllForms.Count - 1
bIsLoaded = CurrentProject.AllForms(nItem).IsLoaded
If Not bIsLoaded Then
On Error Resume Next
DoCmd.OpenForm CurrentProject.AllForms(nItem).NAME, acDesign
End If
Set oForm = Forms(CurrentProject.AllForms(nItem).NAME)
GetControls oForm
If Not bIsLoaded Then
On Error Resume Next
DoCmd.Close acForm, oForm.NAME
End If
Next
End Sub
Sub GetControls(ByVal oForm As Form)
Dim oCtrl As Control
Dim cCtrlType, cCtrlCaption As String
For Each oCtrl In oForm.Controls
If oCtrl.ControlType = acSubform Then Call GetControls(oCtrl.Form)
Select Case oCtrl.ControlType
Case acLabel: cCtrlType = "label": cCtrlCaption = oCtrl.Caption
Case acCommandButton: cCtrlType = "button": cCtrlCaption = oCtrl.Caption
Case acTextBox: cCtrlType = "textbox": cCtrlCaption = oCtrl.Properties("DataSheetCaption")
Case Else: cCtrlType = ""
End Select
If cCtrlType <> "" Then
Debug.Print oForm.NAME
Debug.Print oCtrl.NAME
Debug.Print cCtrlType
Debug.Print cCtrlCaption
End If
Next
End Sub
Something like this
Public Function changelabel()
Dim f As Form
Dim i As Integer
Dim c As Control
For i = 0 To CurrentProject.AllForms.Count - 1
If Not CurrentProject.AllForms(i).IsLoaded Then
DoCmd.OpenForm CurrentProject.AllForms(i).Name, acDesign
End If
Set f = Forms(i)
For Each c In f.Controls
If c.Tag = "TESTING" Then
c.Caption = "TESTING"
End If
Next c
Next i
End Function
You'll need to add a bit of house-keeping to set the objects used to nothing etc..

Email excel data range when target cell changes

This macro works on line 5 ,so i need this macro to work on all lines in one sheet instead of one macro for each line. Row X and email range A:L are copy paste in all lines i.e.( X1 A1:L1 | X2 ,A2:L2 ...)
Dim X5 As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("X5").Value = 1 And X5 <> 1 Then
ActiveSheet.Range("A5:L5").Select
ActiveWorkbook.EnvelopeVisible = True
With ActiveSheet.MailEnvelope
.Introduction = " send thru macro "
.Item.To = "email#gmail.com"
.Item.Subject = "ALERT"
.Item.Send
End With
End If
X5 = Range("X5").Value
End Sub
Not sure if you got your answer or not so I am attempting to answer this question.
To make it flexible for any row, you can store the row of the current cell in a variable using Target.Row and then simply use that to construct your range.
Also to understand how Worksheet_Change works, you may want to see THIS
Is this what you are trying?
Dim X5 As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
'~~> Check if the chnage happened to multiple cells
If Target.cell.CountLarge > 1 Then Exit Sub
Dim Rw As Long
'~~> Get the row number of the cell that was changed
Rw = Target.Row
If Range("X" & Rw).Value = 1 And X5 <> 1 Then
Application.EnableEvents = False
Range("A" & Rw & ":L" & Rw).Select
ActiveWorkbook.EnvelopeVisible = True
With ActiveSheet.MailEnvelope
.Introduction = " send thru macro "
.Item.To = "email#gmail.com"
.Item.Subject = "ALERT"
.Item.Send
End With
End If
X5 = Range("X" & Rw).Value
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub

Compare 2 listboxes and show nonmatching values in 3rd listbox

I've been working on this for 2 days. Basically I have 2 ListBoxes and I want a command button to compare the values and show the non-matching values (those that appear in the first listbox but not in the 2nd) and list them in the 3rd listbox. I'm not sure if this is the best way to go about it but here's my code. It errors on the line with the message:
Wrong number of arguments or invalid property assignment
My listboxes are named CompList1, CompList2 and CompList3.
Dim BoolAdd As Boolean, I As Long, j As Long
'Set initial Flag
BoolAdd = True
'If CompList2 is empty then abort operation
If CompList2.ListCount = 0 Then
MsgBox "Nothing to compare"
Exit Sub
'If CompList1 is empty then copy entire CompList2 to CompList3
ElseIf CompList1.ListCount = 0 Then
For I = 0 To CompList2.ListCount
CompList3.AddItem CompList2.Value
Next I
Else
For I = CompList2.ListCount - 1 To 0 Step -1
For j = 0 To CompList1.ListCount
If CompList2.ListCount(I) = CompList1.ListCount(j) Then
'If match found then abort
BoolAdd = False
Exit For
End If
DoEvents
Next j
'If not found then add to CompList3
If BoolAdd = True Then CompList3.AddItem CompList2.Value
DoEvents
Next I
End If
Some notes:
Dim tdf1 As TableDef
Dim tdf2 As TableDef
Dim db As Database
Set db = CurrentDb
Set tdf1 = db.TableDefs(Me.CompList1.RowSource)
For Each fld In tdf1.Fields
sFields = sFields & ";" & fld.Name
Next
sFields = sFields & ";"
Set tdf2 = db.TableDefs(Me.CompList2.RowSource)
For Each fld In tdf2.Fields
sf = ";" & fld.Name & ";"
sFields = Replace(sFields, sf, ";")
Next
Me.CompList3.RowSource = Mid(sFields,2)
Edit:

Read specific line from text file, according to Checked Listbox selection number

i want to create an application which will read a specific line from a text file and show it in a textbox. The line will be chosen according to the number of the listbox selection i will make.
Here's the code:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
For i = 0 To Me.CheckedListBox1.CheckedIndices.Count - 1
Me.CheckedListBox1.SetItemChecked(Me.CheckedListBox1.CheckedIndices(0),False)
Next i
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
If CheckedListBox1.CheckedItems.Count <> 0 Then
Dim reader As New System.IO.StreamReader(CurDir() & "\" & "READ.txt")
Dim x As Integer
Dim s As String = ""
For x = 0 To CheckedListBox1.CheckedItems.Count - 1
s = s & "Answer " & (x + 1).ToString & ") " & CheckedListBox1.CheckedItems(x).ToString & ControlChars.CrLf & reader.ReadLine() & ControlChars.CrLf & ControlChars.CrLf
Next x
Answer.Text = (s)
Else
MessageBox.Show("Please select questions.", "Error", _
MessageBoxButtons.OK, _
MessageBoxIcon.Information)
Return
End If
End Sub
End Class
So lets say i 'check' the first, second, and fifth items from the checked listbox, i want it to read from the text file the first, second, and fifth lines of text and show them in the textbox.
The current code just reads line 1, 2, 3 (...) in order, no matter what item i have 'checked'.
Increment a counter each time your read a line from the file to track which line you are reading and only add a line to the text box when your line number matches a selected line number.