Creating a custom FrameworkContentElement to add a diagonal line in text in WPF

Is there a way to create a custom FrameworkContentElement (or Inline ) that draws a diagonal line above its content?

Something like Strike-through, but with a diagonal shape: example of strike-through and diagonal decorations

Impossibly inherent from TextDecoration or TextEffect (they are sealed).

Any idea?

+6
decorator wpf inline custom-controls effects
source share
3 answers

UPDATE

I tried to create an example as minimal as possible. In more complex scenarios you will have to expand this. Here's what it looks like:

enter image description here

this is the corresponding 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> 

and what codebehind:

 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)); })); } 

enjoy!

original post :

it is usually quite complicated. I managed to attach adorner to certain elements in flowdocuments, but there are many viewing angles. for example: what should happen if this inline is wrapped? further: if this flowdocument is in a richtextbox, its internal elements retain the permutation of the runs (combining or splitting them), which will pretty much confuse everyone. you need to sort it out carefully.

Please provide details on where this built-in line will be. Inside a FlowdocumentScrollviewer? Or TextBlock? Or Richtextbox? Since you have to attach adorner to the managing FrameworkElement (as you probably already noticed that you cannot directly attach Adorner to FrameworkContentElement), we need to know where the inline is sitting.

I will describe a general route for how to do this: create an attached property that adorner is going to create. the attached property is set to the inline to be decorated. adorner holds an inline reference and attaches to the FrameworkElement control. subscibe to layoutupdated on this framework element and make InvalidateVisual on Adorner. The OnRender essays draw a line with coordinates depending on the Inlines ContentStart and ContentEnd GetCharacterRect . done.

+5
source share
 <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> 

Adorner will not be displayed because

 var adornerLayer = AdornerLayer.GetAdornerLayer((Visual) parent); if(adornerLayer == null) 

always returns null, we must set the attached property after loading Run.

+1
source share

Place the text on the canvas or grid (which allows you to overlap the controls) and add a Line object with its X / Y points anchored to your TextBlock position.

Something like that:

 <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> 
0
source share

All Articles