Textbox Number Control

Textbox Number Control

Textbox Number Control

cNumControl - Textbox Number Control


Option Explicit

Private WithEvents txtNum As MSForms.TextBox
'Private m_oCollectionOfEventHandlers As Collection

Enum TxtDecimalType
    nbAuto = 0
    nbFixed = 1
End Enum

Enum TxtAlignmentConstants
    nbLeft = 1
    nbCenter = 2
    nbRight = 3
End Enum

Enum TxtFormat
    nbStandard = 0
    nbCurrency = 1
End Enum

Enum TxtNegativeChar
    nbMinus = 0
    nbParenthesis = 1
End Enum


Dim m_DecimalPlaces As Byte
Dim m_DecimalType As TxtDecimalType
Dim DecCnt As Byte 'save count of number after decimal

Dim m_Format As TxtFormat
Dim m_NegativeAllow As Boolean
Dim m_NegativeChar As TxtNegativeChar
Dim m_TextIfNothing As String
Dim m_ThousandSeparator As Boolean

'default value
Const m_def_DecimalPlaces = 2
Const m_def_DecimalType = 0
Const m_def_Format = 0
Const m_def_NegativeAllow = True
Const m_def_NegativeChar = 0
Const m_def_ThousandSeparator = True

Dim DCr As String 'save decimal character
Dim SCr As String 'save thousand separator character

'event declaration
Event Change()
Event Click()
Event DblClick()
Event KeyDown(ByVal KeyCode As Integer, Shift As Integer)
Event KeyPress(ByVal KeyAscii As Integer)
Event KeyUp(ByVal KeyCode As Integer, Shift As Integer)
Event MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Event MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Event MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

Public Property Set Init(ByRef controlObject As Object)

    If TypeOf controlObject Is MSForms.TextBox Then
        Set txtNum = controlObject
        'initialize default value
        m_DecimalPlaces = m_def_DecimalPlaces
        m_DecimalType = m_def_DecimalType
        m_Format = m_def_Format
        m_NegativeAllow = m_def_NegativeAllow
        m_NegativeChar = m_def_NegativeChar
        m_ThousandSeparator = m_def_ThousandSeparator
        txtNum.Text = "0"
        
        
    End If
End Property

Sub ReFormat()
    'txtNum.Refresh
    
    If txtNum.Text = "" Then
        txtNum.Text = m_TextIfNothing
    End If
    
    If txtNum.Text = "" Then Exit Sub
    
    On Error GoTo Ero
    Dim strTemp As String
    
    DCr = Mid(5 / 2, 2, 1)
    SCr = Mid(FormatNumber(1000, 0), 2, 1)
    
    txtNum.Text = Replace(txtNum.Text, SCr, "")
    
    If m_NegativeAllow = False Then txtNum.Text = Abs(CDbl(txtNum.Text))
    
    'count number after decimal (DecCnt)
    If m_DecimalType = 0 Then 'auto
        If InStr(txtNum.Text, DCr) Then 'if find decimal
            strTemp = Split(txtNum.Text, DCr)(1) 'get text on right of decimal
            strTemp = Replace(strTemp, ")", "") 'remove parensthesis
            DecCnt = Len(strTemp)
        Else
            DecCnt = 0
        End If
    Else 'fixed
        DecCnt = m_DecimalPlaces
    End If

    'format property
    Select Case m_Format
        Case 0 'standard
            If m_NegativeChar = 0 Then
                txtNum.Text = FormatNumber(txtNum.Text, DecCnt)
            Else
                txtNum.Text = FormatNumber(txtNum.Text, DecCnt, , vbTrue)
            End If
            
        Case 1 'currency
            If m_NegativeChar = 0 Then
                txtNum.Text = FormatCurrency(txtNum.Text, DecCnt, , vbFalse)
            Else
                txtNum.Text = FormatCurrency(txtNum.Text, DecCnt)
            End If
    End Select
    
    'if auto and find decimal
    If m_DecimalType = 0 And InStr(txtNum.Text, DCr) Then
        strTemp = Split(txtNum.Text, DCr)(1)
        txtNum.Text = Left(txtNum.Text, Len(txtNum.Text) - Len(strTemp)) 'remove strTemp from txtNum
        
        strTemp = Replace(strTemp, ")", "") 'remove parensthesis
        Do While Right(strTemp, 1) = "0"
            strTemp = Left(strTemp, Len(strTemp) - 1) 'clear zero
        Loop
        
        If strTemp = "" Then 'if nothing
            txtNum.Text = Replace(txtNum.Text, DCr, "") 'remove decimal
        Else
            txtNum.Text = txtNum.Text & strTemp 'join again
        End If
        
        If InStr(txtNum.Text, "(") Then txtNum.Text = txtNum.Text & ")" 'if find "("
        
    End If
    
        
    'ThousandSeparator property
    If m_ThousandSeparator = False Then
        txtNum.Text = Replace(txtNum.Text, SCr, "") 'remove separator
    End If
    
    Exit Sub
    
Ero:
    MsgBox Err.Description & " :" & vbCrLf & txtNum.Text, vbExclamation
    txtNum.SetFocus

End Sub


Private Sub txtNum_Change()
    RaiseEvent Change
End Sub

Private Sub txtNum_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
    RaiseEvent DblClick

End Sub

'Private Sub txtNum_Click()
'    RaiseEvent Click
'End Sub


Public Sub Control_GotFocus()
    On Error Resume Next
    'if currency
    If m_Format = 1 And txtNum.Text <> "" Then txtNum.Text = FormatNumber(txtNum.Text, DecCnt)

    'if find parensthesis
    If Left(txtNum.Text, 1) = "(" And Right(txtNum.Text, 1) = ")" Then
        txtNum.Text = Mid(txtNum.Text, 2, Len(txtNum.Text) - 2)
        txtNum.Text = "-" & txtNum.Text 'add minus
    End If

    SendKeys "{home}+{end}"

End Sub


Private Sub txtNum_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    If Shift = 1 And KeyCode = 45 Then 'paste (shift + insert)
        'If Not IsNumeric(Clipboard.GetText) Then Clipboard.Clear
    End If

    RaiseEvent KeyDown(KeyCode, Shift)
End Sub

Private Sub txtNum_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    Dim intPosition As Integer

    DCr = Mid(5 / 2, 2, 1)
    SCr = Mid(FormatNumber(1000, 0), 2, 1)
    intPosition = InStrRev(txtNum.Text, DCr)

    Select Case KeyAscii
        Case 48 To 58 '0 - 9
            If intPosition > 0 Then
                If txtNum.SelStart >= intPosition Then 'right of decimal
                    If m_DecimalType = 1 Then 'fixed
                        If (Len(txtNum.Text) - intPosition + 1) > m_DecimalPlaces Then KeyAscii = 0
                    End If
                End If
            End If

        Case 45 ' "-" negatif
            If m_NegativeAllow = False Or txtNum.SelStart <> 0 Then
                KeyAscii = 0
            End If

        Case 8, 13, 27 'backspace, enter, esc

        Case 24, 3 'cut, copy

        Case 22 'paste (ctrl + v)
'            If Not IsNumeric(Clipboard.GetText) Then Clipboard.Clear 'if not numeric

        Case Else
            If Chr(KeyAscii) = DCr Then 'if decimal

                'if find other decimal
                If InStr(txtNum.Text, DCr) Then KeyAscii = 0

                'if fixed and 0
                If m_DecimalType = 1 And m_DecimalPlaces = 0 Then KeyAscii = 0

            ElseIf Chr(KeyAscii) = SCr Then 'tho separator
                If intPosition > 0 Then
                    If txtNum.SelStart >= intPosition Then KeyAscii = 0 'right of decimal
                End If
                If m_ThousandSeparator = False Then KeyAscii = 0

            Else 'other character
                KeyAscii = 0
            End If
    End Select

    RaiseEvent KeyPress(KeyAscii)
End Sub

Public Property Get Alignment() As TxtAlignmentConstants
    Alignment = txtNum.TextAlign
End Property
Public Property Let Alignment(ByVal New_Alignment As TxtAlignmentConstants)
    txtNum.TextAlign = New_Alignment
    
End Property

Public Property Get BackColor() As OLE_COLOR
    BackColor = txtNum.BackColor
End Property
Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR)
    txtNum.BackColor = New_BackColor
    'Call UserControl_Resize
End Property

Public Property Get DecimalPlaces() As Byte
    DecimalPlaces = m_DecimalPlaces
End Property
Public Property Let DecimalPlaces(ByVal New_DecimalPlaces As Byte)
    If New_DecimalPlaces > 15 Then New_DecimalPlaces = 15
    
    m_DecimalPlaces = New_DecimalPlaces
    'PropertyChanged "DecimalPlaces"
    
    Call ReFormat

End Property

Public Property Get DecimalType() As TxtDecimalType
    DecimalType = m_DecimalType
End Property
Public Property Let DecimalType(ByVal New_DecimalType As TxtDecimalType)
    m_DecimalType = New_DecimalType
    'PropertyChanged "DecimalType"
    
   Call ReFormat

End Property

Public Property Get Enabled() As Boolean
    Enabled = txtNum.Enabled
End Property
Public Property Let Enabled(ByVal New_Enabled As Boolean)
    txtNum.Enabled = New_Enabled
    'PropertyChanged "Enabled"
End Property

Public Property Get Font() As Font
    Set Font = txtNum.Font
End Property
Public Property Set Font(ByVal New_Font As Font)
    Set txtNum.Font = New_Font
    'PropertyChanged "Font"
End Property

Public Property Get ForeColor() As OLE_COLOR
    ForeColor = txtNum.ForeColor
End Property
Public Property Let ForeColor(ByVal New_ForeColor As OLE_COLOR)
    txtNum.ForeColor = New_ForeColor
    'PropertyChanged "ForeColor"
End Property

Public Property Get Format() As TxtFormat
    Format = m_Format
End Property
Public Property Let Format(ByVal New_Format As TxtFormat)
    m_Format = New_Format
    'PropertyChanged "Format"
    
    Call ReFormat

End Property

Public Property Get Locked() As Boolean
    Locked = txtNum.Locked
End Property
Public Property Let Locked(ByVal New_Locked As Boolean)
    txtNum.Locked = New_Locked
    'PropertyChanged "Locked"
End Property

Public Property Get MousePointer() As MousePointerConstants
    MousePointer = txtNum.MousePointer
End Property
Public Property Let MousePointer(ByVal New_MousePointer As MousePointerConstants)
    txtNum.MousePointer = New_MousePointer
    'PropertyChanged "MousePointer"
End Property

Public Property Get NegativeAllow() As Boolean
    NegativeAllow = m_NegativeAllow
End Property

Public Property Let NegativeAllow(ByVal New_NegativeAllow As Boolean)
    If New_NegativeAllow = True Then GoTo Okk 'if true, jump to Okk
    
    'check Text property
    If txtNum.Text <> "" Then
        If CDbl(txtNum.Text) < 0 Then
            MsgBox "Invalid propety on [Text] !", vbExclamation, "Negative Allow Property"
            Exit Property
        End If
    End If
        
    'check TextIfNothing property
    If m_TextIfNothing <> "" Then
        If CDbl(m_TextIfNothing) < 0 Then
            MsgBox "Invalid propety on [TextIfNothing] !", vbExclamation, "Negative Allow Property"
            Exit Property
        End If
    End If
    
Okk:
    m_NegativeAllow = New_NegativeAllow
    'PropertyChanged "NegativeAllow"
    
End Property

Public Property Get NegativeChar() As TxtNegativeChar
    NegativeChar = m_NegativeChar
End Property
Public Property Let NegativeChar(ByVal New_NegativeChar As TxtNegativeChar)
    m_NegativeChar = New_NegativeChar
    'PropertyChanged "NegativeChar"
    
    Call ReFormat

End Property

Public Property Get Text() As String
    On Error GoTo Enn
'    If Ambient.UserMode = True Then
        Text = txtNum.Text
'    Else
'        Text = CDbl(txtNum.Text)
'    End If
    Exit Property
    
Enn:
    Text = ""
End Property
Public Property Let Text(ByVal New_Text As String)
    If New_Text = "" Then GoTo Okk 'if blank, jump to Okk
    
    'if not numeric
    If Not IsNumeric(New_Text) Then
        MsgBox "Please enter numbers only !", vbExclamation, "Text Property"
        Exit Property
    End If
        
    'if can't negative
    If m_NegativeAllow = False Then
        If CDbl(New_Text) < 0 Then
            MsgBox "The value of [Text] can't negative !", vbExclamation, "Text Property"
            Exit Property
        End If
    End If
    
        
Okk:
    txtNum.Text = New_Text
    'PropertyChanged "Text"
    
    Call ReFormat
End Property

Public Property Get TextIfNothing() As String
    TextIfNothing = m_TextIfNothing
End Property
Public Property Let TextIfNothing(ByVal New_TextIfNothing As String)
    If New_TextIfNothing = "" Then GoTo Okk
    
    'if not numeric
    If Not IsNumeric(New_TextIfNothing) Then
        MsgBox "Please enter numbers only !", vbExclamation, "Text If Nothing Property"
        Exit Property
    End If
        
    'if can't negative
    If m_NegativeAllow = False Then
        If CDbl(New_TextIfNothing) < 0 Then
            MsgBox "The value of [TextIfNothing] can't negative !", vbExclamation, "Text If Nothing Property"
            Exit Property
        End If
    End If
    
        
Okk:
    m_TextIfNothing = New_TextIfNothing
    'PropertyChanged "TextIfNothing"
    
    Call ReFormat

End Property

Public Property Get ThousandSeparator() As Boolean
    ThousandSeparator = m_ThousandSeparator
End Property
Public Property Let ThousandSeparator(ByVal New_ThousandSeparator As Boolean)
    m_ThousandSeparator = New_ThousandSeparator
    'PropertyChanged "ThousandSeparator"
    
    Call ReFormat

End Property

Public Property Get Value() As Double
    On Error GoTo Enn
    Value = CDbl(Replace(txtNum.Text, SCr, ""))
    Exit Property
Enn:
    Value = 0
End Property
Public Property Let Value(ByVal New_Value As Double)
    'if can't negative
    If m_NegativeAllow = False Then
        If CDbl(New_Value) < 0 Then
            MsgBox "The value can't negative !", vbExclamation, "Value Property"
            Exit Property
        End If
    End If

    txtNum.Text = New_Value
    'PropertyChanged "Value"
    
    Call ReFormat

End Property

Public Sub Control_LostFocus()
    Call ReFormat

End Sub

Private Sub txtNum_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    RaiseEvent KeyUp(KeyCode, Shift)

End Sub

Private Sub txtNum_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    'right click for cancel paste
    'If Button = 2 And IsNumeric(Clipboard.GetText) = False Then Clipboard.Clear

    RaiseEvent MouseDown(Button, Shift, X, Y)

End Sub

Private Sub txtNum_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    RaiseEvent MouseMove(Button, Shift, X, Y)

End Sub

Private Sub txtNum_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    RaiseEvent MouseUp(Button, Shift, X, Y)

End Sub

Private Sub Class_Initialize()
'    Set m_oCollectionOfEventHandlers = New Collection
    
End Sub


Share This