.net WinForms 中的水印文本框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4902565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 15:13:23  来源:igfitidea点击:

Watermark TextBox in WinForms

.netwinformstextboxwatermark

提问by KeithS

Can anyone point me to a good implementation of a basic Windows Forms TextBox that will initially show watermark text that disappears when the cursor enters it? I think I can create my own with some creative use of Enter and Leave events, but I'm sure there's a perfectly usable implementation sitting around somewhere. I saw the WPF implementation and if necessary I could nest it, but a native WinForms TextBox derivative would be better.

任何人都可以指出一个基本的 Windows 窗体文本框的一个很好的实现,它最初会显示当光标进入时消失的水印文本?我想我可以通过一些创造性地使用 Enter 和 Leave 事件来创建我自己的,但我确信在某处有一个完美可用的实现。我看到了 WPF 实现,如有必要,我可以嵌套它,但本机 WinForms TextBox 派生类会更好。

I have this so far; haven't tried it yet but does anyone see any glaring problems?

到目前为止,我有这个;还没有尝试过,但有没有人看到任何明显的问题?

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: The above would have eventually worked with some finessing, but the CueProvider worked much better. Here's my final implementation:

编辑:以上最终会通过一些技巧工作,但 CueProvider 工作得更好。这是我的最终实现:

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);
        }
    }
}

I could have integrated the CueProvider functionality completely, but this works beautifully.

我本可以完全集成 CueProvider 功能,但这工作得很好。

回答by Hans Passant

The official term is "cue banner". Here's another way to do it, just inheriting TextBox gets the job done too. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox and set the Cue property.

官方术语是“提示横幅”。这是另一种方法,只需继承 TextBox 即可完成工作。向您的项目添加一个新类并粘贴如下所示的代码。编译。从工具箱顶部放下新控件并设置 Cue 属性。

You get a live preview of the Cue value in the designer, localized to the form's Language property. Lots of bang for very little buck, an excellent demonstration of the good parts of Winforms.

您可以在设计器中实时预览 Cue 值,并将其本地化为表单的 Language 属性。物超所值,这是对 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);
}

回答by g t

I've updated the answer given by @Hans Passant above to introduce constants, make it consistent with pinvoke.net definitions and to let the code pass FxCop validation.

我已经更新了上面@Hans Passant 给出的答案以引入常量,使其与 pinvoke.net 定义一致并让代码通过 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:update the PInvoke call to set CharSetattribute, to err on the safe side. For more info see the SendMessagepage at pinvoke.net.

编辑:更新 PInvoke 调用以设置CharSet属性,以确保安全。有关更多信息,请参阅pinvoke.net 上SendMessage页面。

回答by Reza Aghaei

.NET Framework

.NET 框架

Here is an implementation of a TextBoxwhich supports showing hint (or watermark or cue):

这是TextBox支持显示提示(或水印或提示)的 a 的实现:

  • It also shows the hint when MultiLineis true.
  • It's based on handling WM_PAINTmessage and drawing the hint. So you can simply customize the hint and add some properties like hint color, or you can draw it right to left or control when to show the hint.
  • 它还显示提示何时MultiLine为真。
  • 它基于处理WM_PAINT消息和绘制提示。因此,您可以简单地自定义提示并添加一些属性,例如提示颜色,或者您可以从右到左绘制它或控制何时显示提示。
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);
                }
            }
        }
    }
}

If you use EM_SETCUEBANNER, then there will be 2 issues. The text always will be shown in a system default color. Also the text will not be shown when the TextBoxis MultiLine.

如果使用EM_SETCUEBANNER,则会有 2 个问题。文本将始终以系统默认颜色显示。当TextBoxis时,文本也不会显示MultiLine

Using the painting solution, you can show the text with any color that you want. You also can show the watermark when the control is multi-line:

使用绘画解决方案,您可以使用您想要的任何颜色显示文本。您还可以在控件为多行时显示水印:

enter image description here

在此处输入图片说明

Download

下载

You can clone or download the working example:

您可以克隆或下载工作示例:

.NET CORE - TextBox.PlaceholderText

.NET 核心 - TextBox.PlaceholderText

The same approach has been used in .NET CORE implementation of TextBoxand in Windows Forms .NET CORE, you can use PlaceholderTextproperty.

.NET CORE 实现TextBox和 Windows Forms .NET CORE 中使用了相同的方法,您可以使用PlaceholderText属性。

It supports both multi-line and single-line text-box.

它支持多行和单行文本框。

回答by deegee

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

And the message constants:

和消息常量:

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

And imho the best way to implement it is as an extension method.
So for the TextBox control the syntax would be:

恕我直言,实现它的最佳方法是作为扩展方法。
因此对于 TextBox 控件,语法为:

MyTextBox.CueBanner(false, "Password");

From the code:

从代码:

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); ;
}

回答by PKanold

You can add a watermark to a Textbox (multiline or not) that works pretty well by drawing it in different controls Paint event. For Example:

您可以将水印添加到文本框(多行与否),通过在不同的控件 Paint 事件中绘制它,效果很好。例如:

    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-

-OO-

回答by VirtualValentin

Using WinForms on .NET Core:

在 .NET Core 上使用 WinForms:

This has been greatly simplified in .NET Core. You can directly add placeholder text by modifying the new PlaceholderText Property of the TextBox.

这在 .NET Core 中得到了极大的简化。您可以通过修改 TextBox 的新 PlaceholderText 属性直接添加占位符文本。

public virtual string PlaceholderText { get; set; }

Properties

特性

Placeholder text in WinForms .NET Core Application

WinForms .NET Core 应用程序中的占位符文本

Note that you would probably still have to edit the ForeColor if you want to get a colored Placeholder Text. The PlaceholderText field is visible when the Text field is null or empty.

请注意,如果您想获得彩色占位符文本,您可能仍需要编辑 ForeColor。当 Text 字段为 null 或为空时,PlaceholderText 字段可见。

回答by jonathana

I wrote a reusable custom control class for my project.
maybe it can help someone that need to implement multiple placeholder textboxes in his project. here is the C# and vb.net versions:

我为我的项目编写了一个可重用的自定义控件类。
也许它可以帮助需要在他的项目中实现多个占位符文本框的人。这是 C# 和 vb.net 版本:

C#:

C#:

namespace reusebleplaceholdertextbox
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // implementation
            CustomPlaceHolderTextbox myCustomTxt = new CustomPlaceHolderTextbox(
                "Please Write Text Here...", Color.Gray, new Font("ARIAL", 11, FontStyle.Italic)
                , Color.Black, new Font("ARIAL", 11, FontStyle.Regular)
                );

            myCustomTxt.Multiline = true;
            myCustomTxt.Size = new Size(200, 50);
            myCustomTxt.Location = new Point(10, 10);
            this.Controls.Add(myCustomTxt);
        }
    }

    class CustomPlaceHolderTextbox : System.Windows.Forms.TextBox
    {
        public string PlaceholderText { get; private set; }
        public Color PlaceholderForeColor { get; private set; }
        public Font PlaceholderFont { get; private set; }

        public Color TextForeColor { get; private set; }
        public Font TextFont { get; private set; }

        public CustomPlaceHolderTextbox(string placeholdertext, Color placeholderforecolor,
            Font placeholderfont, Color textforecolor, Font textfont)
        {
            this.PlaceholderText = placeholdertext;
            this.PlaceholderFont = placeholderfont;
            this.PlaceholderForeColor = placeholderforecolor;
            this.PlaceholderFont = placeholderfont;
            this.TextForeColor = textforecolor;
            this.TextFont = textfont;
            if (!string.IsNullOrEmpty(this.PlaceholderText))
            {
                SetPlaceHolder(true);
                this.Update();
            }
        }

        private void SetPlaceHolder(bool addEvents)
        {
            if (addEvents)
            {  
                this.LostFocus += txt_lostfocus;
                this.Click += txt_click;
            }

            this.Text = PlaceholderText;
            this.ForeColor = PlaceholderForeColor;
            this.Font = PlaceholderFont;
        }

        private void txt_click(object sender, EventArgs e)
        {
            // IsNotFirstClickOnThis:
            // if there is no other control in the form
            // we will have a problem after the first load
            // because we dont other focusable control to move the focus to
            // and we dont want to remove the place holder
            // only on first time the place holder will be removed by click event
            RemovePlaceHolder();
            this.GotFocus += txt_focus;
            // no need for this event listener now
            this.Click -= txt_click;
        }

        private void RemovePlaceHolder()
        {
            this.Text = "";
            this.ForeColor = TextForeColor;
            this.Font = TextFont;
        }
        private void txt_lostfocus(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.Text))
            {
                // set placeholder again
                SetPlaceHolder(false);
            }
        }

        private void txt_focus(object sender, EventArgs e)
        {
            if (this.Text == PlaceholderText)
            {
                // IsNotFirstClickOnThis:
                // if there is no other control in the form
                // we will have a problem after the first load
                // because we dont other focusable control to move the focus to
                // and we dont want to remove the place holder
                RemovePlaceHolder();
            }
        }
    }
}


VB.NET:


VB.NET:



Namespace CustomControls

    Public Class PlaceHolderTextBox
        Inherits System.Windows.Forms.TextBox

        Public Property PlaceholderText As String
        Public Property PlaceholderForeColor As Color
        Public Property PlaceholderFont As Font
        Public Property TextForeColor As Color
        Public Property TextFont As Font

        Public Sub New(ByVal placeholdertext As String, ByVal placeholderforecolor As Color, ByVal placeholderfont As Font, ByVal txtboxbackcolor As Color, ByVal textforecolor As Color, ByVal textfont As Font)
            Me.PlaceholderText = placeholdertext
            Me.PlaceholderFont = placeholderfont
            Me.PlaceholderForeColor = placeholderforecolor
            Me.PlaceholderFont = placeholderfont
            Me.TextForeColor = textforecolor
            Me.TextFont = textfont
            Me.BackColor = txtboxbackcolor
            If Not String.IsNullOrEmpty(Me.PlaceholderText) Then
                SetPlaceHolder(True)
                Me.Update()
            End If
        End Sub

        Private Sub SetPlaceHolder(ByVal addEvents As Boolean)
            If addEvents Then
                AddHandler Me.LostFocus, AddressOf txt_lostfocus
                AddHandler Me.Click, AddressOf txt_click
            End If

            Me.Text = PlaceholderText
            Me.ForeColor = PlaceholderForeColor
            Me.Font = PlaceholderFont
        End Sub

        Private Sub txt_click(ByVal sender As Object, ByVal e As EventArgs)
            RemovePlaceHolder()
            AddHandler Me.GotFocus, AddressOf txt_focus
            RemoveHandler Me.Click, AddressOf txt_click
        End Sub

        Private Sub RemovePlaceHolder()
            Me.Text = ""
            Me.ForeColor = TextForeColor
            Me.Font = TextFont
        End Sub

        Private Sub txt_lostfocus(ByVal sender As Object, ByVal e As EventArgs)
            If String.IsNullOrEmpty(Me.Text) Then
                SetPlaceHolder(False)
            End If
        End Sub

        Private Sub txt_focus(ByVal sender As Object, ByVal e As EventArgs)
            If Me.Text = PlaceholderText Then
                RemovePlaceHolder()
            End If
        End Sub
    End Class

End Namespace

回答by Horacio Quintero

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace PlaceHolderTextBoxCSharp
{
    public class CTextBox : TextBox
    {
        private Panel contenedor;
        protected string texto = "PlaceHolderText";
        protected Color colorTextoDefault = Color.Gray;
        public Color colorTexto = Color.Gray;
        protected Color colorTextoObligatorio = Color.Red;
        private Font fuente;
        private SolidBrush establecerColorTexto;
        private bool obligatoriedad = false;
        private bool colorConFoco = false;
        private int vuelta = 0;

        public CTextBox()
        {
            Inicializar();
        }

        private void Inicializar()
        {
            fuente = Font;
            CharacterCasing = CharacterCasing.Upper;
            contenedor = null;

            MuestraPlaceHolder();

            Leave += new EventHandler(PierdeFoco);
            TextChanged += new EventHandler(CambiaTexto);
        }

        private void EliminaPlaceHolder()
        {
            if (contenedor != null)
            {
                Controls.Remove(contenedor);
                contenedor = null;
            }
        }

        private void MuestraPlaceHolder()
        {
            if (contenedor == null && TextLength <= 0)
            {
                contenedor = new Panel();
                contenedor.Paint += new PaintEventHandler(contenedorPaint);
                contenedor.Invalidate();
                contenedor.Click += new EventHandler(contenedorClick);
                Controls.Add(contenedor);
            }
        }

        private void contenedorClick(object sender, EventArgs e)
        {
            Focus();
        }

        private void contenedorPaint(object sender, PaintEventArgs e)
        {
            contenedor.Location = new Point(2, 0);
            contenedor.Height = Height;
            contenedor.Width = Width;
            contenedor.Anchor = AnchorStyles.Left | AnchorStyles.Right;
            establecerColorTexto = new SolidBrush(colorTexto);
            Graphics g = e.Graphics;
            g.DrawString(texto, fuente, establecerColorTexto, new PointF(-1f, 1f));
        }

        private void PierdeFoco(object sender, EventArgs e)
        {
            if (TextLength > 0)
            {
                EliminaPlaceHolder();
            }
            else
            {
                if (obligatoriedad == true)
                {
                    colorTexto = colorTextoObligatorio;
                }
                else
                {
                    colorTexto = colorTextoDefault;
                }

                Invalidate();
            }
        }

        private void CambiaTexto(object sender, EventArgs e)
        {
            if (TextLength > 0)
            {
                EliminaPlaceHolder();
            }
            else
            {
                MuestraPlaceHolder();

                vuelta += 1;

                if (vuelta >= 1 && obligatoriedad == true)
                {
                    colorTexto = colorTextoObligatorio;
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            MuestraPlaceHolder();
        }

        protected override void OnInvalidated(InvalidateEventArgs e)
        {
            base.OnInvalidated(e);

            if (contenedor != null)
            {
                contenedor.Invalidate();
            }
        }

        [Category("Atributos PlaceHolder")]
        [Description("Establece el texto a mostrar.")]

        public string PlaceHolderText
        {
            get
            {
                return texto;
            }
            set
            {
                texto = value;
                Invalidate();
            }
        }

        [Category("Atributos PlaceHolder")]
        [Description("Establece el estilo de fuente del PlaceHolder.")]

        public Font PlaceHolderFont
        {
            get
            {
                return fuente;
            }
            set
            {
                fuente = value;
                Invalidate();
            }
        }

        [Category("Atributos PlaceHolder")]
        [Description("Indica si el campo es obligatorio.")]

        public bool PlaceHolderFieldRequired
        {
            get
            {
                return obligatoriedad;
            }
            set
            {
                obligatoriedad = value;
                Invalidate();
            }
        }
    }
}

回答by redstonepizza

Private Sub randomSubName() Handles txtWatermark.Click
   txtWatermark.text = ""
End Sub

Make the default text of the textbox whatever you want the watermark to be, I assume in this example you name the textbox txtWatermark

将文本框的默认文本设为您想要的水印,我假设在本示例中您将文本框命名为 txtWatermark

Hey, I'm new. So sorry if I terribly screwed up the post... I also have no idea if this works...

嘿,我是新来的。很抱歉,如果我把帖子搞砸了……我也不知道这是否有效……