WinForms中的水印TextBox

Kei*_*thS 53 .net textbox watermark winforms

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

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:上面最终会有一些finessing,但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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以完全集成CueProvider功能,但这很有效.

Han*_*ant 94

官方用语是"cue banner".这是另一种方法,只需继承TextBox即可完成工作.在项目中添加一个新类并粘贴下面显示的代码.编译.从工具箱顶部删除新控件并设置Cue属性.

您可以在设计器中实时预览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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法的问题是,一旦文本框获得焦点,提示文本就会消失,即使框中没有文本也是如此.如果您的表单带有一个获取默认焦点的文本框,则该提示将不会对用户可见,从而使提示的整个目的无效. (9认同)
  • 我发现的一个问题是它不支持Multiline TextBox控件.我不确定如何改变这个类以支持Multiline. (3认同)
  • @DerekW:这不支持设计的多文本框/ RTB控件.你应该为此编写自己的控件. (3认同)
  • @IgorBrejc,只有当“SendMessage”方法中的第三个参数是“(IntPtr)0”时才会发生这种情况。在搜索 EM_SETCUEBANER 消息时,它在 msdn 中进行了记录。 (2认同)

g t*_*g t 31

我已经更新了@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:更新PInvoke调用set CharSet属性,以安全方面出错.有关更多信息,请参阅pinvoke.net上SendMessage页面.


dee*_*gee 14

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

并且消息常量:

private const uint EM_SETCUEBANNER = 0x1501;
private const uint CB_SETCUEBANNER = 0x1703;    // minimum supported client Windows Vista, minimum supported server Windows Server 2008
Run Code Online (Sandbox Code Playgroud)

而imho实现它的最佳方式是作为扩展方法.
因此,对于TextBox控件,语法将是:

MyTextBox.CueBanner(false, "Password");
Run Code Online (Sandbox Code Playgroud)

从代码:

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); ;
}
Run Code Online (Sandbox Code Playgroud)

  • 您不能像这样修改pinvoke声明。对于32位代码(返回int32)或64位代码(wparam为int64),它都不正确。它往往会偶然地起作用,但是当您停止幸运时,您将没有机会弄清楚它为何停止起作用。 (2认同)

Rez*_*aei 13

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

  • 它还显示了何时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);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用EM_SETCUEBANNER,那么将有2个问题.文本始终以系统默认颜色显示.另外,文本将不会被显示时TextBoxMultiLine.

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

在此输入图像描述

下载

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


Vir*_*tin 5

在 .NET Core 上使用 WinForms:

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

public virtual string PlaceholderText { get; set; }
Run Code Online (Sandbox Code Playgroud)

特性

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

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

  • 请注意,这**仅在 .NET Core 3.0 或更高版本中可用**。 (2认同)