给定像素偏移量计算字符串中字符索引的最佳方法

时间:2011-09-09 01:48:46

标签: c# winforms string textbox measure

相关问题:Getting a string index based on a pixel offset

我知道这很接近这个问题,但这不是问如何直接做,而是询问如何最好地伪造它。


我正在为Windows Forms实现我自己的文本框(因为RichTextBox很糟糕),我正在尝试找到最好的方法,给定在屏幕上绘制的字符串,计算鼠标结束的字符。问题是字符可以是可变宽度。

我想出了两种可能性:

  1. 每次鼠标移动时Graphics.MeasureCharacterRange 以鼠标移动的方式以二进制搜索方式移动<(如顶部链接的问题所示)

  2. 保留每行每个字符的偏移列表。

  3. (1)表现不佳,

    (2)将是内存效率低的加上使键入一个字符成为一个O(n)操作(因为你必须调整它后面的每个字符的偏移量) plus 不能完全做到,因为Graphics.MeasureCharacterRange不准确(它为一个字符返回一个值,为另一个字符返回另一个值,并且在一个字符串中将两个值完全不同的值[不等于前面添加的两个值]]。例如W宽16像素,f宽5像素,但Wf宽20像素。这些数字来自实际测试。)。

    所以我正在寻找一个更好的策略来做到这一点,最好是需要最小空间和O(1)计算复杂度的策略(尽管我很乐意用一点点内存效率来提高速度效率)。

1 个答案:

答案 0 :(得分:1)

我认为你不必做O(1)。 O(1)假设每个附加字符对所有先前字符都有影响,而不是。充其量我会看到每个单词的O(1)应该是疯狂的。听起来你需要的是一种存储方式; 1每个单词的位置,每个单词2个,单词中每个字母的宽度为3个。这将显着减少存储并提高查找速度。也许是这样的:

IEnumerable<TextLocation> TextLocations = ...;

internal class TextLocation
{
    public RectF BoundingBox { get; set; }  //this is relative to the textbox
    public TextWord TextWord { get; set; }
}

internal class TextWord
{
    public string Text { get; set; }
    public IEnumerable<LetterInfo> Letters { get; set; }
}

internal class LetterInfo
{
    public char Letter { get; set; }
    public float left { get; set; }  //these would be relative to the bounding box
    public float right { get; set; } //not to the textbox
}

然后你可能会做类似

的事情
var tl = TextLocations.FirstOrDefault(x => x.BoundingBox.Left < Mouse.X 
                                           && x.BoundingBox.Right > Mouse.X
                                           && x.BoundingBox.Top < Mouse.Y
                                           && x.BoundingBox.Bottom > Mouse.Y)

if (tl != null)
{
    //tl.TextWord.Text is the Word ("The", "Lazy", "Dog"...)

    var letter = tl.TextWord.Letters
                   .FirstOrDefault(x => Mouse.x - tl.BoundingBox.left > x.left
                                        Mouse.x - tl.BoundingBox.left < x.right);

    if (letter != null)
    {
        // you get the idea
    }                              
}