我想知道用户在FlowDocument中点击了这个词。
我目前正在为文档中的每个Run添加一个事件处理程序,并在单击的Run中迭代TextPointers,在每个Run上调用GetCharacterRect()并检查矩形是否包含该点。
然而,当在长期运行结束附近发生点击时,这需要> 10秒。
有没有更有效的方法?
答案 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,它将您的本地鼠标点转换为全局点。