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
There are a couple of things to note:
CueBanner
which is very easy to implement either from form code or as a custom controlPanel
Combobox
set to DropDownList
uses Italic (maybe you never noticed this).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.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
TextRenderer
for drawing the text which is usually a better choice for controls (save Graphics.DrawString()
for drawing to bitmaps and images)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.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:
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.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.
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