如何从FlowDocument中的鼠标单击获取TextPointer [英] How Can I Get a TextPointer from a mouse click in a FlowDocument

查看:33
本文介绍了如何从FlowDocument中的鼠标单击获取TextPointer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想得到用户在FlowDocument中单击的词.

I would like to get the word that a user has clicked on in a FlowDocument.

我目前正在向文档中的每个Run添加一个事件处理程序,并在单击的Run中遍历TextPointers,在每个Run上调用GetCharacterRect()并检查矩形是否包含该点.

I am currently adding an event handler to every Run in the document and iterating through the TextPointers in the Run that was clicked, calling GetCharacterRect() on each one and checking if the rectangle contains the point.

但是,当在长时间运行结束时发生点击时,这需要10秒钟以上的时间.

However, when the click occurs near the end of a long Run this takes > 10 seconds.

有没有更有效的方法?

推荐答案

我会说最简单的方法是使用自动化接口:

I'd say the easiest way is to use the Automation interfaces:

using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;

FlowDocument flowDocument = ...;
Point point = ...;

var peer = new DocumentAutomationPeer(flowDocument);
var textProvider = (ITextProvider)peer.GetPattern(PatternInterface.Text);
var rangeProvider = textProvider.RangeFromPoint(point);

ITextProvider的用法需要引用UIAutomationProvider程序集.该程序集通常不被引用,因此您可能需要添加它.要使用其某些方法,还需要UIAutomationTypes.

The ITextProvider usage requires a reference to the UIAutomationProvider assembly. This assembly is not commonly referenced, so you may need to add it. UIAutomationTypes will also be needed to use some of its methods.

请注意,根据您呈现FlowDocument的方式,有许多用于创建自动化对等项的选项:

Note that there are many options for creating your automation peer depending on how you are presenting the FlowDocument:

var peer = new DocumentAutomationPeer(flowDocument);
var peer = new DocumentAutomationPeer(textBlock);
var peer = new DocumentAutomationPeer(flowDocumentScrollViewer);
var peer = new TextBoxAutomationPeer(textBox);
var peer = new RichTextBoxAutomationPeer(richTextBox);

更新

尽管从ITextRangeProvider转换为TextPointer证明比我想象的要困难,但我尝试了这一点,并且效果很好.

I tried this and it works well, though converting from an ITextRangeProvider to a TextPointer proved more difficult than I expected.

我将算法打包在扩展方法 ScreenPointToTextPointer 中,以方便使用.这是一个示例示例,该示例说明了如何使用我的扩展方法将鼠标指针之前的所有文本加粗,然后将其之后的所有文本不加粗:

I packaged the algorithm in an extension method ScreenPointToTextPointer for easy use. Here is an example of how my extension method can be used to bold all text before the mouse pointer and un-bold all text after it:

private void Window_MouseMove(object sender, MouseEventArgs e)
{
  var document = this.Viewer.Document;
  var screenPoint = PointToScreen(e.GetPosition(this));

  TextPointer pointer = document.ScreenPointToTextPointer(screenPoint);

  new TextRange(document.ContentStart, pointer).ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
  new TextRange(pointer, document.ContentEnd).ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
}

这是扩展方法的代码:

using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Automation.Text;

public static class DocumentExtensions
{
  // Point is specified relative to the given visual
  public static TextPointer ScreenPointToTextPointer(this FlowDocument document, Point screenPoint)
  {
    // Get text before point using automation
    var peer = new DocumentAutomationPeer(document);
    var textProvider = (ITextProvider)peer.GetPattern(PatternInterface.Text);
    var rangeProvider = textProvider.RangeFromPoint(screenPoint);
    rangeProvider.MoveEndpointByUnit(TextPatternRangeEndpoint.Start, TextUnit.Document, 1);
    int charsBeforePoint = rangeProvider.GetText(int.MaxValue).Length;

    // Find the pointer that corresponds to the TextPointer
    var pointer = document.ContentStart.GetPositionAtOffset(charsBeforePoint);

    // Adjust for difference between "text offset" and actual number of characters before pointer
    for(int i=0; i<10; i++)  // Limit to 10 adjustments
    {
      int error = charsBeforePoint - new TextRange(document.ContentStart, pointer).Text.Length;
      if(error==0) break;
      pointer = pointer.GetPositionAtOffset(error);
    }
    return pointer;
  }

}

还请注意在示例MouseMove方法中使用PointToScreen来获取要传递到扩展方法中的屏幕点.

Also note the use of PointToScreen in the example MouseMove method to get a screen point to pass into the extension method.

这篇关于如何从FlowDocument中的鼠标单击获取TextPointer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆