.net 如何从FlowDocument中的鼠标单击中获取文本指针

.net 如何从FlowDocument中的鼠标单击中获取文本指针,.net,wpf,flowdocument,.net,Wpf,Flowdocument,我想获取用户在FlowDocument中单击的单词 我目前正在为文档中的每个运行添加一个事件处理程序,并在单击的运行中遍历TextPoints,对每个运行调用GetCharacterRect(),并检查矩形是否包含该点 但是,当点击在长距离运行接近结束时,这需要>10秒 有没有更有效的方法?我认为最简单的方法是使用自动化界面: using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; Flo

我想获取用户在FlowDocument中单击的单词

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

但是,当点击在长距离运行接近结束时,这需要>10秒


有没有更有效的方法?

我认为最简单的方法是使用自动化界面:

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也需要使用它的一些方法

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

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比我想象的要困难

为了便于使用,我将算法打包在扩展方法
screenpointotextpointer
中。下面是一个示例,说明如何使用我的扩展方法加粗鼠标指针之前的所有文本,并取消加粗鼠标指针之后的所有文本:

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;
  }

}
使用System.Windows.Automation.Peers;
使用System.Windows.Automation.Provider;
使用System.Windows.Automation.Text;
公共静态类文档扩展
{
//点是相对于给定的视觉对象指定的
公共静态文本指针屏幕点到文本指针(此流程文档,点屏幕点)
{
//使用自动化在点之前获取文本
var peer=新文档自动对等(文档);
var textProvider=(ITextProvider)peer.GetPattern(PatternInterface.Text);
var rangeProvider=textProvider.RangeFromPoint(屏幕点);
rangeProvider.MoveEndpointByUnit(TextPatternRangeEndpoint.Start,TextUnit.Document,1);
int charsBeforePoint=rangeProvider.GetText(int.MaxValue).Length;
//查找与TextPointer对应的指针
var指针=document.ContentStart.GetPositionAtOffset(charsBeforePoint);
//调整“文本偏移”和指针前的实际字符数之间的差异

对于(int i=0;i鼠标单击事件会冒泡到顶部,您只需在文档中挂起PreviewMouseLeftButtonUp并查看事件的发送者/原始源,您将获得发送事件的运行

然后你可以从点开始,使用


指向将本地鼠标点转换为全局点的屏幕。

如果流文档是RichTextBox的流文档,则可以使用该方法获取文本指针。

我已经可以轻松获取运行。问题是获取最接近单击的文本指针。运行源于TextElement,它具有ContentStart和ContentEnd which可能是最近的TextPointer,您正在运行中查找TextPointer吗?是的。我需要确切的TextPointer,以便我可以找出单击的单词并提供其他信息(字典查找)关于这个词。谢谢你的有用更新。不过,请参阅我对Ray答案的评论。这似乎是一个有趣的解决方案。RangeFromPoint方法似乎需要屏幕中的一个点(绝对值)与鼠标事件提供的相对坐标相对的坐标。如何转换坐标?此解决方案有两个问题:1.range.GetText()正在返回一个零长度字符串。2.如果我得到了一个字符串,我如何知道字符串中的哪个单词对应于鼠标单击的位置?问得好。我在回答中添加了工作代码,精确显示了如何执行所有这些操作。谢谢!正是我所需要的。