如何通过在FlowDocument中单击鼠标来获取TextPointer

时间:2010-06-05 20:05:01

标签: .net wpf flowdocument

我想知道用户在FlowDocument中点击了这个词。

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

然而,当在长期运行结束附近发生点击时,这需要> 10秒。

有没有更有效的方法?

3 个答案:

答案 0 :(得分:6)

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

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来使用它的一些方法。

请注意,根据您呈现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比我预期的要困难。

我将算法打包在扩展方法ScreenPointToTextPointer中以方便使用。下面是一个示例,说明如何使用我的扩展方法在鼠标指针之前加粗所有文本,并在其后面加上非粗体所有文本:

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来获取要传递到扩展方法的屏幕点。

答案 1 :(得分:1)

如果FlowDocument是RichTextBox的,则可以使用GetPositionFromPoint()方法获取TextPointer。

答案 2 :(得分:0)

鼠标点击事件被冒泡到顶部,而您只需在文档中挂钩PreviewMouseLeftButtonUp并观察事件的发件人/原始来源,您将获得向您发送事件的运行。

然后你可以使用RangeFromPoint,

PointToScreen,它将您的本地鼠标点转换为全局点。