static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source) { VisualLine vl = textView.GetVisualLine(pos.Line); if (vl == null) { return(EMPTY_RECT); } // this may happen during layout changes in AvalonDock, so we just return an empty rectangle // in those cases. It should be refreshed immediately. if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) { return(EMPTY_RECT); } TextLine line = vl.GetTextLine(pos.VisualColumn); Rect displayRect; // calculate the display rect for the current character if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) { displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle; displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop)); } else { // if we are in virtual space, we just use one wide-space as character width displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop), new Size(textView.WideSpaceWidth, textView.DefaultLineHeight)); } // adjust to current scrolling displayRect.Offset(-textView.ScrollOffset); return(textView .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual .TransformToDevice(source.RootVisual)); // rect on HWND }