使用ControlStyles.UserPaint创建带有水印的TextBox仅在创建组件时显示水印

时间:2018-04-07 05:49:15

标签: .net vb.net inheritance textbox onpaint

我处理从TextBox继承的控件。我希望它具有水印属性(没有文本时看到的文本)。

所有步骤:

在新的Visual Studio实例中,单击“创建新项目”链接,选择项目类型Windows窗体控件库,将项目命名为TBW1(带有水印的TextBox),然后单击“确定”。将默认控件重命名为UTextBoxWatermark。

我想从TextBox控件继承,但默认情况下新的用户控件继承自UserControl类。这是在设计师中定义的。

要访问和修改设计器,请在“解决方案资源管理器”的顶​​部单击“显示所有文件”。展开UTextBoxWatermark.vb。双击CTextBoxWatermark.Designer.vb以在代码编辑器中打开它。

将基类Control替换为UserControl

Partial Class UTextBoxWatermark
    Inherits System.Windows.Forms.UserControl
    ...
End Class

到TextBox

Partial Class UTextBoxWatermark
    Inherits System.Windows.Forms.TextBox
    ...
End Class

在InitializeComponent过程中,删除AutoScaleMode赋值。它在TextBox控件中不存在。

Private Sub InitializeComponent()
    components = New System.ComponentModel.Container()
End Sub

关闭CTextBoxWatermark.Designer.vb。使用“全部保存”将新项目保存在项目主文件夹中。

用户控件设计器不再可用,因为它将从继承的类(即TextBox)绘制。在代码编辑器中打开CTextBoxWatermark.vb。正是在这里实现了扩展功能。

我想添加2个属性:一个用于Text属性包含0长度字符串时显示的文本,另一个用于绘制此文本的颜色。

Public Class UTextBoxWatermark
    '============================================================================
    'VARIABLES.
    '============================================================================
    Private gsWatermarkText As String
    Private glWatermarkColor As Color

    '============================================================================
    'PROPERTIES.
    '============================================================================
    Public Property WatermarkText As String
        Get
            Return gsWatermarkText
        End Get
        Set(sValue As String)
            gsWatermarkText = sValue
        End Set
    End Property

    Public Property WatermarkColor As Color
        Get
            Return glWatermarkColor
        End Get
        Set(lValue As Color)
            glWatermarkColor = lValue
        End Set
    End Property
End Class

要绘制文本,将覆盖OnPaint事件。对于文本框,除非在构造函数中将ControlStyles.UserPaint属性设置为True,否则不会调用此事件。如果为true,则控件会自行绘制而不是操作系统。

Public Class UTextBoxWatermark
    ...

    '============================================================================
    'CONSTRUCTORS AND DESTRUCTORS.
    '============================================================================
    Public Sub New()
        'This call is required by the designer.
        InitializeComponent()

        SetStyle(ControlStyles.UserPaint, True)
    End Sub

    '============================================================================
    'EVENT HANDLERS.
    '============================================================================
    Protected Overrides Sub OnPaint(
        ByVal e As System.Windows.Forms.PaintEventArgs)

        Dim oBrush As SolidBrush

        'If the text is empty now, the watermark text should be written instead.
        If Me.Text.Length = 0 Then
            oBrush = New SolidBrush(glWatermarkColor)
            e.Graphics.DrawString(gsWatermarkText, Me.Font, oBrush, 0, 0)
        End If
    End Sub
End Class

为了测试组件,必须立即构建它。

通过文件>向该解决方案添加新项目;添加>新项目。选择Windows Forms App并将项目命名为TBW1_Client。在解决方案资源管理器中右键单击它,然后选择Set as Startup Project

通过Project>添加带有水印项目的文本框的引用; TBW1_Client属性>参考文献>添加>浏览> [TBW1的途径]> bin>调试> TBW1.dll>行。

构建项目。该控件现在可在工具箱中使用。双击它以在测试窗口中获取控件。它看起来应该与普通的TextBox控件完全相同。

单击控件并在属性窗口中检查其属性。两个新定义的属性WatermarkColor和WatermarkText将显示在属性列表的末尾附近。要测试功能,请提供不同的颜色,例如红色和文本阅读,例如“在此处键入”。

有效!但是,有两个问题:

(1)第一次显示控件时,它只工作一次。键入内容然后删除文本会使文本框保持空白。我希望再次看到水印文本。

(2)第一次显示水印文本时,会显示正确的字体(从表格中继承)。开始输入时,会使用丑陋的系统字体。

如何解决这两个问题?

修改

根据VisualVincent的评论,我改进了OnPaint:

Protected Overrides Sub OnPaint(
    ByVal e As System.Windows.Forms.PaintEventArgs)

    Dim oBrush As SolidBrush

    MyBase.OnPaint(e)

    'If the text is empty now, the watermark text should be written instead.
    If Me.Text.Length = 0 Then
        oBrush = New SolidBrush(glWatermarkColor)
        e.Graphics.DrawString(gsWatermarkText, Me.Font, oBrush, 0, 0)
    Else
        oBrush = New SolidBrush(Me.ForeColor)
        e.Graphics.DrawString(Me.Text, Me.Font, oBrush, 0, 0)
    End If
End Sub

并添加了

Private Sub UTextBoxWatermark_TextChanged(sender As Object, e As EventArgs) _
    Handles Me.TextChanged

    Me.Invalidate()
End Sub

出现水印。开始写时,文本仍然显示,但仍然是丑陋的系统字体。当我将鼠标悬停在它上面时,文本会消失。

当我删除文字时,水印不会重新出现,除非我将鼠标悬停在它上面。

2 个答案:

答案 0 :(得分:2)

编辑:由于OP放弃自定义的前色要求而复活。

WinForm Textbox类包装的本机编辑控件支持此功能。如果启用了视觉样式,则在Windows Vista及更高版本中受支持。

参考:EM_SETCUEBANNER message

示例:

Imports System.Runtime.InteropServices

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        SetWaterMark(TextBox1, "Enter Something Here", True)
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Unicode)>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Boolean, ByVal lParam As String) As Boolean
    End Function

    Private Shared Sub SetWaterMark(tb As TextBox, waterMarkText As String, Optional showIfFocused As Boolean = True)
        Const ECM_FIRST As Int32 = &H1500
        Const EM_SETCUEBANNER As Int32 = ECM_FIRST + 1
        If VisualStyles.VisualStyleInformation.IsEnabledByUser Then
            SendMessage(tb.Handle, EM_SETCUEBANNER, showIfFocused, waterMarkText)
        End If
    End Sub
End Class

答案 1 :(得分:1)

修改:我不建议您使用此答案,请参阅评论。和TnTinMan一起回答。

OnPaint应该正确地完成所有绘图。只是因为它不是出于逃避我的原因。 (事实证明,TextBox不是.Net控件。实际上,它只是包装一个Win32控件。)

但是,覆盖WndProc的{​​{1}}消息 可以正常工作。

这是一个有效的WM_PAINT,其中包含两个属性:TextBoxWatermarkText

WatermarkColor