65.9K
CodeProject 正在变化。 阅读更多。
Home

高级文本框

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (5投票s)

2014年5月6日

CPOL

5分钟阅读

viewsIcon

32090

downloadIcon

3662

继承的文本框,具有扩展属性和验证控件。

引言

我知道在此期间互联网上将会有几个增强的文本框控件可用。但开发我的应用程序时,我需要一些功能,这些功能只分布在几个自定义控件中,而不是集中在一个控件中。因此,这个控件被开发出来了。也许它也能满足您的需求。

背景

此控件具有以下功能:

  • 标记,在控件的某个边缘放置一个小彩色三角形
  • 标记位置,设置边缘位置
  • 上下文菜单,验证控制的剪贴板操作剪切、复制、粘贴、删除
  • 输入类型,无验证,验证输入:整数小数

    如果已验证
  • 小数位数,允许的小数位数
  • 验证/设置输入值范围最大/最小
  • 允许/拒绝负数
  • 蜂鸣声,如果验证错误则发出系统蜂鸣声
  • 闪烁,如果验证错误则闪烁背景色 3 次
  • 菜单项,上下文菜单项的文本字符串

Notice:

  • 输入的验证仅针对按键或从剪贴板粘贴的值进行。如果值来自数据源列的绑定,则不会进行验证
  • 如果将输入类型设置为Decimal且小数位数为零,则输入与设置为Integer时相同。
  • 如果从剪贴板粘贴值,并且输入类型设置为IntegerDecimal。粘贴的文本将作为正确的数字值填充。对于粘贴的小数,所有小数位数将四舍五入到正确的小数位数。对于粘贴的小数和设置了Integer验证,小数将被四舍五入为整数。
  • 源代码项目已通过恶意软件和防病毒软件进行了线程检查。未报告线程。

使用代码

第一个 ZIP 文件包含一个包含两个子项目的项目。控件项目和一个 Windows 窗体测试项目。项目使用 VS2012 .NET 4.5 的 VB.NET 开发。第二个 ZIP 文件仅包含控件 DLL 文件。

使用项目解压缩文件,然后用 Visual Studio (VS) 打开它。
使用控件将控件 DLL 保存到控件项目 bin/Debug 文件夹的所需位置,然后像往常一样将其实现到 VS 工具箱中。在设计模式下将控件从工具箱拖到 Windows 窗体上。

控件项目包含三个类。一个用于控件本身(主类clsAdvTexbox.vb),一个用于使用的上下文菜单(clsContextMenu.vb),第三个是辅助类(clsToolBoxIcon.vb)。

我们需要一个自定义上下文菜单,以便能够验证从剪贴板粘贴的文本是否有效。

为了在属性网格中拥有标准文本框控件的所有属性,该控件不是从 UserControl 构建的。而是通过继承构建的。

Public Class advancedTexbox
    Inherits TextBox

所有自定义属性 都经过良好文档记录,如下所示(请记住不要忘记imports System.ComponentModel

 <Browsable(True), DisplayName("Triangle Position"), _
 Description("Choose Triangle Position"), _
 Category("custom Properties")> _
 Public Property TriPosition As _TriPos
.... 

Browsable,在属性网格中显示/隐藏属性。
DisplayName,在属性网格中显示此文本以标识属性。
Description,在属性网格页脚中显示此文本。
Category,如果启用了属性网格分组,则对该属性进行分组。

Visual Studio 工具箱错误 (clsTexBoxIcon)

从 Visual Studio 的第一个版本开始,开发系统就无法在工具箱中正确显示自定义控件的工具箱图标。如果您查看 VS 帮助,会告诉您按照以下方式增强类语句

<Toolboxitem("name.bmp"> _
class  form1

但在大多数情况下,这不会起作用。但是,可以通过将类“clsToolBoxIcon.vb”添加到您的项目中来解决此问题。

<Serializable()> Friend Class TheToolboxItem
    Inherits ToolboxItem 
    ' ## change the parameter of the GetType Statement to the name of the usercontrol class 
    Public Sub New(ByVal oType As Type) 
         MyBase.New(oType) 
    End Sub 
    Public Overrides Sub Initialize(ByVal oType As Type) 
        If Not oType.Equals(GetType(advancedTexbox)) Then
              String.Format(CultureInfo.CurrentCulture, _
               "constructor {0} must be {1}.", 
                Me.GetType().FullName, GetType(advancedTexbox).FullName))
        End If
        MyBase.Initialize(oType)
    End Sub
End Class   

按原样使用此类。唯一需要做的是更改对自定义控件类的引用。为此,请在GetType语句中填写类名(参见上面粗体文本)。

接下来,您需要按照以下方式更改自定义控件类语句

<ToolboxItem(True)> <ToolboxBitmap(GetType(TheToolboxItem), _
"numTextbox.bmp")> Public Class advancedTexbox
    Inherits TextBox

不要忘记以 bmp 格式创建 16x16 像素的图标,并将文件放在项目中。

clsContextMenu

此类创建一个带有剪切、复制、粘贴删除菜单项的上下文菜单。没有什么特别的,所以这里不作描述。上下文菜单是主自定义文本框控件的嵌套控件。因此,我们需要一些自定义属性“菜单项剪切”等来布局菜单项的文本属性。

clsAdvTextBox

代码文档编写得很好,大多数情况下对于初学者来说也是直观易读的。源代码的各个部分通过区域进行分隔,以提高可读性。繁重的代码部分将在下面进行描述。

在控件的某个边缘绘制一个标记(三角形)。

有时,标记文本框以向用户显示这是一个特殊文本框可能很有用。例如,文本框输入是受控的,或者其输入是强制性的。要绘制标记(彩色三角形,参见上图),我们必须捕获一个窗口事件(WndProc),因为标准文本框没有绘制事件。

    ' ## draw colored triangle i a corner
    ' ## if pen color is transparent don't draw
    Protected Overrides Sub WndProc(ByRef m As Message)
        MyBase.WndProc(m)
        If m.Msg = &HF And Not Triangle Is Pens.Transparent Then ' WM_PAINT
            ' ## define triangle position
            Dim x2, y2 As Integer
            Select Case TriPosition
                Case _TriPos.Bottom_Left
                    x2 = 6 : y2 = Me.Height - 6
                Case _TriPos.Bottom_Right
                    x2 = Me.Width - 6 : y2 = Me.Height - 6
                Case _TriPos.Top_Left
                    x2 = 6 : y2 = 6
                Case _TriPos.Top_Right
                    x2 = Me.Width - 6 : y2 = 6
            End Select

            ' ## define triangle shape
            Dim points(3) As Point
            points(0) = New Point(x2 - 6, y2)
            points(1) = New Point(x2, y2 - 4)
            points(2) = New Point(x2, y2)
            points(3) = New Point(x2 - 6, y2)
            
            ' ## draw shape
            Dim g As Graphics = Graphics.FromHwnd(m.HWnd)
            g.DrawPolygon(Triangle, points)
            g.Dispose()
        End If
    End Sub

三角形的颜色可以通过自定义属性TriangleColor选择,位置可以通过自定义属性TriPosition选择。如果选择的颜色是transparent,则不绘制三角形。为了保持控件的所有元素(文本和三角形)整洁,我们必须调用me.refresh方法来强制控件在控件文本更改时重绘。我们在控件的Me.TextChanged 事件中这样做。

验证输入

为了控制输入,我们使用控件的Me.KeyPress 事件

 ' ##### Control the input value
    Private Sub numericTexbox_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress

        ' ## check if validation is required
        If InputType = _IType.NoValidation Then Return

        ' ## check if key pressed is backspace or delete, avoid group seperator
        If e.KeyChar = CChar(ChrW(Keys.Back)) Or e.KeyChar = CChar(ChrW(Keys.Delete)) And e.KeyChar <> GroupeSeparator Then
            Return
        End If

        ' ## check Range
        If Range Then
            If Char.IsDigit(e.KeyChar) Then
                Dim strTemp As String = Me.Text + e.KeyChar
                If Val(strTemp) >= RangeMin And Val(strTemp) <= RangeMax Then Return
            End If
        Else
            ' ## check for decimal
            If _InputType = 2 And _decimals > 0 And Char.IsDigit(e.KeyChar) Then
                ' ## check decimal places
                Dim temp() = Me.Text.Split(DecSeparator)
                If temp.Length = 2 Then

                    If temp(1).Length <= _decimals - 1 Then Return
                ElseIf temp.Length = 1 Then
                    Return
                End If
            ElseIf _InputType = 2 And e.KeyChar = DecSeparator AndAlso (Not Me.Text.Contains(DecSeparator)) Then
                Return
            ElseIf _InputType = 2 And _decimals = 0 Then
                _InputType = 1
            End If

            ' ## check integer
            If _InputType = 1 And Char.IsDigit(e.KeyChar) Then
                DecimalPlaces = 0
                Return
            End If

            ' ## check if negativ is allowed and negative is pressed as 1st char (leading negativ sign)
            If Me.Text.Length = 0 And e.KeyChar = "-" And _negativ And (Not Me.Text.Contains(DecSeparator)) Then Return
        End If

        ' ## all other cases are not valid
        e.Handled = True ' abord key press
        If Flash Then timerFlash.Enabled = e.Handled
        If e.Handled And BP Then Beep()
    End Sub

验证剪贴板操作

为了控制剪贴板操作,我们必须剥离标准的文本框上下文菜单。这可以通过将控件属性me.shortcutsEnabled设置为false来完成。我们在控件的New() 事件中这样做。自定义上下文菜单的定义被外包到单独的类clsContextMenu中,以提高透明度。当单击菜单项时,会触发clsContextMenu类的自定义事件CM_MenueItem,将单击的项目信息传输到控件的主类clsAdvTextbox

class clsContextMenu:

Public Class cMenue
    Inherits ContextMenuStrip

    Friend WithEvents mnCut As System.Windows.Forms.ToolStripMenuItem
    Friend WithEvents mnCopy As System.Windows.Forms.ToolStripMenuItem
    Friend WithEvents mnPaste As System.Windows.Forms.ToolStripMenuItem
    Friend WithEvents ToolStripSeparator1 As System.Windows.Forms.ToolStripSeparator
    Friend WithEvents mnDel As System.Windows.Forms.ToolStripMenuItem

    Public Event MenueItemClicked(ByVal sender)
... 
   Private Sub mnCopy_Click(sender As Object, e As EventArgs) Handles mnCopy.Click
        RaiseEvent MenueItemClicked(sender)
    End Sub

    Private Sub mnCut_Click(sender As Object, e As EventArgs) Handles mnCut.Click
        RaiseEvent MenueItemClicked(sender)
    End Sub

    Private Sub mnDel_Click(sender As Object, e As EventArgs) Handles mnDel.Click
        RaiseEvent MenueItemClicked(sender)
    End Sub

    Private Sub mnPaste_Click(sender As Object, e As EventArgs) Handles mnPaste.Click
        RaiseEvent MenueItemClicked(sender)
 End Sub 

class clsAdvTextBox:

    ' ##### control clipboard actions"
    Private Sub CM_MenueItemClicked(sender As Object) Handles CM.MenueItemClicked
        Select Case sender.name
            Case "mnCut"
                Clipboard.SetText(Me.SelectedText)
                Me.SelectedText = ""
            Case "mnCopy"
                If Me.SelectedText = "" Then
                    Clipboard.SetText(Me.Text)
                Else
                    Clipboard.SetText(Me.SelectedText)
                End If
            Case "mnPaste"
                If InputType <> _IType.NoValidation Then
                    If IsNumeric(Clipboard.GetText) Then
                        If InputType = _IType.Integer Then
                            Me.Text = CInt(Clipboard.GetText)
                        ElseIf InputType = _IType.Decimal Then
                            If DecimalPlaces = 0 Then
                                Me.Text = CInt(Clipboard.GetText)
                            Else
                                Me.Text = Math.Round(CDbl(Clipboard.GetText), DecimalPlaces)
                            End If
                        End If
                    Else
                        If Flash Then timerFlash.Enabled = True
                        If BP Then Beep()
                    End If
                Else
                    If Me.SelectedText = "" Then
                        Me.SelectedText = Clipboard.GetText()
                    Else
                        Me.Text = Clipboard.GetText
                    End If
                End If
            Case "mnDel"
                Me.SelectedText = ""
        End Select
    End Sub
顺便说一句,为了避免三角形标记或背景色闪烁,建议将父窗体的属性DoubleBufferd设置为 true!

历史

2014 年 5 月 7 日 - 11:15

  • 修正了一些丢失的注释和文本错误。
  • 重新编译并重建 ZIP 文件。
  • 添加了工具箱图标变通方法的描述
  • 添加了工具箱图标变通方法类
  • 修正了本地化,为上下文菜单项的文本添加了属性。
© . All rights reserved.