Making a WinForms TextBox behave like your browser's address bar

Viewed 122687

When a C# WinForms textbox receives focus, I want it to behave like your browser's address bar.

To see what I mean, click in your web browser's address bar. You'll notice the following behavior:

  1. Clicking in the textbox should select all the text if the textbox wasn't previously focused.
  2. Mouse down and drag in the textbox should select only the text I've highlighted with the mouse.
  3. If the textbox is already focused, clicking does not select all text.
  4. Focusing the textbox programmatically or via keyboard tabbing should select all text.

I want to do exactly this in WinForms.

FASTEST GUN ALERT: please read the following before answering! Thanks guys. :-)

Calling .SelectAll() during the .Enter or .GotFocus events won't work because if the user clicked the textbox, the caret will be placed where he clicked, thus deselecting all text.

Calling .SelectAll() during the .Click event won't work because the user won't be able to select any text with the mouse; the .SelectAll() call will keep overwriting the user's text selection.

Calling BeginInvoke((Action)textbox.SelectAll) on focus/enter event enter doesn't work because it breaks rule #2 above, it will keep overriding the user's selection on focus.

31 Answers

First of all, thanks for answers! 9 total answers. Thank you.

Bad news: all of the answers had some quirks or didn't work quite right (or at all). I've added a comment to each of your posts.

Good news: I've found a way to make it work. This solution is pretty straightforward and seems to work in all the scenarios (mousing down, selecting text, tabbing focus, etc.)

bool alreadyFocused;

...

textBox1.GotFocus += textBox1_GotFocus;
textBox1.MouseUp += textBox1_MouseUp;
textBox1.Leave += textBox1_Leave;

...

void textBox1_Leave(object sender, EventArgs e)
{
    alreadyFocused = false;
}


void textBox1_GotFocus(object sender, EventArgs e)
{
    // Select all text only if the mouse isn't down.
    // This makes tabbing to the textbox give focus.
    if (MouseButtons == MouseButtons.None)
    {
        this.textBox1.SelectAll();
        alreadyFocused = true;
    }
}

void textBox1_MouseUp(object sender, MouseEventArgs e)
{
    // Web browsers like Google Chrome select the text on mouse up.
    // They only do it if the textbox isn't already focused,
    // and if the user hasn't selected all text.
    if (!alreadyFocused && this.textBox1.SelectionLength == 0)
    {
        alreadyFocused = true;
        this.textBox1.SelectAll();
    }
}

As far as I can tell, this causes a textbox to behave exactly like a web browser's address bar.

Hopefully this helps the next guy who tries to solve this deceptively simple problem.

Thanks again, guys, for all your answers that helped lead me towards the correct path.

It's a bit kludgey, but in your click event, use SendKeys.Send( "{HOME}+{END}" );.

Click event of textbox? Or even MouseCaptureChanged event works for me. - OK. doesn't work.

So you have to do 2 things:

private bool f = false;

private void textBox_MouseClick(object sender, MouseEventArgs e)
{ 
  if (this.f) { this.textBox.SelectAll(); }
  this.f = false;
}

private void textBox_Enter(object sender, EventArgs e)
{
  this.f = true;
  this.textBox.SelectAll();
}
private void textBox_MouseMove(object sender, MouseEventArgs e) // idea from the other answer
{
  this.f = false; 
}

Works for tabbing (through textBoxes to the one) as well - call SelectAll() in Enter just in case...

A one line answer that I use...you might be kicking yourself...

In the Enter Event:

txtFilter.BeginInvoke(new MethodInvoker( txtFilter.SelectAll));

'Inside the Enter event
TextBox1.SelectAll();

Ok, after trying it here is what you want:

  • On the Enter event start a flag that states that you have been in the enter event
  • On the Click event, if you set the flag, call .SelectAll() and reset the flag.
  • On the MouseMove event, set the entered flag to false, which will allow you to click highlight without having to enter the textbox first.

This selected all the text on entry, but allowed me to highlight part of the text afterwards, or allow you to highlight on the first click.

By request:

    bool entered = false;
    private void textBox1_Enter(object sender, EventArgs e)
    {
        entered = true;
        textBox1.SelectAll();   //From Jakub's answer.
    }

    private void textBox1_Click(object sender, EventArgs e)
    {
        if (entered) textBox1.SelectAll();
        entered = false;
    }

    private void textBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (entered) entered = false;
    }

For me, the tabbing into the control selects all the text.

private bool _isSelected = false;
private void textBox_Validated(object sender, EventArgs e)
{
    _isSelected = false;
}

private void textBox_MouseClick(object sender, MouseEventArgs e)
{
    SelectAllText(textBox);
}

private void textBox_Enter(object sender, EventArgs e)
{
    SelectAllText(textBox);
}

private void SelectAllText(TextBox text)
{
    if (!_isSelected)
    {
        _isSelected = true;
        textBox.SelectAll();
    }
}

Interestingly, a ComboBox with DropDownStyle=Simple has pretty much exactly the behaviour you are looking for, I think.

(If you reduce the height of the control to not show the list - and then by a couple of pixels more - there's no effective difference between the ComboBox and the TextBox.)

Actually GotFocus is the right event (message really) that you are interested in, since no matter how you get to the control you’ll get this even eventually. The question is when do you call SelectAll().

Try this:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        this.textBox1.GotFocus += new EventHandler(textBox1_GotFocus);
    }

    private delegate void SelectAllDelegate();    
    private IAsyncResult _selectAllar = null; //So we can clean up afterwards.

    //Catch the input focus event
    void textBox1_GotFocus(object sender, EventArgs e)
    {
        //We could have gotten here many ways (including mouse click)
        //so there could be other messages queued up already that might change the selection.
        //Don't call SelectAll here, since it might get undone by things such as positioning the cursor.
        //Instead use BeginInvoke on the form to queue up a message
        //to select all the text after everything caused by the current event is processed.
        this._selectAllar = this.BeginInvoke(new SelectAllDelegate(this._SelectAll));
    }

    private void _SelectAll()
    {
        //Clean-up the BeginInvoke
        if (this._selectAllar != null)
        {
            this.EndInvoke(this._selectAllar);
        }
        //Now select everything.
        this.textBox1.SelectAll();
    }
}

Why don't you simply use the MouseDown-Event of the text box? It works fine for me and doesn't need an additional boolean. Very clean and simple, eg.:

private void textbox_MouseDown(object sender, MouseEventArgs e) {
    if (textbox != null && !string.IsNullOrEmpty(textbox.Text))
    {
        textbox.SelectAll();
    } }

Just derive a class from TextBox or MaskedTextBox:

public class SMaskedTextBox : MaskedTextBox
{
    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        this.SelectAll();
    }
}

And use it on your forms.

The below seems to work. The enter event handles the tabbing to the control and the MouseDown works when the control is clicked.

    private ########### void textBox1_Enter(object sender, EventArgs e)
    {
        textBox1.SelectAll();
    }

    private void textBox1_MouseDown(object sender, MouseEventArgs e)
    {
        if (textBox1.Focused)
            textBox1.SelectAll();
    }
Related