He hecho algo similar, solo subrayando texto en un TextBox. El director parece ser más o menos el mismo.
Agregue un AdornerDecorator que contenga su RichTextBox pero dentro de ScrollViewer.
<Border ...>
<ScrollViewer ... >
<AdornerDecorator>
<RichTextBox
x:Name="superMagic"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
BorderBrush="{x:Null}"
BorderThickness="0"
...
/>
</AdornerDecorator>
</ScrollViewer>
</Border>
Crear Adorner para hacer que el rectángulo y añadirlo a la AdornerLayer
void HostControl_Loaded(object sender, RoutedEventArgs e)
{
_adorner = new RectangleAdorner(superMagic);
AdornerLayer layer = AdornerLayer.GetAdornerLayer(superMagic);
layer.Add(_adorner);
}
El adorner debe enlazar el evento TextChanged de RichTextBox. Todo lo que necesita hacer es llamar al InvalidateVisuals()
a través del despachador usando DispatcherPriority.Background
para asegurarse de que se represente después del cuadro de texto. No sé si es un problema para el RichTextBox
, pero obtener las coordenadas del personaje de un TextBox
solo es posible si se ha procesado al menos una vez desde que se modificó por última vez.
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);
}
// ...
}
Anulación Adorner.OnRender()
para dibujar el cuadro utilizando TextPointer.GetCharacterRect()
para obtener las coordenadas.
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));
}
Nota: Aunque el código original funcionó bien, lo escribí hace mucho tiempo y no he probado mis adaptaciones para esta respuesta. Al menos debería ayudarlo a ponerse en el camino correcto.
Además, esto no se aplica a los casos en que la palabra se divide entre líneas, pero no debería ser demasiado difícil de atender.
Genial, voy a probar esto. – Kugel
¡Funciona perfecto! :) – JanDotNet