private static void RemoveHyperlink(AutoCompleteRichTextBox myRichTextBox, TextPointer caretPosition) { var backspacePosition = caretPosition.GetNextInsertionPosition(LogicalDirection.Backward); Hyperlink hyperlink; if (backspacePosition == null || !DocumentUtils.IsHyperlinkBoundaryCrossed(caretPosition, backspacePosition, out hyperlink)) { return; } // Remember caretPosition with forward gravity. This is necessary since we are going to delete // the hyperlink element preceeding caretPosition and after deletion current caretPosition // (with backward gravity) will follow content preceeding the hyperlink. // We want to remember content following the hyperlink to set new caret position at. var newCaretPosition = caretPosition.GetPositionAtOffset(0, LogicalDirection.Forward); // Deleting the hyperlink is done using logic below. // 1. Copy its children Inline to a temporary array. var hyperlinkChildren = hyperlink.Inlines; var inlines = new Inline[hyperlinkChildren.Count]; hyperlinkChildren.CopyTo(inlines, 0); // 2. Remove each child from parent hyperlink element and insert it after the hyperlink. for (int i = inlines.Length - 1; i >= 0; i--) { hyperlinkChildren.Remove(inlines[i]); hyperlink.SiblingInlines.InsertAfter(hyperlink, inlines[i]); } // 3. Apply hyperlink's local formatting properties to inlines (which are now outside hyperlink scope). var localProperties = hyperlink.GetLocalValueEnumerator(); var inlineRange = new TextRange(inlines[0].ContentStart, inlines[inlines.Length - 1].ContentEnd); while (localProperties.MoveNext()) { var property = localProperties.Current; var dp = property.Property; object value = property.Value; if (!dp.ReadOnly && dp != Inline.TextDecorationsProperty && // Ignore hyperlink defaults. dp != TextElement.ForegroundProperty && dp != BaseUriHelper.BaseUriProperty && !DocumentUtils.IsHyperlinkProperty(dp)) { inlineRange.ApplyPropertyValue(dp, value); } } // 4. Delete the (empty) hyperlink element. hyperlink.SiblingInlines.Remove(hyperlink); // 5. Update selection, since we deleted Hyperlink element and caretPosition was at that Hyperlink's end boundary. myRichTextBox.Selection.Select(newCaretPosition, newCaretPosition); }
private void OnTextChanged(object sender, TextChangedEventArgs e) { if (!myWordsAddedFlag || Document == null) { return; } // Temporarily disable TextChanged event handler, since following code might insert Hyperlinks, // which will raise another TextChanged event. TextChanged -= OnTextChanged; var navigator = mySelectionStartPosition; while (navigator != null && navigator.CompareTo(mySelectionEndPosition) <= 0) { var wordRange = DocumentUtils.GetWordRange(navigator); if (wordRange == null || wordRange.IsEmpty) { // No more words in the document. break; } string wordText = wordRange.Text; if (IsHyperlink(wordText) && !DocumentUtils.IsInHyperlinkScope(wordRange.Start) && !DocumentUtils.IsInHyperlinkScope(wordRange.End)) { var hyperlink = new Hyperlink(wordRange.Start, wordRange.End); hyperlink.NavigateUri = new Uri(wordText.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? wordText : "http://" + wordText); WeakEventManager <Hyperlink, RequestNavigateEventArgs> .AddHandler(hyperlink, "RequestNavigate", OnHyperlinkRequestNavigate); navigator = hyperlink.ElementEnd.GetNextInsertionPosition(LogicalDirection.Forward); } else { navigator = wordRange.End.GetNextInsertionPosition(LogicalDirection.Forward); } } TextChanged += OnTextChanged; myWordsAddedFlag = false; mySelectionStartPosition = null; mySelectionEndPosition = null; }