// Point is specified relative to the given visual public static TextPointer ScreenPointToTextPointer(this FlowDocument document, Point screenPoint) { try { // 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); var charsBeforePoint = rangeProvider?.GetText(int.MaxValue)?.Length ?? 0; // 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 (var i = 0; i < 10; i++) // Limit to 10 adjustments { var error = charsBeforePoint - new TextRange(document.ContentStart, pointer).Text.Length; if (error == 0) { break; } pointer = pointer?.GetPositionAtOffset(error); } return(pointer); } catch (Exception e) { Console.WriteLine(e); return(null); } }
// Point is specified relative to the given visual public static TextPointer ScreenPointToTextPointer(this FlowDocument document, Point screenPoint) { try { // Get text before point using automation var peer = new DocumentAutomationPeer(document); var textProvider = (ITextProvider)peer.GetPattern(PatternInterface.Text); var rangeProvider = textProvider.RangeFromPoint(screenPoint); // Try to use reflection to make retrieval of the pointer much faster var member = rangeProvider.GetType().GetField("_start", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (null != member) { var result = (TextPointer)member.GetValue(rangeProvider); return(result); } else { // if reflection failed, we have to use the hard way 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); // this is only a first guess // Adjust for difference between "text offset" and actual number of characters before pointer // Note that this is time consuming and can take some seconds (!) for big documents for (int i = 0; i < 12; i++) // Limit to 12 adjustments { int error = charsBeforePoint - new TextRange(document.ContentStart, pointer).Text.Length; if (error == 0) { break; } pointer = pointer.GetPositionAtOffset(error); // try to get the pointer iteratively } return(pointer); } } catch (System.Exception ex) { return(null); } }