Why the GetCharIndexFromPosition() doesn't return properly for last character of textbox?

后端 未结 2 1757
太阳男子
太阳男子 2021-01-16 14:10
public partial class Form1 : Form
{
    TextBox textBox;
    public Form1()
    {
        InitializeComponent();
        textBox = new TextBox() { Height = 30, Width         


        
相关标签:
2条回答
  • 2021-01-16 14:33

    This ended up really bothering me, so I expanded Ivan Stoev's idea to a rather overengineered method that calculates the pixel width of the last character and divides it by two to accurately emulate the same behaviour as on the other characters.

    The method was written for a drag/drop scenario, where the selection is adjusted to the drop position while hovering the mouse over.

    // Cached, so it doesn't get recalculated on each moved pixel.
    private Char _textBoxLastChar = '\0';
    private Int32 _textBoxLastCharHalfWidth = 0;
    
    private void TextBox_DragOver(object sender, DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(DataFormats.UnicodeText))
            return;
        TextBox tb = sender as TextBox;
        if (tb == null)
            return;
        Int32 textLen = tb.Text.Length;
        if (textLen > 0 && _textBoxLastChar != tb.Text[textLen - 1])
        {
            _textBoxLastChar = tb.Text[textLen - 1];
            _textBoxLastCharHalfWidth = (Int32)Math.Round(GetStringWidth(_textBoxLastChar.ToString(), tb.Font) / 2);
        }
        Point localPoint = tb.PointToClient(new Point(e.X, e.Y));
        Int32 index = tb.GetCharIndexFromPosition(localPoint);
        // fix for fact it returns the last char position when you go outside text bounds.
        Int32 charPosition = tb.GetPositionFromCharIndex(index).X;
        if (textLen != 0 && index == textLen - 1 && localPoint.X > charPosition + _textBoxLastCharHalfWidth)
            index++;
        if (!tb.Focused)
            tb.Focus();
        tb.SelectionStart = index;
        tb.SelectionLength = 0;
    }
    
    public static Double GetStringWidth(String text, Font f)
    {
        //create a bmp / graphic to use MeasureString on
        Single areaSize = f.Size * 20;
        using (Bitmap b = new Bitmap(1, 1))
        using (Graphics g = Graphics.FromImage(b))
        {
            SizeF sizeOfString = g.MeasureString(text, f, new SizeF(areaSize, areaSize), StringFormat.GenericTypographic);
            return sizeOfString.Width;
        }
    }
    

    Of course, if you ever change the font or font size of the text box, you'll have to reset _textBoxLastChar back to '\0'.

    0 讨论(0)
  • 2021-01-16 14:38

    This is a known documented behavior. The Remarks section of the GetCharIndexFromPosition method documentation contains the following Important note:

    If the specified location is not within the client rectangle of the control, or is beyond the last character in the control, the return value is the index of the last character.

    The workaround is to use the reverse method GetPositionFromCharIndex to adjust the returned index.

    Something like this

    void textBox_MouseMove(object sender, MouseEventArgs e)
    {
        var charIndex = textBox.GetCharIndexFromPosition(e.Location);
        var charPosition = textBox.GetPositionFromCharIndex(charIndex);
        if (e.Location.X > charPosition.X) charIndex++;
        textBox.Select(charIndex, 0);
    }
    

    P.S. As a side note, I have no idea what this method is trying to achieve, but for sure it prevents the standard text selection by mouse behavior.

    0 讨论(0)
提交回复
热议问题