Создание TextBox с водяным знаком с помощью ControlStyles.UserPaint показывает водяной знак только один раз при создании компонента
Я работаю над элементом управления, наследующим от TextBox. Я хочу, чтобы он имел свойство водяного знака (текст, который вы видите, когда текста нет).
Все шаги предприняты:
В новом экземпляре Visual Studio щелкните ссылку "Создать новый проект", выберите тип проекта "Библиотека управления Windows Forms", назовите проект TBW1 (TextBox with Watermark) и нажмите "ОК". Переименуйте элемент управления по умолчанию в 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. Если это правда, элемент управления рисует сам, а не ОС.
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 и назовите проект TBW1_Client. Щелкните правой кнопкой мыши в обозревателе решений и выберите Set as Startup Project
,
Добавьте ссылку на текстовое поле с проектом водяного знака через Project > TBW1_Client Properties > References > Add > Browse > [Path to TBW1] > bin > Debug >TBW1.dll > OK.
Постройте проект. Элемент управления теперь доступен в наборе инструментов. Дважды щелкните его, чтобы получить элемент управления в окне теста. Он должен выглядеть точно так же, как обычный элемент управления TextBox.
Нажмите на элемент управления и проверьте его свойства в окне свойств. Два недавно определенных свойства WatermarkColor и WatermarkText будут показаны в конце списка свойств. Чтобы проверить функциональность, укажите отдельный цвет, например красный, и текстовое прочтение, например, "Введите здесь".
Оно работает! Однако есть две проблемы:
(1) Он работает только один раз, когда элемент управления отображается впервые. Ввод чего-то, а затем удаление текста оставляет текстовое поле пустым. Я хотел бы увидеть текст водяного знака снова.
(2) При отображении текста водяного знака в первый раз отображается правильный шрифт (как унаследовано от формы). При начале печати используется некрасивый системный шрифт.
Как я могу решить эти 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 ответа
Редактировать: Воскрешен из-за того, что OP отказался от требований к цвету.
Эта функция поддерживается собственным элементом редактирования, который переносит класс WinForm Textbox. Он поддерживается в Windows Vista и выше, если включены визуальные стили.
ссылка: сообщение EM_SETCUEBANNER
Пример:
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
Изменить: я не рекомендую использовать этот ответ, см. Комментарии. Перейти с ответом TnTinMan.
OnPaint
Предполагается, что все рисунки выполнены правильно. Только то, что это не по причинам, которые избегают меня. (Оказывается, что TextBox
это не элемент управления.Net. На самом деле, это просто оболочка элемента управления Win32.)
Тем не менее, переопределение WndProc
"s WM_PAINT
сообщение работает
Это рабочий TextBox
с двумя добавленными свойствами: WatermarkText
а также WatermarkColor
,
Public Class UTextBoxWatermark
Inherits TextBox
'============================================================================
'CONSTANTS.
'============================================================================
Const WM_PAINT As Integer = &HF
'============================================================================
'VARIABLES.
'============================================================================
Private gsWatermarkText As String = "Watermark"
Private glWatermarkColor As Color = Color.Gray
'============================================================================
'PROPERTIES.
'============================================================================
Public Property WatermarkText As String
Get
Return gsWatermarkText
End Get
Set(sValue As String)
gsWatermarkText = sValue
Me.Invalidate()
End Set
End Property
Public Property WatermarkColor As Color
Get
Return glWatermarkColor
End Get
Set(lValue As Color)
glWatermarkColor = lValue
Me.Invalidate()
End Set
End Property
'============================================================================
'CONSTRUCTORS AND DESTRUCTORS.
'============================================================================
Public Sub New()
'This call is required by the designer.
InitializeComponent()
End Sub
'============================================================================
'EVENT HANDLERS.
'============================================================================
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
Dim oBrush As SolidBrush
If m.Msg = WM_PAINT Then
'If the text is empty now, the watermark text should be written
'instead.
If Me.Text.Length = 0 Then
oBrush = New SolidBrush(glWatermarkColor)
Using oGraphics As Graphics = Me.CreateGraphics
oGraphics.DrawString(gsWatermarkText, Me.Font, oBrush, 0, 0)
End Using
End If
End If
End Sub
Private Sub UTextBoxWatermark_TextChanged(sender As Object, e As EventArgs) _
Handles Me.TextChanged
If Me.Text.Length = 0 Then
Me.Invalidate()
End If
End Sub
End Class