1

I have a Form that contains a few textboxes. when loading the form, I want those textboxes to show a default grayed out text that disappears when the user clicks the textbox. I have used this code:

    private void Form1_Load(object sender, EventArgs e)
    {
        //textbox1 default text
        this.tBox1.Enter += new EventHandler(tBox1_Enter);
        this.textbox1.Leave += new EventHandler(tBox1_Leave);
        tBox1_SetText();
        
        /*...
         same for other textboxes
        */

    }

    /*textbox1 default text*/

    // default greyed out text in tBox1
    public void tBox1_SetText()
    {
        tBox1.Text = "some default text";
        tBox1.ForeColor = Color.Gray;
    }

    // remove default text from tBox1 once cruser is set in
    public void tBox1_Enter(object sender, EventArgs e)
    {
        if (tBox1.ForeColor == Color.Black)
            return;
        tBox1.Text = "";
        tBox1.ForeColor = Color.Black;
    }

    // sets the default text back to tBox1 once cruser is gone
    public void tBox1_Leave(object sender, EventArgs e)
    {
        if (tBox1.Text.Trim() == "")
            tBox1_SetText();
    }

    /*...
     same for other textboxes
    */

since I have a lot of textboxes I would like to move all this code from my form to a separate class (something like DefaultText.cs), create a method in that class ("ShowDefaultText()") and just call this method in form_load so my code would be more understandable. I tried creating this class but there is no #include in C# and I cant use static class/methods because of the instances. how can I do this otherwise?

Amit Gabay
  • 29
  • 2
  • that's what the [`#using`-directive](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/using-directive) is for – MakePeaceGreatAgain Sep 13 '21 at 09:47
  • 2
    You're going about this worng - create a component that wraps the functionality of having your "placeholder" text and drop that in in place of the default text box wherever you need it – Jamiec Sep 13 '21 at 09:49
  • @Jamiec That is exactly what im trying to avoid. I dont want to place it wherever i need it because i dont want to have a 100 lines in my form.cs dedicated only to set some text in a textbox. i want all the functionality in a different class and only write one simple line in my form code. – Amit Gabay Sep 13 '21 at 09:58
  • You're missing my point - by making this a component you have _no_ lines of code repeated. The code to make this text gray and a placeholder exists just once - in the component. – Jamiec Sep 13 '21 at 10:00
  • @Jamiec yes but unfortunately I need every textbox to show different text, so ill have to manually set it for every textbox and I want to avoid all those lines of code because my code is already pretty long and these lines arn't really significant. – Amit Gabay Sep 13 '21 at 10:04
  • You would set the default text in the designer like you would with every other property of a textbox! – Jamiec Sep 13 '21 at 10:07
  • @jamiec oh! Im sorry I misunderstood you at first. Thank you so much. – Amit Gabay Sep 13 '21 at 10:12
  • Uhm.... are you aware windows forms text box has a property [`PlaceholderText`](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.textbox.placeholdertext?view=net-5.0) which seems to do exactly what you're trying to re-invent? – Jamiec Sep 13 '21 at 10:21
  • @Jamiec i have actually searched for it quite a lot and couldnt find it. i searched online and the best solution i could find was creating the code i added in my question. I just looked again and it doesnt seen to have that property. – Amit Gabay Sep 13 '21 at 10:30
  • [Watermark Textbox in Windows Forms](https://stackoverflow.com/q/4902565/15498) seems to have dealt with this about a decade ago – Damien_The_Unbeliever Sep 13 '21 at 10:31

1 Answers1

2

As Jamiec points out, if you're using .net Core 3.1 or later then there is a TextBox.PlaceholderText property you can use to achieve you aim.

However if you're using .net framework 4.8 (or earlier) which doesn't have that property, the best way to do this is to write a customized TextBox class that does this for you - then you just need to use that instead of TextBox.

For example:

Firstly, add a custom control to your project using Add | New Item and then select "Windows Forms" and choose "Custom Control" and click "Add". Name it something like TextBoxGreyed.

Then edit the implementation to be something like this:

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

namespace WinFormsApp1
{
    public partial class TextBoxGreyed : TextBox
    {
        public TextBoxGreyed()
        {
            InitializeComponent();
            setText();
        }

        [
            Browsable(true),
            Category("Appearance"),
            DefaultValue(""),
            Localizable(true),
            Description("Default text to display when text box is empty")
        ]

        public string DefaultText
        {
            get => _defaultText;

            set
            {
                _defaultText = value;
                setText();
                Invalidate();
            }
        }

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

            if (this.ForeColor == Color.Black)
                return;

            this.Text      = "";
            this.ForeColor = Color.Black;
        }

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

            if (this.Text.Trim() == "")
                setText();
        }

        void setText()
        {
            this.Text      = _defaultText;
            this.ForeColor = Color.Gray;
        }

        string _defaultText = "";
    }
}

After building, you should see the new custom control at the top of the toolbox in the Forms designer. If you add a TextBoxGreyed to the form and inspect its properties, you should see a DefaultText property that you can set to your desired text.

This is a fairly basic example, but hopefully it will get you started.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • OP this is the solution I was describing in my comments - got there first so no need for me to post same answer. – Jamiec Sep 13 '21 at 10:11
  • The only problem with this solution is that by calling `setText` in the constructor (before `DefaultText` has been set) it will be blank when first loaded until it loses focus – Jamiec Sep 13 '21 at 10:18
  • @Jamiec I'll fix that in a couple of minutes. Stay tuned ;) – Matthew Watson Sep 13 '21 at 10:22
  • Think we're both beating a dead horse though: [`PlaceholderText`](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.textbox.placeholdertext?view=net-5.0) – Jamiec Sep 13 '21 at 10:23
  • @Jamiec I fixed the initialisation. As for `PlaceholderText` - that's not available in .net 4.8, so it might not be usable by the OP. If so, this is still useful! – Matthew Watson Sep 13 '21 at 10:25
  • Yep, great answer! – Jamiec Sep 13 '21 at 10:31
  • wish I had thought of this earlier. is there any solution that would remove all those line of code from my form class and wont require me to recreate a 100 textboxes? – Amit Gabay Sep 13 '21 at 10:35
  • Windows API has this covered, one just has to [SendMessage to the textbox](https://stackoverflow.com/a/22405509). Read the comments to learn when you can call SendMessage. – Dialecticus Sep 13 '21 at 10:38
  • @AmitGabay If you want 100 text boxes, then you're going to have to create them. Normally you'd add them using the designer, but it's certainly possible to [add them programmatically](https://learn.microsoft.com/en-us/troubleshoot/dotnet/csharp/add-controls-to-windows-forms). You'd probably want to write a method to create a text box with specified text and location and call it in a loop. – Matthew Watson Sep 13 '21 at 10:44
  • In .Net Framework, you can simply send EM_SETCUEBANNER to the TextBox, it creates the System default grayed text, in two flavors. Example here: [How to set the Text of a TextBox to a default value](https://stackoverflow.com/a/59803179/7444103) (as an extension method, but of course it can be used in a Custom Control, internally). It has the advantage that you don't need to override or keep track of the Control's `ForeColor`. – Jimi Sep 13 '21 at 12:59