【问题标题】:watermark Text-box Align Center水印文本框居中对齐
【发布时间】:2017-01-06 08:31:59
【问题描述】:

我正在使用自定义控件watermark textbox,它很完美,但问题是我无法将水印 textAlign 更改为居中或从右到左。 默认是从左到右,如何设置居中或从右到左?

这是代码:

Imports System.ComponentModel

Public Class Watermark
Inherits TextBox

'Declare A Few Variables
Dim WaterText As String
Dim WaterColor As Color
Dim WaterFont As Font
Dim WaterBrush As SolidBrush
Dim WaterContainer As Panel

Public Sub New()
    MyBase.New()
    StartProcess()
End Sub

Private Sub StartProcess()
    'Assign Values To the Variables
    WaterText = "Default Watermark"
    WaterColor = Color.Gray
    WaterFont = New Font(Me.Font, FontStyle.Italic)
    WaterBrush = New SolidBrush(WaterColor)

    CreateWatermark()

    AddHandler TextChanged, AddressOf ChangeText
    AddHandler FontChanged, AddressOf ChangeFont
End Sub

Private Sub CreateWatermark()
    WaterContainer = New Panel
    Me.Controls.Add(WaterContainer)
    AddHandler WaterContainer.Click, AddressOf Clicked
    AddHandler WaterContainer.Paint, AddressOf Painted
End Sub

Private Sub RemoveWatermark()
    Me.Controls.Remove(WaterContainer)
End Sub

Private Sub ChangeText(sender As Object, e As EventArgs)
    If Me.TextLength <= 0 Then
        CreateWatermark()
    ElseIf Me.TextLength > 0 Then
        RemoveWatermark()
    End If
End Sub

Private Sub ChangeFont(sender As Object, e As EventArgs)
    WaterFont = New Font(Me.Font, FontStyle.Italic)
End Sub

Private Sub Clicked(sender As Object, e As EventArgs)
    Me.Focus()
End Sub

Private Sub Painted(sender As Object, e As PaintEventArgs)
    WaterContainer.Location = New Point(2, 0)
    WaterContainer.Anchor = AnchorStyles.Left Or AnchorStyles.Right
    WaterContainer.Height = Me.Height
    WaterContainer.Width = Me.Width
    WaterBrush = New SolidBrush(WaterColor)

    Dim Graphic As Graphics = e.Graphics
    Graphic.DrawString(WaterText, WaterFont, WaterBrush, New PointF(-2.0!, 1.0!))
End Sub

Protected Overrides Sub OnInvalidated(e As System.Windows.Forms.InvalidateEventArgs)
    MyBase.OnInvalidated(e)
    WaterContainer.Invalidate()
End Sub

<Category("Watermark Attributes"), Description("Sets Watermark Text")> Public Property WatermarkText As String
    Get
        Return WaterText
    End Get
    Set(value As String)
        WaterText = value
        Me.Invalidate()
    End Set
End Property

<Category("Watermark Attributes"), Description("Sets Watermark Color")> Public Property WatermarkColor As Color
    Get
        Return WaterColor
    End Get
    Set(value As Color)
        WaterColor = value
        Me.Invalidate()
    End Set
End Property
End Class

【问题讨论】:

    标签: .net vb.net


    【解决方案1】:

    有几点需要注意:

    • 在 Windows 中已经有一种称为 CueBanner 的机制,它很容易通过表单代码或自定义控件实现
    • 您的实现与本机实现的行为或看起来不太一样:
      a) 在您隐藏 Panel
      之前,没有视觉提示(TB 光标) b) 在您开始输入之前,您的面板/提示文本不会消失。这似乎是您想要的,但这不是本地人通常的工作方式(正如 Visual Vincent 在评论中指出的那样,有一个选项。我个人不记得看到它足以向我注册)。
      c) 在本机实现中,提示文本/水印使用与控件相同的字体。只有 Combobox 设置为 DropDownList 使用斜体(也许你从未注意到这一点)。
    • 您的应用程序在某些地方严重泄漏:CreateWatermark 被反复调用以通过每次创建一个新的 Panel 来重新显示 Watermark/CueText,但您并没有处理旧的。请记住:如果您在代码中创建了一个控件,您还需要将其丢弃,而不仅仅是从控件集合中删除它。
    • Font 对象也是如此,尽管这仅在 TextBox 上的 Font 发生更改时才有意义。

    首先,回答您提出的问题 - 允许左、右、居中 TextAlignment。

    Private Sub Painted(sender As Object, e As PaintEventArgs)
        ' this can all be moved to the CreateWatermark method
        'WaterContainer.Location = New Point(0, 0)
        'WaterContainer.Anchor = AnchorStyles.Left Or AnchorStyles.Right
        'WaterContainer.Height = Me.Height
        'WaterContainer.Width = Me.Width
    
        Dim rect As New Rectangle(2, 1, Width - 8, Height - 2)
        Dim TxtFlags As TextFormatFlags
        Select Case TxtAlign
            Case HorizontalAlignment.Center
                TxtFlags = TextFormatFlags.HorizontalCenter
            Case HorizontalAlignment.Left
                TxtFlags = TextFormatFlags.Left
            Case HorizontalAlignment.Right
                TxtFlags = TextFormatFlags.Right
        End Select
    
        TextRenderer.DrawText(e.Graphics, WatermarkText, WFont, rect, WatermarkColor, TxtFlags)
    End Sub
    
    • 这使用TextRenderer 绘制文本,这通常是控件的更好选择(保存Graphics.DrawString() 用于绘制位图和图像)
    • 此版本基于新的WatermarkTextAlign 属性显示它,以防您希望它与TextBox 不同。如果没有,请使用 Visual Vincent 版本的部分内容或将它们组合起来。

    提示横幅

    Partial Friend Class NativeMethods
        ...
        <DllImport("user32.dll", CharSet:=CharSet.Auto)>
        Friend Shared Function SendMessage(hWnd As IntPtr, msg As Integer,
                                           wParam As Integer,
                                           <MarshalAs(UnmanagedType.LPWStr)> lParam As String) As Int32
        End Function
    
        Private Const EM_SETCUEBANNER As Integer = &H1501
        Private Const CB_SETCUEBANNER As Int32 = &H1703
    
        Friend Shared Sub SetCueText(ctl As Control, text As String)
    
            If TypeOf ctl Is ComboBox Then
                SendMessage(ctl.Handle, CB_SETCUEBANNER, 0, text)
            Else
                SendMessage(ctl.Handle, EM_SETCUEBANNER, 0, text)
            End If
    
        End Sub
    
    End Class
    

    用法很简单:

    NativeMethods.SetCueText(tbPInvWM, "Enter text...")
    NativeMethods.SetCueText(ComboBox1, "Select...")
    

    您还可以创建一个从 TextBox 继承的控件并在那里实现它(覆盖 OnHandleCreated)。这将允许提示文本像您一样在设计时显示,但代码要少得多。


    最后,有一个更简单的方法是使用内部控制:

    • 而不是Panel,使用Label。您可以使用标签的FontTextAlignForeColorText 属性作为您公开的属性的支持字段。 RightToLeft 设置也可以被镜像。
    • 要使水印/提示文本/标签根据需要消失,只需将其设置为不可见

    这将消除与处理和绘制文本相关的所有问题

    Public Class TextBoxEx
        Inherits TextBox
    
        Private WaterCtrl As Label
    
        ...
        ' Example of using label props as the backing field:
       <Category("Watermark Attributes"),
         Description("Sets Watermark Text"),
         DefaultValue("Watermark Text")>
            Public Property WatermarkText As String
            Get
                If WaterCtrl IsNot Nothing Then
                    Return WaterCtrl.Text
                Else
                    Return ""
                End If
            End Get
            Set(value As String)
                If WaterCtrl IsNot Nothing Then
                    WaterCtrl.Text = value
                End If
            End Set
        End Property
    

    提示文本显示是通过使标签可见与否来处理的:

    ' when they click on the label, hide and
    ' activate the TB like a regular CueBanner does
    Private Sub LblClick(sender As Object, e As EventArgs)
        WaterCtrl.Visible = False
        Me.Select()
    End Sub
    
    Protected Overrides Sub OnLeave(e As EventArgs)
        MyBase.OnLeave(e)
        WaterCtrl.Visible = (Text.Length = 0)
    End Sub
    

    这不是所有的代码,但它应该让你开始,它更简单,更像原生版本。

    【讨论】:

    • 感谢您的回答,您的回答非常方便,但我已经在项目中工作了一个月,直到现在我不想再做水印文本框,您的回答因为我的代码很完美,但是有没有办法使水印字体小于实际字体?以及关于 Visual Vincent 代码,水印未显示。
    • WaterFont = New Font(Me.Font.Name, Me.Font.SizeInPoints - 2, FontStyle.Italic) 或使用固定值。与泄漏相关的问题是您应该忽略的 - 最终您将耗尽资源。第三个版本 - 用标签替换面板 - 做你正在做的事情要简单得多 - 不需要绘制或处理任何东西
    • 我在您的回答中对此发表了评论:b) Your Panel/Cue Text doesn't disappear until you start typing. This seems to be what you want, but it is not how the native one works. - 事实上,本地人确实可以选择以这种方式操作。请参阅this documentation article: "wParam - TRUE 如果提示横幅应该显示即使编辑控件具有焦点;否则为 FALSE。FALSE 是默认行为——提示横幅在用户单击控件时消失"
    • @VisualVincent 我不知道那个选项在那里......而且它有效。但是我不记得在 WinForms 中看到过这种行为。任何地方的 Web 输入似乎都会持续存在,直到您输入 - 我一直在尝试选择和删除提示。他们似乎也使用了较深的颜色,使它看起来像是文字而不是提示……至少在我蹩脚的眼睛里
    • 嘿。好吧,这不是 WinForms,而是 Windows 7 的内置远程桌面应用程序 (RDP) 使用了这种行为(我不知道 Windows 8-10,因为我没有它们)。
    【解决方案2】:

    您在每次绘制水印时设置水印的位置。您只需要检查 TextBox 的属性并确定放置水印的位置(但是您必须在设置水印的大小后这样做)。

    您还必须将水印的宽度设置为实际水印文本的宽度,否则您将无法进行任何对齐。

    Private Sub Painted(sender As Object, e As PaintEventArgs)
        WaterContainer.Anchor = AnchorStyles.Left Or AnchorStyles.Right
        WaterContainer.Height = Me.Height
    
        'Declare a variable indicating whether RightToLeft is set or not.
        Dim RightToLeft As Boolean = (Me.RightToLeft = Windows.Forms.RightToLeft.Yes)
    
        'Set the watermark's client width according to the text's width.
        Dim MeasuredTextSize As Size = TextRenderer.MeasureText(WaterText, WaterFont, Me.Size, If(RightToLeft, TextFormatFlags.RightToLeft, TextFormatFlags.Default))
        WaterContainer.ClientSize = New Size(MeasuredTextSize.Width, WaterContainer.ClientSize.Height)
    
        'Determine the watermark's alignment.
        If (Me.TextAlign = HorizontalAlignment.Left AndAlso RightToLeft = False) OrElse (Me.TextAlign = HorizontalAlignment.Right AndAlso RightToLeft = True) Then
            WaterContainer.Location = New Point(2, 0) 'Left alignment, or right alignment with RightToLeft set.
    
        ElseIf Me.TextAlign = HorizontalAlignment.Center Then
            WaterContainer.Location = New Point((Me.Width / 2) - (WaterContainer.Width / 2), 0) 'Center alignment.
    
        ElseIf (Me.TextAlign = HorizontalAlignment.Right AndAlso RightToLeft = False) OrElse (Me.TextAlign = HorizontalAlignment.Left AndAlso RightToLeft = True) Then
            WaterContainer.Location = New Point((Me.Width - WaterContainer.Width) - 2, 0) 'Right alignment, or left alignment with RightToLeft set.
    
        End If
    
        WaterBrush = New SolidBrush(WaterColor)
    
        Dim Graphic As Graphics = e.Graphics
        Graphic.DrawString(WaterText, WaterFont, WaterBrush, New PointF(-2.0! + If(RightToLeft, MeasuredTextSize.Width, 0), 1.0!), New StringFormat(CType(If(RightToLeft, StringFormatFlags.DirectionRightToLeft, 0), Integer)))
    End Sub
    

    【讨论】:

    • 谢谢你的回答,但是这段代码在最后一行给了我错误:Graphic.DrawString(WaterText, WaterFont, WaterBrush, New PointF(-2.0!, 1.0!), New StringFormat(If(Me.RightToLeft, 0)))这里的错误:Me.RightToLeft错误是:First Operated in binary'If' expression must be nullable or reference type.
    • @Rabeeaqabaha :很抱歉,犯了一点代码错误。编辑了答案。
    • 代码现在没有错误,但不幸的是水印文字现在消失了:(.
    • @Rabeeaqabaha :您尝试过不同类型的设置吗?水印的对齐方式应根据 TextBox 的 TextAlignRightToLeft 属性而有所不同。等有时间我会试试这个,但我现在恐怕不能这样做(我用手机写了答案)。
    • @Plutonix :你肯定是对的!将它停靠在TextBox 是最简单的事情,因为它只需要你修改它的TextAlign 属性。 -- 我想我会帮助 OP 修改他当前的代码,因为提问者并不总是希望在他们自己设法实现之后改变他们的做事方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-17
    • 2015-07-29
    • 1970-01-01
    • 2011-03-13
    • 2018-05-04
    • 1970-01-01
    相关资源
    最近更新 更多