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

后端 未结 2 1776
太阳男子
太阳男子 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'.

提交回复
热议问题