watermark Text-box Align Center

后端 未结 2 610
醉话见心
醉话见心 2020-12-22 08:39

I\'m using custom control watermark textbox and it\'s perfect, but the problem is that I can\'t change the watermark textAlign to center or right to left. By de

相关标签:
2条回答
  • 2020-12-22 09:26

    There are a couple of things to note:

    • There is already a mechanism for this in Windows called a CueBanner which is very easy to implement either from form code or as a custom control
    • Your implementation doesn't quite behave or look the same as the Native implementation:
      a) there is no visual cue (TB Cursor) until you hide the Panel
      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 typically works (as Visual Vincent points out in a comment, there is an option for this. I personally cant recall seeing it enough to register with me).
      c) In the native implementation, the Cue Text/Watermark uses the same font as the control. Only a Combobox set to DropDownList uses Italic (maybe you never noticed this).
    • Your app is leaking pretty badly in spots: CreateWatermark gets called repeatedly to redisplay the Watermark/CueText by creating a new Panel each time, but you are not disposing of the old one. Remember: If you create a control in code, you also need to dispose of it rather than just removing it from the controls collection.
    • The same is true for the Font object, although that will only matter if the Font on the TextBox changes.

    First, an answer to the question you asked - allowing Left, Right, Center 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
    
    • This uses the TextRenderer for drawing the text which is usually a better choice for controls (save Graphics.DrawString() for drawing to bitmaps and images)
    • This version shows it based on a new WatermarkTextAlign property in case you want to allow it to vary from the TextBox. If not, use parts of Visual Vincent's version or combine them.

    Cue Banner

    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
    

    Usage is simple:

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

    You can also create a control which inherits from TextBox and implement it there (override OnHandleCreated). This would allow the Cue Text to show at design time as your does, but with much less code.


    Finally, there is an easier way to do it with an internal control:

    • Rather than a Panel, use a Label. You can use the label's Font, TextAlign, ForeColor and Text properties as the backing fields for the Properties you expose. The RightToLeft setting could also be mirrored.
    • To make the Watermark/Cue Text/Label disappear as needed, just set it to invisible

    This would eliminate all issues related to disposing and drawing the text.

    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
    

    The Cue Text display is handled by making the label visible or not:

    ' 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
    

    Thats not all the code, but it should give you a start and it's both simpler and acts more like the native version.

    0 讨论(0)
  • 2020-12-22 09:38

    You set the watermark's position every time it is painted. You just have to check the TextBox's properties and determine where to place the watermark (however you must do so after you set the watermark's size).

    You would also have to set the watermark's width to the width of the actual watermark text or else you won't be able to do any alignment.

    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
    
    0 讨论(0)
提交回复
热议问题