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

4

12

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?

Claudicant answered 28/6, 2011 at 14:31 Comment(2)
Was my answer of any use at all or is this question a dead question?Ishmaelite
Your answer is what I would do if I wanted to roll my own kind of thing. I was looking for approaches geared towards an event driven, context sensitive way to have custom conditions for what text to highlight on a double click. Apparently there isn't a way to do this.Claudicant
L
4

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);
    }
}
Lir answered 20/4, 2015 at 23:5 Comment(2)
how do i get multiple delimiters , can you give a example with a char[] ?Restitution
If this isn't working for anyone else replace the base.WndProc(ref m); at the end with else base.WndProc(ref m);Magnuson
P
4

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.

Persson answered 30/7, 2017 at 23:19 Comment(0)
M
3

I use Regex \w which equal to [a-zA-Z0-9_] to replace TextBox selection

private void TextBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    string text = TextBox1.SelectedText;
    var strA = Regex.Match(text, @"\w+");
    int indexA = TextBox1.SelectionStart + text.IndexOf(strA.Value);
    TextBox1.Select(indexA, strA.Value.Length);
}

original result:

This is a "sample"sentence

with method above

This is a "sample" sentence

Muttonchops answered 18/10, 2018 at 21:0 Comment(1)
Very nice. Looking for a way to do the same but all text "between spaces" (before and after the original selection).Trutko
R
1

Adding on to DanW's Work i have added multiple delimiters, that seems to work out nicely.

class RichTextBoxX : RichTextBox
{
    private char[] delimiterList = new[] { ',', ' '};

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

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

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

                if (this.Text.IndexOf(c, start) < right)
                {
                    right = this.Text.IndexOf(c, start);
                    if (right == -1) right = Text.Length;
                }
            }

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

        base.WndProc(ref m);
    }
}

One more thing: to implement paste the code above into your form's "code" that you are going to use it on,now use the "Form Designer" to drag and drop a RichTextBox onto your form, now go to the form's designer class in my case form1.desigener.cs and find the implementation string eg:

this.richTextBox1 = new System.Windows.Forms.RichTextBox();

and replace it with

this.richTextBox1 = new project.form1.RichTextBoxX();

After this that control will work like a normal RichTextBox implementation with the extra overide feature

Restitution answered 1/12, 2016 at 14:52 Comment(1)
Careful, see Eugen's answer above.Lir

© 2022 - 2024 — McMap. All rights reserved.