【问题标题】:What's a good way to perform hit testing on a FormattedText?在 FormattedText 上执行命中测试的好方法是什么?
【发布时间】:2009-02-12 04:19:24
【问题描述】:

我正在使用FormattedText 呈现文本,但似乎有任何方法可以对呈现的输出执行每个字符的命中测试。它是只读的,所以我基本上只需要选择,不需要编辑。

我会使用RichTextBox 或类似的,但我需要根据嵌入在文本本身中的控制代码输出文本,因此它们并不总是嵌套,这使得构建正确的内联元素非常复杂。我也有点担心该解决方案的性能;我有很多行,并且经常追加新行。

我看过GlyphRun,看来我可以从它或相关类中进行命中测试,但我会重新实现很多功能,而且似乎应该有一个更简单的方法.. .

有人知道实现这个的好方法吗?

【问题讨论】:

    标签: c# wpf text hittest


    【解决方案1】:

    您可以从 FormattedText 对象中获取每个字符的几何形状,并使用每个字符的边界来进行命中测试。

    var geometry = (GeometryGroup)((GeometryGroup)text.BuildGeometry(new Point(0, 0))).Children[0];
    foreach (var c in geometry.Children)
    {
      if (c.Bounds.Contains(point))
        return index;
      index++;
    }
    

    在 OnRender 中,您可以渲染这些几何对象而不是格式化文本。

    【讨论】:

    • 指出这一点你就是救命稻草。我开始考虑如何解决没有这个问题。
    • 我认为这个解决方案有问题。空格字符没有几何形状,因此几何形状中实际上存在“孔”,并且索引因命中点之前出现的空格数量而异。
    • 我认为你只是想获得几何的边界而不是使用实际的几何本身。
    【解决方案2】:

    最好的方法是设计一个好的数据结构来存储你的文本,同时还要考虑命中测试。一个示例可能是将文本拆分为块(单词、行或段落,具体取决于您的需要)。然后每个这样的块应该有一个边界框,应该在任何格式化操作中重新计算。还要考虑设计中的插入符号位置。

    一旦有了这样的工具,就可以很容易地进行命中测试,只需使用边界框即可。它还将有助于后续操作,例如突出显示文本的特定部分。

    【讨论】:

    • FormattedText 已经允许我获取文本范围的框,但我需要实际选择;所以我可能需要每个字符的盒子,这很快就会变得一团糟。这是一个可能的解决方案,但不是一个很好的解决方案......
    • 有一个fly-weight或其他一些设计模式可以解决类似的问题(bbox for single char)。你能查一下关于设计模式的 Gamma 书吗?它在里面。
    【解决方案3】:

    完全同意 Sesh - 不重新实现全部 FormattedText 功能的最简单方法是将要进行命中测试的单个项目拆分为它们自己的控件/内联。

    考虑使用 TextBlock 并将每个单词添加为它自己的内联(或),然后绑定到内联的 IsMouseDirectlyOver 属性,我们将委托添加到 MouseEnter 和 MouseLeave 事件。

    如果您想对实际字形进行像素级命中测试(即鼠标是否恰好位于此“i”的点中),那么您需要使用 GlyphRuns 并对字形进行手动命中测试 (阅读:努力工作)。

    【讨论】:

    • 或者只使用字形为整行生成边界框...点击测试。不知所措。
    【解决方案4】:

    我参加聚会很晚了——如果聚会没有结束,而且你不需要实际的角色几何图形,我发现这样的东西很有用:

     for (int i = 0; i < FormattedText.Text.Length; i++)
     {
                characterHighlightGeometry = FormattedText.BuildHighlightGeometry(new Point(), i, 1);
                CharacterHighlightGeometries.Children.Add(characterHighlightGeometry);
     }
    

    BuildGeometry() 仅包含角色的实际路径几何。 BuildHighlightGeometry() 生成所有字符的外部边界——包括 空间,因此可以通过以下方式找到空间的索引:

     foreach (var c in CharacterHighlightGeometries.Children)
            {
                if (c.Bounds.Contains(centerpoint))
                {
                    q = c;
                    cpos = index;
                    break;
                }
                index++;
            }
    

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多