Создание 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
Другие вопросы по тегам