// 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);
            }
        }
Ejemplo n.º 2
0
        // 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);
            }
        }