- 1.2k
- 7
- 9
Below is an example refactoring that creates two of the classes needed: DecimalDigitTextBox
and EmailTextBox
. A standard module AdvTextBoxFactory
is introduced to hold the common enums and constants. As the name implies, the module also acts as a Factory to create the specific of textbox validator class that is needed. And, most importantly, an IAdvTextBox
interface is defined so that each textbox class can look the same for client code. Code the interacts with the TextBox directly has been moved to a TextBoxHandler
class. Hope you find this useful.
Public Function HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
End Function
Public Function HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
End Function
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox)
End Sub
Public Sub ConfigureTypeSpecifics(value As TextBoxTypes)
End Sub
Public Function Validate() As Boolean
End Function
Public Property Get TextBoxType() As TextBoxTypes
End Property
Public Property Get MaxValue() As Double
End Property
Public Property Let MaxValue(ByVal value As Double)
End Property
Public Property Get MinValue() As Double
End Property
Public Property Let MinValue(ByVal value As Double)
End Property
Public Property Get FixedFormat() As Boolean
End Property
Public Property Let FixedFormat(ByVal value As Boolean)
End Property
Public Property Get ToCase() As DesiredCase
End Property
Public Property Let ToCase(ByVal value As DesiredCase)
End Property
Public Property Get InvalidValueMessage() As String
End Property
Public Property Let InvalidValueMessage(ByVal value As String)
End Property
Public Property Get IsValid() As Boolean
End Property
Public Property Let IsValid(ByVal value As Boolean)
End Property
Public Property Get ShowValidityThrough() As ValidityProperty
End Property
Public Property Let ShowValidityThrough(ByVal value As ValidityProperty)
End Property
Public Property Get ValidColor() As Long
End Property
Public Property Let ValidColor(ByVal value As Long)
End Property
Public Property Get InvalidColor() As Long
End Property
Public Property Let InvalidColor(ByVal value As Long)
End Property
Public Property Get Enlarged() As Boolean
End Property
Public Property Let Enlarged(ByVal value As Boolean)
End Property
Public Property Get AllowedCharacters() As String
End Property
Public Property Let AllowedCharacters(ByVal value As String)
End Property
Option Explicit
Public Type TTextBoxTAdvTextBox
TextBoxType As TextBoxTypes
MaxValue As Double
MinValue As Double
FixedFormat As Boolean
ToCase As DesiredCase
InvalidValueMessage As String
IsValid As Boolean
ShowValidityThrough As ValidityProperty
ValidColor As Long
InvalidColor As Long
AllowedCharacters As String
OutputFormatoutputFormat As String
DecimalSeparator As String
Enlarged As Boolean
End Type
Public Enum TextBoxTypes
ShortText = 0
Notes = 1
Iban = 10
ItalianVatNumber = 11
Email = 12
WholeNumber = 20
Decimal1Digit = 21
Decimal2Digit = 22
Decimal3Digit = 23
Decimal4Digit = 24
Decimal5Digit = 25
Decimal6Digit = 26
End Enum
Public Enum DesiredCase
Normal = 0
UpperCase = 1
LowerCase = 2
ProperCase = 3
End Enum
Public Enum ValidityProperty
NoOne = 0
vBorders = 1
vBackColor = 2
vForeColor = 3
End Enum
' constants
Public Const numbers As String = "0123456789"
Public Const letters As String = "abcdefghijklmnopqrstuvwxyz"
Public Const accented As String = "èéàòì"
Public Const numberPunctuation As String = ",."
Public Const otherPunctuation As String = " !?=_/|-@€+"
Public Const defaultInvalidColor As Long = &H5F5BDD
Public Function Create(ByVal obj As MSForms.TextBox, _
ByVal txtType As TextBoxTypes) As IAdvTextBox
Dim advTxtBox As IAdvTextBox
Select Case txtType
Case ShortText
'TODO
Case Notes
'TODO
Case Iban
'TODO
Case ItalianVatNumber
'TODO
Case Email
Dim emTxtBox As EmailTextBox
Set advTxtBoxemTxtBox = New EmailTextBox
emTxtBox.ConnectToTextBox obj
Set advTxtBox = emTxtBox
Case WholeNumber
'TODO
Case Decimal1Digit, Decimal2Digit, Decimal3Digit, Decimal4Digit, Decimal5Digit, Decimal6Digit
Dim ddTextBox As DecimalDigitTextBox
Set advTxtBoxddTextBox = New DecimalDigitTextBox
Case Else
ddTextBox.SetupDecimalDigits txtType
'throw an error ddTextBox.ConnectToTextBox obj
End Select Set advTxtBox = ddTextBox
Case Else
advTxtBox.ConfigureTypeSpecifics txtType 'throw an error
advTxtBox.ConnectToTextBoxEnd objSelect
Select Case advTxtBox.ShowValidityThrough
Case NoOne, vBackColor
advTxtBox.ValidColor = obj.BackColor
Case vBorders
advTxtBox.ValidColor = obj.BorderColor
Case vForeColor
advTxtBox.ValidColor = obj.ForeColor
End Select
advTxtBox.InvalidColor = defaultInvalidColor
Set Create = advTxtBox
End Function
Option Explicit
Private WithEvents txt As MSForms.TextBox
Implements IAdvTextBox
Private this As TTextBoxTAdvTextBox
Private txtBoxHandlerpDecimalDigitsDivisor As TextBoxHandlerLong
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
Set txtBoxHandlerpAllowEvents = New TextBoxHandlerTrue
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
'factory updates with correct values in SetupDecimalDigits
this.TextBoxType = Decimal1Digit 'factory sets correct valuethis.outputFormat using= IAdvTextBox_ConfigureTypeSpecifics"#,##0.0"
pDecimalDigitsDivisor = 10
End Sub
PrivatePublic FunctionSub IAdvTextBox_HandleNewTextValueConnectToTextBox(ByVal value As Variant, ByRef outSelectTexttxtBox As BooleanMSForms.TextBox) As Variant
outSelectTextSet txt = FalsetxtBox
Dim valore As Variant
this.AllowedCharacters = numbers valore& =IIf(this.FixedFormat, valuevbNullString, numberPunctuation)
valoretxt.ControlTipText = Replace(Replace(valore,"Press ",",""-"" vbNullString),to ".",change vbNullString)
the sign"
If valoretxt.text = vbNullString0
End ThenSub
Public valoreSub =SetupDecimalDigits(ByVal 0txtType As TextBoxTypes)
valorethis.TextBoxType = CDbl(valore)txtType
Select Case this.TextBoxTypetxtType
Case Decimal1Digit
valorethis.outputFormat = valore"#,##0.0"
/ pDecimalDigitsDivisor = 10
Case Decimal2Digit
valorethis.outputFormat = valore"#,##0.00"
/ pDecimalDigitsDivisor = 100
Case Decimal3Digit
valorethis.outputFormat = valore"#,##0.000"
/ pDecimalDigitsDivisor = 1000
Case Decimal4Digit
valorethis.outputFormat = valore"#,##0.0000"
/ pDecimalDigitsDivisor = 10000
Case Decimal5Digit
valorethis.outputFormat = valore"#,##0.00000"
/ pDecimalDigitsDivisor = 100000
Case Decimal6Digit
valorethis.outputFormat = valore"#,##0.000000"
/ pDecimalDigitsDivisor = 1000000
Case Else
'throw an error
End Select
IAdvTextBox_HandleNewTextValue = valore
End FunctionSub
Private FunctionSub IAdvTextBox_HandleKeyPresstxt_Change(ByVal)
KeyAscii
If Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim valore As MSFormsVariant
valore = Replace(Replace(txt.ReturnIntegertext, value",", AsvbNullString), String".", vbNullString) As String
IAdvTextBox_HandleKeyPressIf valore = valuevbNullString Then valore = 0
valore = CDbl(valore) / pDecimalDigitsDivisor
txt.text = CStr(valore)
pAllowEvents = True
End Sub
Private Sub txt_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If KeyAscii = 45 Then
IAdvTextBox_HandleKeyPresstxt.text = CDbl(valuetxt.text) * -1
End If
End Function
Private Sub IAdvTextBox_ConnectToTextBox(txtBox As MSForms.TextBox)If Not KeyAscii = 8 Then
txtBoxHandler If InStr(1, this.ConnectToTextBoxAllowedCharacters, txtBoxChr(KeyAscii), MevbTextCompare) = 0 Then KeyAscii = 0
End If
End Sub
Private Sub IAdvTextBox_ConfigureTypeSpecificsColorTextBox(valuevalidity As TextBoxTypesBoolean)
this.TextBoxType = value
Select Case value
Case Decimal1Digit
this.OutputFormat = "#,##0.0"
Case Decimal2Digit
this.OutputFormat = "#,##0.00"
Case Decimal3Digit
this.OutputFormat = "#,##0.000"
Case Decimal4Digit
this.OutputFormat = "#,##0.0000"
Case Decimal5Digit
this.OutputFormat = "#,##0.00000"
Case Decimal6Digit
If (Not this.OutputFormatShowValidityThrough = "#,##0.000000"
Case Else
'throw anNoOne) error
And (Not txt Is EndNothing) SelectThen
Dim color As Long
color = IIf(validity, this.AllowedCharactersValidColor, this.InvalidColor)
Select Case this.ShowValidityThrough
Case vBackColor
txt.BackColor = numberscolor
& Case vBorders
txt.BorderStyle = fmBorderStyleSingle
txt.BorderColor = color
txt.Width = txt.Width + IIf(this.FixedFormatEnlarged, vbNullString-0.1, numberPunctuation0.1)
txtBoxHandler.TextBox this.ControlTipTextEnlarged = "PressNot ""-""this.Enlarged
to change the sign" Case vForeColor
txtBoxHandler.TextBox txt.textForeColor = 0color
End Select
End If
End Sub
Private Function IAdvTextBox_Validate() As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = this.TextBoxType
End Property
Private Property Get IAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
this.MaxValue = value
End Property
Private Property Get IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
this.MinValue = value
End Property
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value As Boolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = this.ToCase
End Property
Private Property Let IAdvTextBox_ToCase(ByVal value As DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() As String
IAdvTextBox_InvalidValueMessage = this.InvalidValueMessage
End Property
Private Property Let IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IAdvTextBox_IsValid() As Boolean
IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
this.IsValid = value
txtBoxHandler.ColorTextBox this.IsValid, Me
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
IAdvTextBox_ShowValidityThrough = this.ShowValidityThrough
End Property
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
this.ShowValidityThrough = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_ValidColor() As Long
IAdvTextBox_ValidColor = this.ValidColor
End Property
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_InvalidColor() As Long
IAdvTextBox_InvalidColor = this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
this.InvalidColor = value
End Property
Private Property Get IAdvTextBox_Enlarged() As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End Property
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
this.Enlarged = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_AllowedCharacters() As String
IAdvTextBox_AllowedCharacters = this.AllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(ByVal value As String)
this.AllowedCharacters = value
End Property
Option Explicit
Implements IAdvTextBox
Private txtBoxHandlerWithEvents txt As TextBoxHandlerMSForms.TextBox
Private this As TTextBoxTAdvTextBox
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
Set txtBoxHandlerpAllowEvents = New TextBoxHandlerTrue
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Email
End Sub
Private Function IsValidEmail(ByVal emailAddress As String) As Boolean
IsValidEmail = False
Const emailPattern As String = "^([a-zA-Z0-9_\-\.]+)@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"
With CreateObject("VBScript.RegExp")
.Global = True
.IgnoreCase = True
.Pattern = emailPattern
IsValidEmail = .Test(emailAddress)
End With
End Function
PrivatePublic FunctionSub IAdvTextBox_HandleNewTextValueConnectToTextBox(ByVal valuetxtBox As Variant,MSForms.TextBox)
ByRef outSelectText As BooleanSet txt = txtBox
End Sub
Private Sub txt_Change() As Variant
outSelectTextIf Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim valore As Variant
valore = valuetxt.text
IAdvTextBox_IsValidthis.IsValid = IsValidEmail(valore)
valore = LCase(valore)
IAdvTextBox_HandleNewTextValuetxt.text = valore
End Function
Private Function IAdvTextBox_HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
pAllowEvents = True
'nothing to do
End FunctionSub
Private Sub IAdvTextBox_ConnectToTextBoxtxt_KeyPress(txtBoxByVal KeyAscii As MSForms.TextBoxReturnInteger)
txtBoxHandler.ConnectToTextBoxIf txtBox,Not Me
EndKeyAscii Sub
= 8 Then
Private Sub IAdvTextBox_ConfigureTypeSpecifics(value As TextBoxTypes)
If InStr(1, this.AllowedCharacters, =Chr(KeyAscii), numbersvbTextCompare) &= letters0 &Then numberPunctuationKeyAscii &= otherPunctuation0
txtBoxHandler.TextBox.text =End ""If
End Sub
Private FunctionSub IAdvTextBox_ValidateColorTextBox()validity As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me)
If (Not this.IsValidShowValidityThrough = NoOne) And (Not this.InvalidValueMessagetxt =Is vbNullStringNothing) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value" Dim color As Long
IAdvTextBox_Validate color = IIf(validity, this.IsValid
EndValidColor, Functionthis.InvalidColor)
'Other properties are the same as for DecimalDigitTextBox and are omitted here
TextBoxHandler
Option Explicit
Select Case this.ShowValidityThrough
Private WithEvents txt Case vBackColor
As MSForms.TextBox
Private theClient As IAdvTextBox
Private pAllowEventstxt.BackColor As= Boolean
color
Private Sub Class_Initialize()
pAllowEvents = True
End Sub
Case vBorders
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox, client As IAdvTextBox)
Set txt.BorderStyle = txtBoxfmBorderStyleSingle
Set theClient = client
End Sub
Public Property Get TextBox() As MSForms txt.TextBoxBorderColor = color
Set TextBox = txt
End Property
Public Sub ColorTextBox(validity As Boolean, tb As IAdvTextBox)
txt.Width = txt.Width If+ IIf(Not tbthis.ShowValidityThroughEnlarged, =-0.1, NoOne0.1) And (Not txt Is Nothing) Then
Select Case tbthis.ShowValidityThroughEnlarged = Not this.Enlarged
Case vBackColorvForeColor
txt.BackColorForeColor = IIf(validity,color
tb.ValidColor, tb.InvalidColor)
End Select
End CaseIf
End vBordersSub
Private Function IAdvTextBox_Validate() As Boolean
ColorTextBox this.IsValid
If (Not this.IsValid) txtAnd (Not this.BorderStyleInvalidValueMessage = fmBorderStyleSinglevbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = txtthis.BorderColorTextBoxType
End =Property
Private IIf(validity,Property tb.ValidColor,Get tb.InvalidColorIAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
txtthis.WidthMaxValue = txt.Widthvalue
End +Property
Private IIf(tb.Enlarged,Property -0.1,Get 0.1IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
tbthis.EnlargedMinValue = Notvalue
End tb.EnlargedProperty
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value CaseAs vForeColorBoolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = txtthis.ForeColorToCase
End =Property
Private IIfProperty Let IAdvTextBox_ToCase(validity,ByVal tb.ValidColor,value tb.InvalidColorAs DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() EndAs SelectString
EndIAdvTextBox_InvalidValueMessage If= this.InvalidValueMessage
End SubProperty
Private SubProperty txt_ChangeLet IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IfIAdvTextBox_IsValid() NotAs pAllowEventsBoolean
Then Exit Sub IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
pAllowEventsthis.IsValid = Falsevalue
ColorTextBox this.IsValid
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
DimIAdvTextBox_ShowValidityThrough byRefSelectText= Asthis.ShowValidityThrough
End BooleanProperty
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
txtthis.textShowValidityThrough = theClient.HandleNewTextValuevalue
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_ValidColor(txt) As Long
IAdvTextBox_ValidColor = this.text,ValidColor
End byRefSelectTextProperty
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
IfColorTextBox byRefSelectTextIAdvTextBox_IsValid
End ThenProperty
Private Property Get IAdvTextBox_InvalidColor() As Long
txt.SelStart IAdvTextBox_InvalidColor = 0this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
txt this.SelLengthInvalidColor = Len(CStrvalue
End Property
Private Property Get IAdvTextBox_Enlarged(txt.text)) As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End IfProperty
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
pAllowEventsthis.Enlarged = Truevalue
ColorTextBox IAdvTextBox_IsValid
End SubProperty
Private SubProperty txt_KeyPress(ByValGet KeyAsciiIAdvTextBox_AllowedCharacters() As MSForms.ReturnInteger)
String
txt.textIAdvTextBox_AllowedCharacters = theClientthis.HandleKeyPressAllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(KeyAscii,ByVal txt.textvalue As String)
this.AllowedCharacters = value
End SubProperty
Below is an example refactoring that creates two of the classes needed: DecimalDigitTextBox
and EmailTextBox
. A standard module AdvTextBoxFactory
is introduced to hold the common enums and constants. As the name implies, the module also acts as a Factory to create the specific of textbox validator class that is needed. And, most importantly, an IAdvTextBox
interface is defined so that each textbox class can look the same for client code. Code the interacts with the TextBox directly has been moved to a TextBoxHandler
class. Hope you find this useful.
Public Function HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
End Function
Public Function HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
End Function
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox)
End Sub
Public Sub ConfigureTypeSpecifics(value As TextBoxTypes)
End Sub
Public Function Validate() As Boolean
End Function
Public Property Get TextBoxType() As TextBoxTypes
End Property
Public Property Get MaxValue() As Double
End Property
Public Property Let MaxValue(ByVal value As Double)
End Property
Public Property Get MinValue() As Double
End Property
Public Property Let MinValue(ByVal value As Double)
End Property
Public Property Get FixedFormat() As Boolean
End Property
Public Property Let FixedFormat(ByVal value As Boolean)
End Property
Public Property Get ToCase() As DesiredCase
End Property
Public Property Let ToCase(ByVal value As DesiredCase)
End Property
Public Property Get InvalidValueMessage() As String
End Property
Public Property Let InvalidValueMessage(ByVal value As String)
End Property
Public Property Get IsValid() As Boolean
End Property
Public Property Let IsValid(ByVal value As Boolean)
End Property
Public Property Get ShowValidityThrough() As ValidityProperty
End Property
Public Property Let ShowValidityThrough(ByVal value As ValidityProperty)
End Property
Public Property Get ValidColor() As Long
End Property
Public Property Let ValidColor(ByVal value As Long)
End Property
Public Property Get InvalidColor() As Long
End Property
Public Property Let InvalidColor(ByVal value As Long)
End Property
Public Property Get Enlarged() As Boolean
End Property
Public Property Let Enlarged(ByVal value As Boolean)
End Property
Public Property Get AllowedCharacters() As String
End Property
Public Property Let AllowedCharacters(ByVal value As String)
End Property
Option Explicit
Public Type TTextBox
TextBoxType As TextBoxTypes
MaxValue As Double
MinValue As Double
FixedFormat As Boolean
ToCase As DesiredCase
InvalidValueMessage As String
IsValid As Boolean
ShowValidityThrough As ValidityProperty
ValidColor As Long
InvalidColor As Long
AllowedCharacters As String
OutputFormat As String
DecimalSeparator As String
Enlarged As Boolean
End Type
Public Enum TextBoxTypes
ShortText = 0
Notes = 1
Iban = 10
ItalianVatNumber = 11
Email = 12
WholeNumber = 20
Decimal1Digit = 21
Decimal2Digit = 22
Decimal3Digit = 23
Decimal4Digit = 24
Decimal5Digit = 25
Decimal6Digit = 26
End Enum
Public Enum DesiredCase
Normal = 0
UpperCase = 1
LowerCase = 2
ProperCase = 3
End Enum
Public Enum ValidityProperty
NoOne = 0
vBorders = 1
vBackColor = 2
vForeColor = 3
End Enum
' constants
Public Const numbers As String = "0123456789"
Public Const letters As String = "abcdefghijklmnopqrstuvwxyz"
Public Const accented As String = "èéàòì"
Public Const numberPunctuation As String = ",."
Public Const otherPunctuation As String = " !?=_/|-@€+"
Public Const defaultInvalidColor As Long = &H5F5BDD
Public Function Create(ByVal obj As MSForms.TextBox, _
ByVal txtType As TextBoxTypes) As IAdvTextBox
Dim advTxtBox As IAdvTextBox
Select Case txtType
Case ShortText
'TODO
Case Notes
'TODO
Case Iban
'TODO
Case ItalianVatNumber
'TODO
Case Email
Set advTxtBox = New EmailTextBox
Case WholeNumber
'TODO
Case Decimal1Digit, Decimal2Digit, Decimal3Digit, Decimal4Digit, Decimal5Digit, Decimal6Digit
Set advTxtBox = New DecimalDigitTextBox
Case Else
'throw an error
End Select
advTxtBox.ConfigureTypeSpecifics txtType
advTxtBox.ConnectToTextBox obj
Select Case advTxtBox.ShowValidityThrough
Case NoOne, vBackColor
advTxtBox.ValidColor = obj.BackColor
Case vBorders
advTxtBox.ValidColor = obj.BorderColor
Case vForeColor
advTxtBox.ValidColor = obj.ForeColor
End Select
advTxtBox.InvalidColor = defaultInvalidColor
Set Create = advTxtBox
End Function
Option Explicit
Implements IAdvTextBox
Private this As TTextBox
Private txtBoxHandler As TextBoxHandler
Private Sub Class_Initialize()
Set txtBoxHandler = New TextBoxHandler
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Decimal1Digit 'factory sets correct value using IAdvTextBox_ConfigureTypeSpecifics
End Sub
Private Function IAdvTextBox_HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
outSelectText = False
Dim valore As Variant
valore = value
valore = Replace(Replace(valore, ",", vbNullString), ".", vbNullString)
If valore = vbNullString Then valore = 0
valore = CDbl(valore)
Select Case this.TextBoxType
Case Decimal1Digit
valore = valore / 10
Case Decimal2Digit
valore = valore / 100
Case Decimal3Digit
valore = valore / 1000
Case Decimal4Digit
valore = valore / 10000
Case Decimal5Digit
valore = valore / 100000
Case Decimal6Digit
valore = valore / 1000000
End Select
IAdvTextBox_HandleNewTextValue = valore
End Function
Private Function IAdvTextBox_HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
IAdvTextBox_HandleKeyPress = value
If KeyAscii = 45 Then
IAdvTextBox_HandleKeyPress = CDbl(value) * -1
End If
End Function
Private Sub IAdvTextBox_ConnectToTextBox(txtBox As MSForms.TextBox)
txtBoxHandler.ConnectToTextBox txtBox, Me
End Sub
Private Sub IAdvTextBox_ConfigureTypeSpecifics(value As TextBoxTypes)
this.TextBoxType = value
Select Case value
Case Decimal1Digit
this.OutputFormat = "#,##0.0"
Case Decimal2Digit
this.OutputFormat = "#,##0.00"
Case Decimal3Digit
this.OutputFormat = "#,##0.000"
Case Decimal4Digit
this.OutputFormat = "#,##0.0000"
Case Decimal5Digit
this.OutputFormat = "#,##0.00000"
Case Decimal6Digit
this.OutputFormat = "#,##0.000000"
Case Else
'throw an error
End Select
this.AllowedCharacters = numbers & IIf(this.FixedFormat, vbNullString, numberPunctuation)
txtBoxHandler.TextBox.ControlTipText = "Press ""-"" to change the sign"
txtBoxHandler.TextBox.text = 0
End Sub
Private Function IAdvTextBox_Validate() As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = this.TextBoxType
End Property
Private Property Get IAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
this.MaxValue = value
End Property
Private Property Get IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
this.MinValue = value
End Property
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value As Boolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = this.ToCase
End Property
Private Property Let IAdvTextBox_ToCase(ByVal value As DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() As String
IAdvTextBox_InvalidValueMessage = this.InvalidValueMessage
End Property
Private Property Let IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IAdvTextBox_IsValid() As Boolean
IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
this.IsValid = value
txtBoxHandler.ColorTextBox this.IsValid, Me
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
IAdvTextBox_ShowValidityThrough = this.ShowValidityThrough
End Property
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
this.ShowValidityThrough = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_ValidColor() As Long
IAdvTextBox_ValidColor = this.ValidColor
End Property
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_InvalidColor() As Long
IAdvTextBox_InvalidColor = this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
this.InvalidColor = value
End Property
Private Property Get IAdvTextBox_Enlarged() As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End Property
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
this.Enlarged = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_AllowedCharacters() As String
IAdvTextBox_AllowedCharacters = this.AllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(ByVal value As String)
this.AllowedCharacters = value
End Property
Option Explicit
Implements IAdvTextBox
Private txtBoxHandler As TextBoxHandler
Private this As TTextBox
Private Sub Class_Initialize()
Set txtBoxHandler = New TextBoxHandler
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Email
End Sub
Private Function IsValidEmail(ByVal emailAddress As String) As Boolean
IsValidEmail = False
Const emailPattern As String = "^([a-zA-Z0-9_\-\.]+)@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"
With CreateObject("VBScript.RegExp")
.Global = True
.IgnoreCase = True
.Pattern = emailPattern
IsValidEmail = .Test(emailAddress)
End With
End Function
Private Function IAdvTextBox_HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
outSelectText = False
Dim valore As Variant
valore = value
IAdvTextBox_IsValid = IsValidEmail(valore)
valore = LCase(valore)
IAdvTextBox_HandleNewTextValue = valore
End Function
Private Function IAdvTextBox_HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
'nothing to do
End Function
Private Sub IAdvTextBox_ConnectToTextBox(txtBox As MSForms.TextBox)
txtBoxHandler.ConnectToTextBox txtBox, Me
End Sub
Private Sub IAdvTextBox_ConfigureTypeSpecifics(value As TextBoxTypes)
this.AllowedCharacters = numbers & letters & numberPunctuation & otherPunctuation
txtBoxHandler.TextBox.text = ""
End Sub
Private Function IAdvTextBox_Validate() As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
'Other properties are the same as for DecimalDigitTextBox and are omitted here
TextBoxHandler
Option Explicit
Private WithEvents txt As MSForms.TextBox
Private theClient As IAdvTextBox
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
pAllowEvents = True
End Sub
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox, client As IAdvTextBox)
Set txt = txtBox
Set theClient = client
End Sub
Public Property Get TextBox() As MSForms.TextBox
Set TextBox = txt
End Property
Public Sub ColorTextBox(validity As Boolean, tb As IAdvTextBox)
If (Not tb.ShowValidityThrough = NoOne) And (Not txt Is Nothing) Then
Select Case tb.ShowValidityThrough
Case vBackColor
txt.BackColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
Case vBorders
txt.BorderStyle = fmBorderStyleSingle
txt.BorderColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
txt.Width = txt.Width + IIf(tb.Enlarged, -0.1, 0.1)
tb.Enlarged = Not tb.Enlarged
Case vForeColor
txt.ForeColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
End Select
End If
End Sub
Private Sub txt_Change()
If Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim byRefSelectText As Boolean
txt.text = theClient.HandleNewTextValue(txt.text, byRefSelectText)
If byRefSelectText Then
txt.SelStart = 0
txt.SelLength = Len(CStr(txt.text))
End If
pAllowEvents = True
End Sub
Private Sub txt_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
txt.text = theClient.HandleKeyPress(KeyAscii, txt.text)
End Sub
Below is an example refactoring that creates two of the classes needed: DecimalDigitTextBox
and EmailTextBox
. A standard module AdvTextBoxFactory
is introduced to hold the common enums and constants. As the name implies, the module also acts as a Factory to create the specific of textbox validator class that is needed. And, most importantly, an IAdvTextBox
interface is defined so that each textbox class can look the same for client code. Hope you find this useful.
Public Function Validate() As Boolean
End Function
Public Property Get TextBoxType() As TextBoxTypes
End Property
Public Property Get MaxValue() As Double
End Property
Public Property Let MaxValue(ByVal value As Double)
End Property
Public Property Get MinValue() As Double
End Property
Public Property Let MinValue(ByVal value As Double)
End Property
Public Property Get FixedFormat() As Boolean
End Property
Public Property Let FixedFormat(ByVal value As Boolean)
End Property
Public Property Get ToCase() As DesiredCase
End Property
Public Property Let ToCase(ByVal value As DesiredCase)
End Property
Public Property Get InvalidValueMessage() As String
End Property
Public Property Let InvalidValueMessage(ByVal value As String)
End Property
Public Property Get IsValid() As Boolean
End Property
Public Property Let IsValid(ByVal value As Boolean)
End Property
Public Property Get ShowValidityThrough() As ValidityProperty
End Property
Public Property Let ShowValidityThrough(ByVal value As ValidityProperty)
End Property
Public Property Get ValidColor() As Long
End Property
Public Property Let ValidColor(ByVal value As Long)
End Property
Public Property Get InvalidColor() As Long
End Property
Public Property Let InvalidColor(ByVal value As Long)
End Property
Public Property Get Enlarged() As Boolean
End Property
Public Property Let Enlarged(ByVal value As Boolean)
End Property
Public Property Get AllowedCharacters() As String
End Property
Public Property Let AllowedCharacters(ByVal value As String)
End Property
Option Explicit
Public Type TAdvTextBox
TextBoxType As TextBoxTypes
MaxValue As Double
MinValue As Double
FixedFormat As Boolean
ToCase As DesiredCase
InvalidValueMessage As String
IsValid As Boolean
ShowValidityThrough As ValidityProperty
ValidColor As Long
InvalidColor As Long
AllowedCharacters As String
outputFormat As String
DecimalSeparator As String
Enlarged As Boolean
End Type
Public Enum TextBoxTypes
ShortText = 0
Notes = 1
Iban = 10
ItalianVatNumber = 11
Email = 12
WholeNumber = 20
Decimal1Digit = 21
Decimal2Digit = 22
Decimal3Digit = 23
Decimal4Digit = 24
Decimal5Digit = 25
Decimal6Digit = 26
End Enum
Public Enum DesiredCase
Normal = 0
UpperCase = 1
LowerCase = 2
ProperCase = 3
End Enum
Public Enum ValidityProperty
NoOne = 0
vBorders = 1
vBackColor = 2
vForeColor = 3
End Enum
' constants
Public Const numbers As String = "0123456789"
Public Const letters As String = "abcdefghijklmnopqrstuvwxyz"
Public Const accented As String = "èéàòì"
Public Const numberPunctuation As String = ",."
Public Const otherPunctuation As String = " !?=_/|-@€+"
Public Const defaultInvalidColor As Long = &H5F5BDD
Public Function Create(ByVal obj As MSForms.TextBox, _
ByVal txtType As TextBoxTypes) As IAdvTextBox
Dim advTxtBox As IAdvTextBox
Select Case txtType
Case ShortText
'TODO
Case Notes
'TODO
Case Iban
'TODO
Case ItalianVatNumber
'TODO
Case Email
Dim emTxtBox As EmailTextBox
Set emTxtBox = New EmailTextBox
emTxtBox.ConnectToTextBox obj
Set advTxtBox = emTxtBox
Case WholeNumber
'TODO
Case Decimal1Digit, Decimal2Digit, Decimal3Digit, Decimal4Digit, Decimal5Digit, Decimal6Digit
Dim ddTextBox As DecimalDigitTextBox
Set ddTextBox = New DecimalDigitTextBox
ddTextBox.SetupDecimalDigits txtType
ddTextBox.ConnectToTextBox obj
Set advTxtBox = ddTextBox
Case Else
'throw an error
End Select
Select Case advTxtBox.ShowValidityThrough
Case NoOne, vBackColor
advTxtBox.ValidColor = obj.BackColor
Case vBorders
advTxtBox.ValidColor = obj.BorderColor
Case vForeColor
advTxtBox.ValidColor = obj.ForeColor
End Select
advTxtBox.InvalidColor = defaultInvalidColor
Set Create = advTxtBox
End Function
Option Explicit
Private WithEvents txt As MSForms.TextBox
Implements IAdvTextBox
Private this As TAdvTextBox
Private pDecimalDigitsDivisor As Long
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
pAllowEvents = True
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
'factory updates with correct values in SetupDecimalDigits
this.TextBoxType = Decimal1Digit this.outputFormat = "#,##0.0"
pDecimalDigitsDivisor = 10
End Sub
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox)
Set txt = txtBox
this.AllowedCharacters = numbers & IIf(this.FixedFormat, vbNullString, numberPunctuation)
txt.ControlTipText = "Press ""-"" to change the sign"
txt.text = 0
End Sub
Public Sub SetupDecimalDigits(ByVal txtType As TextBoxTypes)
this.TextBoxType = txtType
Select Case txtType
Case Decimal1Digit
this.outputFormat = "#,##0.0"
pDecimalDigitsDivisor = 10
Case Decimal2Digit
this.outputFormat = "#,##0.00"
pDecimalDigitsDivisor = 100
Case Decimal3Digit
this.outputFormat = "#,##0.000"
pDecimalDigitsDivisor = 1000
Case Decimal4Digit
this.outputFormat = "#,##0.0000"
pDecimalDigitsDivisor = 10000
Case Decimal5Digit
this.outputFormat = "#,##0.00000"
pDecimalDigitsDivisor = 100000
Case Decimal6Digit
this.outputFormat = "#,##0.000000"
pDecimalDigitsDivisor = 1000000
Case Else
'throw an error
End Select
End Sub
Private Sub txt_Change()
If Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim valore As Variant
valore = Replace(Replace(txt.text, ",", vbNullString), ".", vbNullString)
If valore = vbNullString Then valore = 0
valore = CDbl(valore) / pDecimalDigitsDivisor
txt.text = CStr(valore)
pAllowEvents = True
End Sub
Private Sub txt_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If KeyAscii = 45 Then
txt.text = CDbl(txt.text) * -1
End If
If Not KeyAscii = 8 Then
If InStr(1, this.AllowedCharacters, Chr(KeyAscii), vbTextCompare) = 0 Then KeyAscii = 0
End If
End Sub
Private Sub ColorTextBox(validity As Boolean)
If (Not this.ShowValidityThrough = NoOne) And (Not txt Is Nothing) Then
Dim color As Long
color = IIf(validity, this.ValidColor, this.InvalidColor)
Select Case this.ShowValidityThrough
Case vBackColor
txt.BackColor = color
Case vBorders
txt.BorderStyle = fmBorderStyleSingle
txt.BorderColor = color
txt.Width = txt.Width + IIf(this.Enlarged, -0.1, 0.1)
this.Enlarged = Not this.Enlarged
Case vForeColor
txt.ForeColor = color
End Select
End If
End Sub
Private Function IAdvTextBox_Validate() As Boolean
ColorTextBox this.IsValid
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = this.TextBoxType
End Property
Private Property Get IAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
this.MaxValue = value
End Property
Private Property Get IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
this.MinValue = value
End Property
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value As Boolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = this.ToCase
End Property
Private Property Let IAdvTextBox_ToCase(ByVal value As DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() As String
IAdvTextBox_InvalidValueMessage = this.InvalidValueMessage
End Property
Private Property Let IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IAdvTextBox_IsValid() As Boolean
IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
this.IsValid = value
ColorTextBox this.IsValid
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
IAdvTextBox_ShowValidityThrough = this.ShowValidityThrough
End Property
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
this.ShowValidityThrough = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_ValidColor() As Long
IAdvTextBox_ValidColor = this.ValidColor
End Property
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_InvalidColor() As Long
IAdvTextBox_InvalidColor = this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
this.InvalidColor = value
End Property
Private Property Get IAdvTextBox_Enlarged() As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End Property
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
this.Enlarged = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_AllowedCharacters() As String
IAdvTextBox_AllowedCharacters = this.AllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(ByVal value As String)
this.AllowedCharacters = value
End Property
Option Explicit
Implements IAdvTextBox
Private WithEvents txt As MSForms.TextBox
Private this As TAdvTextBox
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
pAllowEvents = True
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Email
End Sub
Private Function IsValidEmail(ByVal emailAddress As String) As Boolean
IsValidEmail = False
Const emailPattern As String = "^([a-zA-Z0-9_\-\.]+)@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"
With CreateObject("VBScript.RegExp")
.Global = True
.IgnoreCase = True
.Pattern = emailPattern
IsValidEmail = .Test(emailAddress)
End With
End Function
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox)
Set txt = txtBox
End Sub
Private Sub txt_Change()
If Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim valore As Variant
valore = txt.text
this.IsValid = IsValidEmail(valore)
valore = LCase(valore)
txt.text = valore
pAllowEvents = True
End Sub
Private Sub txt_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If Not KeyAscii = 8 Then
If InStr(1, this.AllowedCharacters, Chr(KeyAscii), vbTextCompare) = 0 Then KeyAscii = 0
End If
End Sub
Private Sub ColorTextBox(validity As Boolean)
If (Not this.ShowValidityThrough = NoOne) And (Not txt Is Nothing) Then Dim color As Long
color = IIf(validity, this.ValidColor, this.InvalidColor)
Select Case this.ShowValidityThrough
Case vBackColor
txt.BackColor = color
Case vBorders
txt.BorderStyle = fmBorderStyleSingle
txt.BorderColor = color
txt.Width = txt.Width + IIf(this.Enlarged, -0.1, 0.1) this.Enlarged = Not this.Enlarged
Case vForeColor
txt.ForeColor = color
End Select
End If
End Sub
Private Function IAdvTextBox_Validate() As Boolean
ColorTextBox this.IsValid
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = this.TextBoxType
End Property
Private Property Get IAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
this.MaxValue = value
End Property
Private Property Get IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
this.MinValue = value
End Property
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value As Boolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = this.ToCase
End Property
Private Property Let IAdvTextBox_ToCase(ByVal value As DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() As String
IAdvTextBox_InvalidValueMessage = this.InvalidValueMessage
End Property
Private Property Let IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IAdvTextBox_IsValid() As Boolean
IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
this.IsValid = value
ColorTextBox this.IsValid
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
IAdvTextBox_ShowValidityThrough = this.ShowValidityThrough
End Property
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
this.ShowValidityThrough = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_ValidColor() As Long
IAdvTextBox_ValidColor = this.ValidColor
End Property
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_InvalidColor() As Long
IAdvTextBox_InvalidColor = this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
this.InvalidColor = value
End Property
Private Property Get IAdvTextBox_Enlarged() As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End Property
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
this.Enlarged = value
ColorTextBox IAdvTextBox_IsValid
End Property
Private Property Get IAdvTextBox_AllowedCharacters() As String
IAdvTextBox_AllowedCharacters = this.AllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(ByVal value As String)
this.AllowedCharacters = value
End Property
The one class is trying to handle multiple types of textboxes. In this specific case it would seem a better approach to create a class for each textbox type. This keeps code specific to each textbox type very isolated, focused, and much more OO. So, how to get there...
Below is an example refactoring that creates two of the classes needed: DecimalDigitTextBox
and EmailTextBox
. A standard module AdvTextBoxFactory
is introduced to hold the common enums and constants. As the name implies, the module also acts as a Factory to create the specific of textbox validator class that is needed. And, most importantly, an IAdvTextBox
interface is defined so that each textbox class can look the same for client code. Code the interacts with the TextBox directly has been moved to a TextBoxHandler
class. Hope you find this useful.
IAdvTextBox
Public Function HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
End Function
Public Function HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
End Function
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox)
End Sub
Public Sub ConfigureTypeSpecifics(value As TextBoxTypes)
End Sub
Public Function Validate() As Boolean
End Function
Public Property Get TextBoxType() As TextBoxTypes
End Property
Public Property Get MaxValue() As Double
End Property
Public Property Let MaxValue(ByVal value As Double)
End Property
Public Property Get MinValue() As Double
End Property
Public Property Let MinValue(ByVal value As Double)
End Property
Public Property Get FixedFormat() As Boolean
End Property
Public Property Let FixedFormat(ByVal value As Boolean)
End Property
Public Property Get ToCase() As DesiredCase
End Property
Public Property Let ToCase(ByVal value As DesiredCase)
End Property
Public Property Get InvalidValueMessage() As String
End Property
Public Property Let InvalidValueMessage(ByVal value As String)
End Property
Public Property Get IsValid() As Boolean
End Property
Public Property Let IsValid(ByVal value As Boolean)
End Property
Public Property Get ShowValidityThrough() As ValidityProperty
End Property
Public Property Let ShowValidityThrough(ByVal value As ValidityProperty)
End Property
Public Property Get ValidColor() As Long
End Property
Public Property Let ValidColor(ByVal value As Long)
End Property
Public Property Get InvalidColor() As Long
End Property
Public Property Let InvalidColor(ByVal value As Long)
End Property
Public Property Get Enlarged() As Boolean
End Property
Public Property Let Enlarged(ByVal value As Boolean)
End Property
Public Property Get AllowedCharacters() As String
End Property
Public Property Let AllowedCharacters(ByVal value As String)
End Property
AdvTextBoxFactory
Option Explicit
Public Type TTextBox
TextBoxType As TextBoxTypes
MaxValue As Double
MinValue As Double
FixedFormat As Boolean
ToCase As DesiredCase
InvalidValueMessage As String
IsValid As Boolean
ShowValidityThrough As ValidityProperty
ValidColor As Long
InvalidColor As Long
AllowedCharacters As String
OutputFormat As String
DecimalSeparator As String
Enlarged As Boolean
End Type
Public Enum TextBoxTypes
ShortText = 0
Notes = 1
Iban = 10
ItalianVatNumber = 11
Email = 12
WholeNumber = 20
Decimal1Digit = 21
Decimal2Digit = 22
Decimal3Digit = 23
Decimal4Digit = 24
Decimal5Digit = 25
Decimal6Digit = 26
End Enum
Public Enum DesiredCase
Normal = 0
UpperCase = 1
LowerCase = 2
ProperCase = 3
End Enum
Public Enum ValidityProperty
NoOne = 0
vBorders = 1
vBackColor = 2
vForeColor = 3
End Enum
' constants
Public Const numbers As String = "0123456789"
Public Const letters As String = "abcdefghijklmnopqrstuvwxyz"
Public Const accented As String = "èéàòì"
Public Const numberPunctuation As String = ",."
Public Const otherPunctuation As String = " !?=_/|-@€+"
Public Const defaultInvalidColor As Long = &H5F5BDD
Public Function Create(ByVal obj As MSForms.TextBox, _
ByVal txtType As TextBoxTypes) As IAdvTextBox
Dim advTxtBox As IAdvTextBox
Select Case txtType
Case ShortText
'TODO
Case Notes
'TODO
Case Iban
'TODO
Case ItalianVatNumber
'TODO
Case Email
Set advTxtBox = New EmailTextBox
Case WholeNumber
'TODO
Case Decimal1Digit, Decimal2Digit, Decimal3Digit, Decimal4Digit, Decimal5Digit, Decimal6Digit
Set advTxtBox = New DecimalDigitTextBox
Case Else
'throw an error
End Select
advTxtBox.ConfigureTypeSpecifics txtType
advTxtBox.ConnectToTextBox obj
Select Case advTxtBox.ShowValidityThrough
Case NoOne, vBackColor
advTxtBox.ValidColor = obj.BackColor
Case vBorders
advTxtBox.ValidColor = obj.BorderColor
Case vForeColor
advTxtBox.ValidColor = obj.ForeColor
End Select
advTxtBox.InvalidColor = defaultInvalidColor
Set Create = advTxtBox
End Function
DecimalDigitTextBox
Option Explicit
Implements IAdvTextBox
Private this As TTextBox
Private txtBoxHandler As TextBoxHandler
Private Sub Class_Initialize()
Set txtBoxHandler = New TextBoxHandler
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Decimal1Digit 'factory sets correct value using IAdvTextBox_ConfigureTypeSpecifics
End Sub
Private Function IAdvTextBox_HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
outSelectText = False
Dim valore As Variant
valore = value
valore = Replace(Replace(valore, ",", vbNullString), ".", vbNullString)
If valore = vbNullString Then valore = 0
valore = CDbl(valore)
Select Case this.TextBoxType
Case Decimal1Digit
valore = valore / 10
Case Decimal2Digit
valore = valore / 100
Case Decimal3Digit
valore = valore / 1000
Case Decimal4Digit
valore = valore / 10000
Case Decimal5Digit
valore = valore / 100000
Case Decimal6Digit
valore = valore / 1000000
End Select
IAdvTextBox_HandleNewTextValue = valore
End Function
Private Function IAdvTextBox_HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
IAdvTextBox_HandleKeyPress = value
If KeyAscii = 45 Then
IAdvTextBox_HandleKeyPress = CDbl(value) * -1
End If
End Function
Private Sub IAdvTextBox_ConnectToTextBox(txtBox As MSForms.TextBox)
txtBoxHandler.ConnectToTextBox txtBox, Me
End Sub
Private Sub IAdvTextBox_ConfigureTypeSpecifics(value As TextBoxTypes)
this.TextBoxType = value
Select Case value
Case Decimal1Digit
this.OutputFormat = "#,##0.0"
Case Decimal2Digit
this.OutputFormat = "#,##0.00"
Case Decimal3Digit
this.OutputFormat = "#,##0.000"
Case Decimal4Digit
this.OutputFormat = "#,##0.0000"
Case Decimal5Digit
this.OutputFormat = "#,##0.00000"
Case Decimal6Digit
this.OutputFormat = "#,##0.000000"
Case Else
'throw an error
End Select
this.AllowedCharacters = numbers & IIf(this.FixedFormat, vbNullString, numberPunctuation)
txtBoxHandler.TextBox.ControlTipText = "Press ""-"" to change the sign"
txtBoxHandler.TextBox.text = 0
End Sub
Private Function IAdvTextBox_Validate() As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
Private Property Get IAdvTextBox_TextBoxType() As TextBoxTypes
IAdvTextBox_TextBoxType = this.TextBoxType
End Property
Private Property Get IAdvTextBox_MaxValue() As Double
IAdvTextBox_MaxValue = this.MaxValue
End Property
Private Property Let IAdvTextBox_MaxValue(ByVal value As Double)
this.MaxValue = value
End Property
Private Property Get IAdvTextBox_MinValue() As Double
IAdvTextBox_MinValue = this.MinValue
End Property
Private Property Let IAdvTextBox_MinValue(ByVal value As Double)
this.MinValue = value
End Property
Private Property Get IAdvTextBox_FixedFormat() As Boolean
IAdvTextBox_FixedFormat = this.FixedFormat
End Property
Private Property Let IAdvTextBox_FixedFormat(ByVal value As Boolean)
this.FixedFormat = value
End Property
Private Property Get IAdvTextBox_ToCase() As DesiredCase
IAdvTextBox_ToCase = this.ToCase
End Property
Private Property Let IAdvTextBox_ToCase(ByVal value As DesiredCase)
this.ToCase = value
End Property
Private Property Get IAdvTextBox_InvalidValueMessage() As String
IAdvTextBox_InvalidValueMessage = this.InvalidValueMessage
End Property
Private Property Let IAdvTextBox_InvalidValueMessage(ByVal value As String)
this.InvalidValueMessage = value
End Property
Private Property Get IAdvTextBox_IsValid() As Boolean
IAdvTextBox_IsValid = this.IsValid
End Property
Private Property Let IAdvTextBox_IsValid(ByVal value As Boolean)
this.IsValid = value
txtBoxHandler.ColorTextBox this.IsValid, Me
End Property
Private Property Get IAdvTextBox_ShowValidityThrough() As ValidityProperty
IAdvTextBox_ShowValidityThrough = this.ShowValidityThrough
End Property
Private Property Let IAdvTextBox_ShowValidityThrough(ByVal value As ValidityProperty)
this.ShowValidityThrough = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_ValidColor() As Long
IAdvTextBox_ValidColor = this.ValidColor
End Property
Private Property Let IAdvTextBox_ValidColor(ByVal value As Long)
this.ValidColor = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_InvalidColor() As Long
IAdvTextBox_InvalidColor = this.InvalidColor
End Property
Private Property Let IAdvTextBox_InvalidColor(ByVal value As Long)
this.InvalidColor = value
End Property
Private Property Get IAdvTextBox_Enlarged() As Boolean
IAdvTextBox_Enlarged = this.Enlarged
End Property
Private Property Let IAdvTextBox_Enlarged(ByVal value As Boolean)
this.Enlarged = value
txtBoxHandler.ColorTextBox IAdvTextBox_IsValid, Me
End Property
Private Property Get IAdvTextBox_AllowedCharacters() As String
IAdvTextBox_AllowedCharacters = this.AllowedCharacters
End Property
Private Property Let IAdvTextBox_AllowedCharacters(ByVal value As String)
this.AllowedCharacters = value
End Property
EmailTextBox
Option Explicit
Implements IAdvTextBox
Private txtBoxHandler As TextBoxHandler
Private this As TTextBox
Private Sub Class_Initialize()
Set txtBoxHandler = New TextBoxHandler
this.DecimalSeparator = Application.DecimalSeparator
this.FixedFormat = True
this.ShowValidityThrough = NoOne
this.ToCase = Normal
this.ValidColor = -1
this.InvalidColor = -1
this.TextBoxType = Email
End Sub
Private Function IsValidEmail(ByVal emailAddress As String) As Boolean
IsValidEmail = False
Const emailPattern As String = "^([a-zA-Z0-9_\-\.]+)@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"
With CreateObject("VBScript.RegExp")
.Global = True
.IgnoreCase = True
.Pattern = emailPattern
IsValidEmail = .Test(emailAddress)
End With
End Function
Private Function IAdvTextBox_HandleNewTextValue(ByVal value As Variant, ByRef outSelectText As Boolean) As Variant
outSelectText = False
Dim valore As Variant
valore = value
IAdvTextBox_IsValid = IsValidEmail(valore)
valore = LCase(valore)
IAdvTextBox_HandleNewTextValue = valore
End Function
Private Function IAdvTextBox_HandleKeyPress(ByVal KeyAscii As MSForms.ReturnInteger, value As String) As String
'nothing to do
End Function
Private Sub IAdvTextBox_ConnectToTextBox(txtBox As MSForms.TextBox)
txtBoxHandler.ConnectToTextBox txtBox, Me
End Sub
Private Sub IAdvTextBox_ConfigureTypeSpecifics(value As TextBoxTypes)
this.AllowedCharacters = numbers & letters & numberPunctuation & otherPunctuation
txtBoxHandler.TextBox.text = ""
End Sub
Private Function IAdvTextBox_Validate() As Boolean
txtBoxHandler.ColorTextBox this.IsValid, Me
If (Not this.IsValid) And (Not this.InvalidValueMessage = vbNullString) Then MsgBox this.InvalidValueMessage, vbInformation, "Invalid value"
IAdvTextBox_Validate = this.IsValid
End Function
'Other properties are the same as for DecimalDigitTextBox and are omitted here
TextBoxHandler
Option Explicit
Private WithEvents txt As MSForms.TextBox
Private theClient As IAdvTextBox
Private pAllowEvents As Boolean
Private Sub Class_Initialize()
pAllowEvents = True
End Sub
Public Sub ConnectToTextBox(txtBox As MSForms.TextBox, client As IAdvTextBox)
Set txt = txtBox
Set theClient = client
End Sub
Public Property Get TextBox() As MSForms.TextBox
Set TextBox = txt
End Property
Public Sub ColorTextBox(validity As Boolean, tb As IAdvTextBox)
If (Not tb.ShowValidityThrough = NoOne) And (Not txt Is Nothing) Then
Select Case tb.ShowValidityThrough
Case vBackColor
txt.BackColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
Case vBorders
txt.BorderStyle = fmBorderStyleSingle
txt.BorderColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
txt.Width = txt.Width + IIf(tb.Enlarged, -0.1, 0.1)
tb.Enlarged = Not tb.Enlarged
Case vForeColor
txt.ForeColor = IIf(validity, tb.ValidColor, tb.InvalidColor)
End Select
End If
End Sub
Private Sub txt_Change()
If Not pAllowEvents Then Exit Sub
pAllowEvents = False
Dim byRefSelectText As Boolean
txt.text = theClient.HandleNewTextValue(txt.text, byRefSelectText)
If byRefSelectText Then
txt.SelStart = 0
txt.SelLength = Len(CStr(txt.text))
End If
pAllowEvents = True
End Sub
Private Sub txt_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
txt.text = theClient.HandleKeyPress(KeyAscii, txt.text)
End Sub