Я сделал нечто подобное, только подчеркнув текст в TextBox. Принципал, кажется, в основном тот же.
Добавьте AdornerDecorator, содержащий ваш RichTextBox, но внутри ScrollViewer.
<Border ...>
<ScrollViewer ... >
<AdornerDecorator>
<RichTextBox
x:Name="superMagic"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
BorderBrush="{x:Null}"
BorderThickness="0"
...
/>
</AdornerDecorator>
</ScrollViewer>
</Border>
Создать Adorner для рендеринга прямоугольника и добавить его в AdornerLayer
void HostControl_Loaded(object sender, RoutedEventArgs e)
{
_adorner = new RectangleAdorner(superMagic);
AdornerLayer layer = AdornerLayer.GetAdornerLayer(superMagic);
layer.Add(_adorner);
}
Обладатель должен перехватить событие TextChanged RichTextBox. Все, что вам нужно сделать, это вызвать InvalidateVisuals()
через диспетчера, используя DispatcherPriority.Background
, чтобы убедиться, что он отображается после текстового поля. Я не знаю, является ли это проблемой для RichTextBox
, но получение координат символа из TextBox
возможно только в том случае, если оно было визуализировано хотя бы один раз с момента последнего изменения содержимого.
class RectangleAdorner : Adorner
{
public RectangleAdorner(RichTextBox textbox)
: base(textbox)
{
textbox.TextChanged += delegate
{
SignalInvalidate();
};
}
void SignalInvalidate()
{
RichTextBox box = (RichTextBox)this.AdornedElement;
box.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)InvalidateVisual);
}
// ...
}
Переопределите Adorner.OnRender()
, чтобы нарисовать прямоугольник, используя TextPointer.GetCharacterRect()
, чтобы получить координаты.
protected override void OnRender(DrawingContext drawingContext)
{
TextPointer start;
TextPointer end;
// Find the start and end of your word
// Actually, if you did this in the TextChanged event handler,
// you could probably save some calculation time on large texts
// by considering what actually changed relative to an earlier
// calculation. (TextChangedEventArgs includes a list of changes
// - 'n' characters inserted here, 'm' characters deleted there).
Rect startRect = start.GetCharacterRect(LogicalDirection.Backward);
Rect endRect = end.GetCharacterRect(LogicalDirection.Forward);
drawingContext.DrawRectangle(null, pen, Rect.Union(startRect, endRect));
}
Примечание: Хотя оригинальный код работал хорошо, я написал его давным-давно и не проверял свои приспособления для этого ответа. По крайней мере, это должно помочь вам встать на правильный путь.
Кроме того, это не относится к случаям, когда слово разбивается по строкам, но не должно быть слишком сложным для обслуживания.