Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetCharIndexFromPosition() for the last char

Tags:

c#

.net

winforms

I want to get the sub-string under cursor in a RichTextBox.

private void richTextBox1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Clicks == 1 && e.Button == MouseButtons.Left)
    {
        string wholeText = richTextBox1.Text;
        // Obtain the character index where the user clicks on the control. 
        int positionBegin = richTextBox1.GetCharIndexFromPosition(new Point(e.X, e.Y));
        SelectedText = wholeText.Substring(positionBegin);
    }

For example, if I type the string World then place the cursor between l and d, the sub-string should be d. Till now my code is working, however if I place the cursor at the end of the string, it still returns d.

I expect that it will return the empty then in this case. It looks like a bug. See http://www.pcreview.co.uk/forums/problem-getcharindexfromposition-t4037504.html


1 Answers

How is it a bug? If you look at the discription, it gets the closest character. The closest character to the position you click is indeed the last letter. I haven't seen the source, but based on the description, it is working as intended.

In my brief look at the methods available to the RichTextBox I didn't see one that would resolve the issue easily. Something you could do is pad the ending of the text with an actual character, eg: a space.

//...
string originalText = richTextBox1.Text;
richTextBox1.Text += " ";

//Your code to get the index and substring

//Return the textbox to its original state
richTextBox1.Text = originalText;
richTextBox1.SelectionLength = 0;
richTextBox1.SelectionStart = positionBegin;

That should do what you are looking for, but it is rather simple and will be inefficient depending on the amount of text you're dealing with. If you wanted to create a method that functions like the GetCharIndexFromPosition(Point pt), you will probably end up in the unmanaged code realm.


EDIT: Because I was curious (Extension method solution)

I started looking at the source code of the RichTextBox control and it does indeed stop you from selecting a character outside the max length of the text length as seen here:

public override int GetCharIndexFromPosition(Point pt) {
    NativeMethods.POINT wpt = new NativeMethods.POINT(pt.X, pt.Y);
    int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.EM_CHARFROMPOS, 0, wpt);

    string t = this.Text;
    //This bit is stopping you from getting an index outside the length of the text
    if (index >= t.Length) {
        index = Math.Max(t.Length - 1, 0);
    }
    return index;
}

To "fix" this you can make a extension method that implements it the same way but remove the bit about checking to see if the index is larger than the length of the text:

public static class RichTextBoxExtensions
{
    private const int EM_CHARFROMPOS = 0x00D7;

    public static int GetTrueIndexPositionFromPoint(this RichTextBox rtb, Point pt)
    {
        POINT wpt = new POINT(pt.X, pt.Y);
        int index = (int)SendMessage(new HandleRef(rtb, rtb.Handle), EM_CHARFROMPOS, 0, wpt);

        return index;
    }

    [DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, POINT lParam);
}

[StructLayout(LayoutKind.Sequential)]
internal class POINT
{
    public int x;
    public int y;

    public POINT()
    {
    }

    public POINT(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

With this you are back to your original code! Just change GetCharIndexFromPosition to GetTrueIndexPositionFromPoint and it will work as expected.

like image 164
Justin Avatar answered Dec 07 '25 19:12

Justin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!