Watermark TextBox w WinForms

Czy ktoś może wskazać mi dobrą implementację podstawowego pola tekstowego Windows Forms, które początkowo wyświetla tekst znaku wodnego, który znika po wejściu kursora do niego? Myślę, że mogę stworzyć swój własny z kreatywnym wykorzystaniem zdarzeń Enter I Leave, ale jestem pewien, że gdzieś tu jest idealnie użyteczna implementacja. Widziałem implementację WPF i w razie potrzeby mogłem ją zagnieżdżać, ale natywna pochodna Textboxa WinForms byłaby lepsza.

Mam to do tej pory; nie próbowałem ale czy ktoś widzi jakieś rażące problemy?

public class WatermarkTextBox:TextBox
{
    public string WatermarkText { get; set; }

    public Color WatermarkColor { get; set; }

    private Color TextColor { get; set; }

    private bool isInTransition;

    public WatermarkTextBox()
    {
        WatermarkColor = SystemColors.GrayText;
    }

    private bool HasText { get { return Text.IsNotNullOrBlankOr(WatermarkText); }}

    protected override void OnEnter(EventArgs e)
    {
        base.OnEnter(e);

        if (HasText) return;

        isInTransition = true;
        ForeColor = TextColor;
        Text = String.Empty;
        isInTransition = false;
    }

    protected override void OnForeColorChanged(EventArgs e)
    {
        base.OnForeColorChanged(e);
        if (!isInTransition) //the change came from outside
            TextColor = ForeColor;
    }

    protected override void OnLeave(EventArgs e)
    {
        base.OnLeave(e);

        if (HasText) return;

        isInTransition = true;
        ForeColor = WatermarkColor;
        Text = WatermarkText.EmptyIfNull();
        isInTransition = false;
    }
}

EDIT: powyższe w końcu zadziałałoby z jakimś finessingiem, ale CueProvider działał znacznie lepiej. Oto moja ostateczna realizacja:

public class WatermarkTextBox:TextBox
{
    private string watermarkText;
    public string WatermarkText
    {
        get { return watermarkText; }
        set
        {
            watermarkText = value;
            if (watermarkText.IsNullOrBlank())
                CueProvider.ClearCue(this);
            else
                CueProvider.SetCue(this, watermarkText);
        }
    }
}

Mogłem całkowicie zintegrować funkcjonalność Cueprovidera, ale to działa pięknie.

Author: KeithS, 2011-02-04

6 answers

Oficjalna nazwa to "cue banner". Oto inny sposób, aby to zrobić, po prostu dziedziczenie TextBox również wykonuje zadanie. Dodaj nową klasę do swojego projektu i wklej kod pokazany poniżej. / Align = "left" / Upuść nową kontrolkę z góry skrzynki narzędziowej i ustaw właściwość Cue.

Otrzymasz podgląd na żywo wartości Cue w Projektancie, zlokalizowanej na właściwości języka formularza. Dużo huku za bardzo mało pieniędzy, doskonała demonstracja dobrych części Winforms.

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class CueTextBox : TextBox {
    [Localizable(true)]
    public string Cue {
        get { return mCue; }
        set { mCue = value; updateCue(); }
    }

    private void updateCue() {
        if (this.IsHandleCreated && mCue != null) {
            SendMessage(this.Handle, 0x1501, (IntPtr)1, mCue);
        }
    }
    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        updateCue();
    }
    private string mCue;

    // PInvoke
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, string lp);
}
 87
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-13 21:43:00

Zaktualizowałem odpowiedź udzieloną przez @ Hans Passant powyżej, aby wprowadzić stałe, aby były zgodne z pinvoke.net definicje i aby Kod przeszedł walidację FxCop.

class CueTextBox : TextBox
{
    private static class NativeMethods
    {
        private const uint ECM_FIRST = 0x1500;
        internal const uint EM_SETCUEBANNER = ECM_FIRST + 1;

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, string lParam);
    }

    private string _cue;

    public string Cue
    {
        get
        {
            return _cue;
        }
        set
        {
            _cue = value;
            UpdateCue();
        }
    }

    private void UpdateCue()
    {
        if (IsHandleCreated && _cue != null)
        {
            NativeMethods.SendMessage(Handle, NativeMethods.EM_SETCUEBANNER, (IntPtr)1, _cue);
        }
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        UpdateCue();
    }
}

Edit: zaktualizuj wywołanie PInvoke, aby ustawić atrybut CharSet, aby Błąd po bezpiecznej stronie. Więcej informacji można znaleźć na stronie SendMessage pod adresem pinvoke.net .

 30
Author: g t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-11 13:32:41
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);

I stałe wiadomości:

private const uint EM_SETCUEBANNER = 0x1501;
private const uint CB_SETCUEBANNER = 0x1703;    // minimum supported client Windows Vista, minimum supported server Windows Server 2008

I imho najlepszym sposobem na jej wdrożenie jest metoda rozszerzenia.
Tak więc dla kontrolki TextBox składnia będzie:

MyTextBox.CueBanner(false, "Password");

Z kodu:

public static void CueBanner(this TextBox textbox, bool showcuewhenfocus, string cuetext)
{
    uint BOOL = 0;
    if (showcuewhenfocus == true) { BOOL = 1; }

    SendMessage(textbox.Handle, EM_SETCUEBANNER, (IntPtr)BOOL, cuetext); ;
}
 12
Author: deegee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-07 23:25:01

Oto implementacja TextBox, która obsługuje wyświetlanie podpowiedzi (lub znaku wodnego lub cue):

  • pokazuje również podpowiedź, kiedy MultiLine jest prawdą.
  • opiera się na obsłudze WM_PAINT Wiadomości i rysowaniu podpowiedzi. Możesz więc po prostu dostosować podpowiedź i dodać pewne właściwości, takie jak kolor podpowiedzi, lub narysować ją od prawej do lewej lub kontrolować, kiedy pokazać podpowiedź.
using System.Drawing;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
    string hint;
    public string Hint
    {
        get { return hint; }
        set { hint = value; this.Invalidate(); }
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == 0xf)
        {
            if (!this.Focused && string.IsNullOrEmpty(this.Text)
                && !string.IsNullOrEmpty(this.Hint))
            {
                using (var g = this.CreateGraphics())
                {
                    TextRenderer.DrawText(g, this.Hint, this.Font,
                        this.ClientRectangle, SystemColors.GrayText , this.BackColor, 
                        TextFormatFlags.Top | TextFormatFlags.Left);
                }
            }
        }
    }
}

Jeśli użyjesz EM_SETCUEBANNER, będą 2 problemy. Tekst zawsze będzie wyświetlany w domyślnym systemie kolor. Również tekst nie będzie wyświetlany, gdy TextBox jest MultiLine.

Za pomocą rozwiązania do malowania możesz pokazać tekst w dowolnym kolorze. Możesz również pokazać znak wodny, gdy kontrolka jest wielowierszowa:

Tutaj wpisz opis obrazka

Pobierz

Możesz sklonować lub pobrać działający przykład:

 11
Author: Reza Aghaei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 05:52:34

Możesz dodać znak wodny do pola tekstowego (wielowierszowego lub nie), który działa całkiem dobrze, rysując go w różnych zdarzeniach controls Paint. Na Przykład:

    Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
        If TextBox1.Text = "" Then
            TextBox1.CreateGraphics.DrawString("Enter Text Here", Me.Font, New SolidBrush(Color.LightGray), 0, 0)
        End If
    End Sub

- OO -

 1
Author: user5923595,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-29 14:51:56
Private Sub randomSubName() Handles txtWatermark.Click
   txtWatermark.text = ""
End Sub

Ustaw domyślny tekst pola tekstowego niezależnie od tego, jaki ma być znak wodny, zakładam, że w tym przykładzie nazwiesz pole tekstowe txtWatermark

Hej, jestem nowy. Przepraszam, jeśli strasznie schrzaniłem post... Nie mam też pojęcia, czy to zadziała...
 0
Author: redstonepizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-03 03:43:34