Создайте пользовательский FrameworkContentElement для добавления диагональной линии над текстом в WPF
Есть ли способ создать кастом FrameworkContentElement
(или Inline
) что рисует диагональную линию над своим содержимым?
Что-то наподобие зачистного украшения, но диагональной формы:
Невозможно присущи от TextDecoration
или же TextEffect
(они опечатаны).
Любая идея?
3 ответа
ОБНОВЛЕНИЕ:
Я попытался создать пример как можно меньше. В более сложных сценариях вам придется расширить это. Вот как это выглядит:
это соответствующий xaml:
<AdornerDecorator>
<StackPanel>
<TextBlock>
<Run>this is normal Text</Run><LineBreak/>
<Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run><LineBreak/>
<Run>more normal text yeah</Run>
</TextBlock>
<FlowDocumentScrollViewer>
<FlowDocument>
<Paragraph>
<Run>this is normal Text</Run>
<LineBreak/>
<Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run>
<LineBreak/>
<Run>more normal text yeah</Run>
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</StackPanel>
</AdornerDecorator>
и вот код:
public class DiagonalStrikeThroughAdorner : Adorner
{
private readonly Inline _inline;
private readonly Pen _pen;
public DiagonalStrikeThroughAdorner(UIElement adornedElement, Inline inline, Brush brush) : base(adornedElement)
{
_inline = inline;
_pen = new Pen(brush, 2);
}
protected override void OnRender(DrawingContext drawingContext)
{
if(!(_inline.ContentStart.HasValidLayout && _inline.ContentEnd.HasValidLayout))
return;
var startrect = _inline.ContentStart.GetCharacterRect(LogicalDirection.Forward);
var endrect = _inline.ContentEnd.GetCharacterRect(LogicalDirection.Backward);
drawingContext.DrawLine(_pen,startrect.BottomLeft,endrect.TopRight);
}
public static Brush GetStrikeThroughBrush(DependencyObject obj)
{
return (Brush)obj.GetValue(StrikeThroughBrushProperty);
}
public static void SetStrikeThroughBrush(DependencyObject obj, Brush value)
{
obj.SetValue(StrikeThroughBrushProperty, value);
}
public static readonly DependencyProperty StrikeThroughBrushProperty =
DependencyProperty.RegisterAttached("StrikeThroughBrush", typeof(Brush), typeof(DiagonalStrikeThroughAdorner), new UIPropertyMetadata((o, args) =>
{
if(!(o is TextElement)) return;
var parent = ((TextElement)o).Parent;
while (parent is FrameworkContentElement)
parent = ((FrameworkContentElement) parent).Parent;
if (parent == null || !(parent is Visual)) return;
var adornerLayer = AdornerLayer.GetAdornerLayer((Visual) parent);
if(adornerLayer == null) return;
adornerLayer.Add(new DiagonalStrikeThroughAdorner((UIElement) parent,o as Inline,(Brush) args.NewValue));
}));
}
повеселись!
исходное сообщение:
это вообще довольно сложно. Мне удалось прикрепить рекламное объявление к конкретным элементам в flowdocuments, но есть много угловых случаев для рассмотрения. например: что должно произойти, если этот Inline обернут? далее: если этот потоковый документ находится в текстовом поле richtextbox, его внутренние компоненты продолжают перестраивать прогоны (объединяя или разделяя их), что в значительной степени портит все. Вы должны все тщательно настроить.
Пожалуйста, опишите подробнее, где эта строка будет находиться. Внутри FlowdocumentScrollviewer? Или текстовый блок? Или Richtextbox? Поскольку вы должны прикрепить рекламное объявление к управляющему FrameworkElement (как вы, вероятно, уже заметили, вы не можете напрямую прикрепить рекламное устройство к FrameworkContentElement), нам нужно знать, где находится встроенный элемент.
Я опишу общий путь для достижения этой цели: создайте прикрепленное свойство, которое будет создавать рекламодателя. прикрепленное свойство установлено на встроенном объекте, который будет украшен. владелец сохраняет ссылку на встроенный элемент и присоединяется к управляющему элементу FrameworkElement. подпишитесь на макет, обновленный на этом элементе фрейма, и создайте InvalidateVisual на Adorner. Украшающие OnRender рисуют линию с координатами в зависимости от прямоугольников Inlines ContentStart и ContentEnd GetCharacterRect. сделанный.
<TextBlock>
<Run>this is normal Text</Run><LineBreak/>
<Run local:DiagonalStrikeThroughAdorner.StrikeThroughBrush="Red">Some text with diagonal decoration</Run><LineBreak/>
<Run>more normal text yeah</Run>
</TextBlock>
Адорнер не покажет, потому что
var adornerLayer = AdornerLayer.GetAdornerLayer((Visual) parent);
if(adornerLayer == null)
всегда возвращать ноль, мы должны установить присоединенное свойство после того, как Run is Loaded.
Поместите ваш текст в Canvas или Grid (то, что позволяет элементам управления перекрываться) и добавьте объект Line с его точками X/Y, привязанными к вашей позиции TextBlock
Что-то вроде этого:
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Yellow">
<TextBlock x:Name="TestText" Text="This is a Test" />
<Line X1="{Binding ElementName=TestText, Path=ActualWidth}"
Y1="{Binding ElementName=TestText, Path=ActualHeight}"
X2="{Binding ElementName=TestText, Path=Canvas.Left}"
Y2="{Binding ElementName=TestText, Path=Canvas.Top}"
Stroke="Red" StrokeThickness="2" />
</Canvas>