Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify highlight behavior when double clicking a text area or text box?

I've noticed many applications that expect you to do a lot of text editing will provide non-default behavior for double clicking text in that the application highlights text that it believes you're most likely trying to interact with.

As a quick example, this sentence behaves differently in different applications:

This is a "sample" sentence

If I type that in Notepad and double click the word 'sample' (ideally middle of the word, say, between the 'm' and 'p' of sample) then notepad highlights from the first quote to the space after the second quote inclusive. If that sentence is in a comment in Visual Studio and I double click in the same location, it highlights from the 's' to the 'e' of sample without highlighting the quotes.

How can I customize these highlighting behaviors in my own application? Is it different from winforms to WPF? I suppose I could hack my way to make it work on a double click event, but is there a more elegant/deliberate solution meant exclusively for this?

like image 670
Corey Ogburn Avatar asked Jun 28 '11 14:06

Corey Ogburn


2 Answers

Yes, using the DoubleClick event to do as you want is kludgy as it appears it's doing the selection twice which is slower, looks worse, and could trigger unwanted events/code.

So the code below should do the trick for Winforms at least. Create a new class, and extend the TextBox (or RichTextBox) in the usual manner (using the newly created control which should magically appear in the designer). I've made a simple routine where you can specify the delimiter to use. With a little more work, it should be easy to account for a range of a characters instead of just a single one, or even create other more advanced ways of selecting.

If you're using a TextBox instead of a RichTextBox, simply remove the "Rich" bit that appears twice in the class definition.

class RichTextBoxX : RichTextBox
{
    char delimiter = ',';   // Specify what character to use for start and end of selection

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        if (m.Msg==0x0203) // WM_LBUTTONDBLCLK
        {
            // Perfect situation for off-by-one bugs! Checked thoroughly for at least 10 mins, so should be okay now:
            int start = this.SelectionStart;
            if (start < 1) start = 1;
            int left = this.Text.LastIndexOf(delimiter, start - 1);
            int right = this.Text.IndexOf(delimiter, start);
            if (right == -1) right = Text.Length;
            this.SelectionStart = left + 1;
            this.SelectionLength = right - left - 1;
            return;             
        }

        base.WndProc(ref m);
    }
}
like image 191
Dan W Avatar answered Nov 12 '22 17:11

Dan W


The improved answer from DarkPh03n1X was almost working for me, however it has a nasty bug: if a delimiter character could not be found, Text.IndexOf(c, start) will return -1 which will set right to -1 and then if (right == -1) right = Text.Length triggers.

So now we have selected until the end of the text, even though the expected selection should be shorter. The start is handled correctly I think.

I have removed if (right == -1) right = Text.Length but added && pos != -1. Here is the fixed version:

class RichTextBoxX : RichTextBox
{
    // implement selection to work with "whole words" on double-click 
    // and without selecting the leading/trailing spaces/blanks/line breaks
    private char[] delimiterList = new[] { '\n', ',', ' ', '(', ')', '_', '/' };

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x0203) // WM_LBUTTONDBLCLK
        {
            int start = SelectionStart;
            if (start < 1) start = 1;

            int left = -1;
            int right = Text.Length;
            int pos;

            foreach (char c in delimiterList)
            {
                pos = Text.LastIndexOf(c, start - 1);
                if (pos > left) left = pos;

                pos = Text.IndexOf(c, start);
                if (pos < right && pos != -1) right = pos;
            }

            SelectionStart = left + 1;
            SelectionLength = right - left - 1;
            return;
        }

        base.WndProc(ref m);
    }
}

To verify the behavour, here is the sample text I was using:

12.34.56.78  (ab1-2-3-4-5.test-1.example.com)
Jersey City
New Jersey
US, United States
ASN: Example.com/12345

I have added a few other delimiter, feel free to choose what you need.

like image 32
Eugen Avatar answered Nov 12 '22 16:11

Eugen