How do I implement a TextBox that displays “Type h

2019-01-17 10:12发布

问题:

Displaying "Type here to ..." until the user enters text into a TextBox is a well-known usability feature nowadays. How would one implement this feature in C#?

My idea is to override OnTextChanged, but the logic to handle the changes of text from and to "Type here" is a bit tricky...

Displaying "Type here" on initialization and removing it on first input is easy, but I want to display the message every time the entered text becomes empty.

回答1:

What you're looking for is a texbox with "watermark"

There's a sample implementation for C# here.

Hope it helps



回答2:

Something that has worked for me:

this.waterMarkActive = true;
this.textBox.ForeColor = Color.Gray;
this.textBox.Text = "Type here";

this.textBox.GotFocus += (source, e) =>
  {
    if (this.waterMarkActive)
    {
      this.waterMarkActive = false;
      this.textBox.Text = "";
      this.textBox.ForeColor = Color.Black;
    }
  };

this.textBox.LostFocus += (source, e) =>
  {
    if (!this.waterMarkActive && string.IsNullOrEmpty(this.textBox.Text))
    {
      this.waterMarkActive = true;
      this.textBox.Text = "Type here";
      this.textBox.ForeColor = Color.Gray;
    }
  };

Where bool waterMarkActive is a class member variable and textBox is the TextBox. This probably should be encapsulated though :) There might be some issues with this approach, but I'm not currently aware of any.

I recently discovered that Windows support water marks in text boxes; they are called cue banners (see here). It's very easy to implement:

// Within your class or scoped in a more appropriate location:
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

// In your constructor or somewhere more suitable:
SendMessage(textBox.Handle, 0x1501, 1, "Please type here.");

Where textBox is an instance of TextBox, 0x1501 is the code for the windows message EM_SETCUEBANNER, the wParam may either be TRUE (non-zero) or FALSE (zero), and lParam is the water mark you'd like to display. wParam indicates when the cue banner should be displayed; if set to TRUE then the cue banner will be displayed even when the control has focus.



回答3:

Based on @Pooven's answer (thank you!), I created this class. Works for me.

/// <summary>
/// A textbox that supports a watermak hint.
/// </summary>
public class WatermarkTextBox : TextBox
{
    /// <summary>
    /// The text that will be presented as the watermak hint
    /// </summary>
    private string _watermarkText = "Type here";
    /// <summary>
    /// Gets or Sets the text that will be presented as the watermak hint
    /// </summary>
    public string WatermarkText
    {
        get { return _watermarkText; }
        set { _watermarkText = value; }
    }

    /// <summary>
    /// Whether watermark effect is enabled or not
    /// </summary>
    private bool _watermarkActive = true;
    /// <summary>
    /// Gets or Sets whether watermark effect is enabled or not
    /// </summary>
    public bool WatermarkActive
    {
        get { return _watermarkActive; }
        set { _watermarkActive = value; }
    }

    /// <summary>
    /// Create a new TextBox that supports watermak hint
    /// </summary>
    public WatermarkTextBox()
    {
        this._watermarkActive = true;
        this.Text = _watermarkText;
        this.ForeColor = Color.Gray;

        GotFocus += (source, e) =>
        {
            RemoveWatermak();
        };

        LostFocus += (source, e) =>
        {
            ApplyWatermark();
        };

    }

    /// <summary>
    /// Remove watermark from the textbox
    /// </summary>
    public void RemoveWatermak()
    {
        if (this._watermarkActive)
        {
            this._watermarkActive = false;
            this.Text = "";
            this.ForeColor = Color.Black;
        }
    }

    /// <summary>
    /// Applywatermak immediately
    /// </summary>
    public void ApplyWatermark()
    {
        if (!this._watermarkActive && string.IsNullOrEmpty(this.Text)
            || ForeColor == Color.Gray ) 
        {
            this._watermarkActive = true;
            this.Text = _watermarkText;
            this.ForeColor = Color.Gray;
        }
    }

    /// <summary>
    /// Apply watermak to the textbox. 
    /// </summary>
    /// <param name="newText">Text to apply</param>
    public void ApplyWatermark(string newText)
    {
        WatermarkText = newText;
        ApplyWatermark();
    }

}


回答4:

I'm just starting to learn C# this semester so I'm not an expert, but this worked for me: (This is using windows forms)

private void Form1_Load(object sender, EventArgs e)
{
    textBox1.SelectionStart = 0;  //This keeps the text
    textBox1.SelectionLength = 0; //from being highlighted
    textBox1.ForeColor = Color.Gray;
}

private void textBox_MouseMove(object sender, MouseEventArgs e)
{
    Cursor.Current = Cursors.IBeam; //Without this the mouse pointer shows busy
}

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
    if (textBox1.Text.Equals("Type here...") == true)
    {
        textBox1.Text = "";
        textBox1.ForeColor = Color.Black;
    }
}

private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
    if (textBox1.Text.Equals(null) == true || textBox1.Text.Equals("") == true)
    {
        textBox1.Text = "Type here...";
        textBox1.ForeColor = Color.Gray;
    }
}


回答5:

  [DllImport("user32.dll", CharSet = CharSet.Auto)]
  private static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)]string lParam);
  const int EM_SETCUEBANNER = 0x1501; 

  public Form1()
  {
      InitializeComponent();
      SendMessage(textBox1.Handle, EM_SETCUEBANNER, 1, "Username");
      SendMessage(textBox2.Handle, EM_SETCUEBANNER, 1, "Password");
  }


回答6:

Handle the lost focus event and if the property Text is empty, fill it with your default string.



回答7:

If this is ASP.NET (as opposed to winforms), you could do this:

If you are using jQuery, add this to your document ready (or however you initialize your page):

var $textbox = $("textbox selector"); // assumes you select a single text box
if ($textbox.val() == "") {
   $textbox.val("Type here to...");
   $textbox.one('focus', function() {
     $(this).attr('value', '');
   });
}

You'll need to do some small refactoring if you are selecting more than one text box (put the if statement inside of an each on the element).



回答8:

In the last version of C# the TextBox has the property PlaceholderText, which does all work. So you only need to set "Type here..." as value of this property.



回答9:

You can draw string "Type here" to the textbox background until it empty



回答10:

If this is for ASP.NET then you can try TextBoxWatermark.

If this is for Windows Forms, this is already answered here in SO.



回答11:

Why using OnTextChanged? I would suggest to remove the text "Type here" when the TextBox gets focus. When the control loses focus and no text is entered, you can display the text again.

Same result and no need for tricky logic.



回答12:

If you want to avoid control resizing problems and data binding problems and make the code simpler (ok, it is questionable), you can just use a label and toggle it's visibility. Then

    private void FilterComboBox_GotFocus(object sender, EventArgs e)
    {
        FilterWatermarkLabel.Visible = false;
    }

    private void FilterComboBox_LostFocus(object sender, EventArgs e)
    {
        if (!FilterWatermarkLabel.Visible && string.IsNullOrEmpty(FilterComboBox.Text))
        {
            FilterWatermarkLabel.Visible = true;
        }
    }

Another approach for images and also avoiding data binding problems is here https://msdn.microsoft.com/en-us/library/bb613590(v=vs.100).aspx



回答13:

PRODUCES SIMILAR OUTPUT TO HTML WATERMARK

Here is my code for textbox "watermark" or "preview" text - works great! Using Windows Forms Application.

NOTE: This example has 3 text boxes, each has the below method for the "mouse leave" event, and "mouse enter" event respectively.

private void textBoxFav_Leave(object sender, EventArgs e) {
  TextBox textbox = (TextBox)sender;
  if (String.IsNullOrWhiteSpace(textbox.Text)) {
    textbox.ForeColor = Color.Gray;
    if (textbox.Name == "textBoxFavFood") {
      textbox.Text = "Favorite Food";
    }
    else if (textbox.Name == "textBoxFavDrink") {
      textbox.Text = "Favorite Drink";
    }
    else if (textbox.Name == "textBoxFavDesert") {
      textbox.Text = "Favorite Desert";
    }
  }
  else {
    textbox.ForeColor = Color.Black;
  }
}

private void textBoxFav_Enter(object sender, EventArgs e) {
  TextBox textbox = (TextBox)sender;
  if (textbox.Text == "Favorite Food" || textbox.Text == "Favorite Drink" || textbox.Text == "Favorite Desert") {
    textbox.Text = "";
    textbox.ForeColor = Color.Black;
  }
}


回答14:

Based on @Joel's answer. I fix his class (thanks for the base!)

/// <summary>
/// A textbox that supports a watermak hint.
/// Based on: https://stackoverflow.com/a/15232752
/// </summary>
public class WatermarkTextBox : TextBox
{
    /// <summary>
    /// The text that will be presented as the watermak hint
    /// </summary>
    private string _watermarkText;

    /// <summary>
    /// Gets or Sets the text that will be presented as the watermak hint
    /// </summary>
    public string WatermarkText
    {
        get { return _watermarkText; }
        set { _watermarkText = value; }
    }

    /// <summary>
    /// Whether watermark effect is enabled or not
    /// </summary>
    private bool _watermarkActive;
    /// <summary>
    /// Gets or Sets whether watermark effect is enabled or not
    /// </summary>
    public bool WatermarkActive
    {
        get { return _watermarkActive; }
        set { _watermarkActive = value; }
    }

    /// <summary>
    /// Create a new TextBox that supports watermak hint
    /// </summary>
    public WatermarkTextBox()
    {
        this.WatermarkActive = _watermarkActive;
        this.Text = _watermarkText;
    }

    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        if (this.WatermarkActive)
            CheckWatermark();
    }

    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        CheckWatermark();
    }

    protected override void OnLostFocus(EventArgs e)
    {
        base.OnLostFocus(e);
        CheckWatermark();
    }        

    public void CheckWatermark()
    {
        if ((this.WatermarkActive) && String.IsNullOrWhiteSpace(this.Text))
        {
            ForeColor = Color.Gray;
            this.Text = _watermarkText;
        }
        else if ((this.WatermarkActive) && (!String.IsNullOrWhiteSpace(this.Text)))
        {
            if (this.Text == _watermarkText)
                this.Text = "";
            ForeColor = Color.Black;
        }
        else
            ForeColor = Color.Black;
    }
}


回答15:

Based on answer of Ahmed Soliman Flasha use following class:

public class TextBoxHint : TextBox
{
    string _hint;

    [Localizable(true)]
    public string Hint
    {
        get { return _hint; }
        set { _hint = value; OnHintChanged(); }
    }

    protected virtual void OnHintChanged()
    {
        SendMessage(this.Handle, EM_SETCUEBANNER, 1, _hint);
    }     

    const int EM_SETCUEBANNER = 0x1501;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)]string lParam);
}


回答16:

Displaying "Type here to ..." until the user enters text into a TextBox is a well-known usability feature nowadays. How would one implement this feature in C#?

  1. Set textbox.text as "Type here to ..."

  2. create an event, say box_click()

  3. -->Put this code in your method

    private void box_Click(object sender, EventArgs e)
    {
        Textbox b = (Textbox)sender;
        b.Text = null;
    }
    
  4. now assign this method to the "Enter" event of your textbox(maybe one or many)