1

I use this example ( https://siderite.dev/blog/how-to-draw-outlined-text-in-wpf-and.html ) for add outline to a text in a TextBlock. However this example don't support Inlines.

I try to add this ability modifying the OnRender and I iterate the Inlines collection to build a Geometry for each Inline block. The problem is that I need to get the position and size of the inline block in the TextBlock instead of those of the whole text in the TextBlock.

This is the source code that I have modified.

protected override void OnRender(DrawingContext drawingContext)
{
    ensureTextBlock();
    base.OnRender(drawingContext);

    foreach (Inline inline in _textBlock.Inlines)
    {
        var textRange = new TextRange(inline.ContentStart, inline.ContentEnd);
        var formattedText = new FormattedText(
            textRange.Text,
            CultureInfo.CurrentUICulture,
            inline.FlowDirection,
            new Typeface(inline.FontFamily, inline.FontStyle, inline.FontWeight, inline.FontStretch),
            inline.FontSize, System.Windows.Media.Brushes.Black // This brush does not matter since we use the geometry of the text.
        );
        formattedText.SetTextDecorations(inline.TextDecorations);


        //*****************************************************************
        //This part get the size and position of the TextBlock
        // Instead I need to find a way to get the size and position of the Inline block

        //formattedText.TextAlignment = _textBlock.TextAlignment;
        //formattedText.Trimming = _textBlock.TextTrimming;

        formattedText.LineHeight = _textBlock.LineHeight;
        formattedText.MaxTextWidth = _textBlock.ActualWidth - _textBlock.Padding.Left - _textBlock.Padding.Right;
        formattedText.MaxTextHeight = _textBlock.ActualHeight - _textBlock.Padding.Top;// - _textBlock.Padding.Bottom;
        while (formattedText.Extent == double.NegativeInfinity)
        {
            formattedText.MaxTextHeight++;
        }

        // Build the geometry object that represents the text.
        var _textGeometry = formattedText.BuildGeometry(new System.Windows.Point(_textBlock.Padding.Left, _textBlock.Padding.Top));

        //*****************************************************************


        var textPen = new System.Windows.Media.Pen(Stroke, StrokeThickness * 2)
        {
            DashCap = PenLineCap.Round,
            EndLineCap = PenLineCap.Round,
            LineJoin = PenLineJoin.Round,
            StartLineCap = PenLineCap.Round
        };

        var boundsGeo = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight));

        _clipGeometry = Geometry.Combine(boundsGeo, _textGeometry, GeometryCombineMode.Exclude, null);
        drawingContext.PushClip(_clipGeometry);
        drawingContext.DrawGeometry(System.Windows.Media.Brushes.Transparent, textPen, _textGeometry);
        drawingContext.Pop();
    }
}

I need to change the part that get the size and position from the TextBlock in order to get the size and position of the Inline block, but I don't see any property of Inline that can get that information. Any idea?

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
user2272143
  • 469
  • 5
  • 22

3 Answers3

1

I have found a way to get the position with:

inline.ElementStart.GetCharacterRect(LogicalDirection.Forward).Left
inline.ElementStart.GetCharacterRect(LogicalDirection.Forward).Top

Then I changed the line that build the geometry in this way:

double posLeft = inline.ElementStart.GetCharacterRect(LogicalDirection.Forward).Left;
double posTop = inline.ElementStart.GetCharacterRect(LogicalDirection.Forward).Top;

var _textGeometry = formattedText.BuildGeometry(new System.Windows.Point(posLeft, posTop));

I still have some problem with wrap because when the text is wrapped the Geometry is built only in the first line.

user2272143
  • 469
  • 5
  • 22
  • This needs to be an update to your question and not posted as an answer. It'll get voted down and possibly removed. – Michael Puckett II Nov 28 '18 at 21:13
  • @MichaelPuckettII This answer to my question: how get the position of Inline? inline.ElementStart.GetCharacterRect(LogicalDirection.Forward) I still have problem with wrapped text but this is beyond the goal of this question and I think I will open a new question for this point. – user2272143 Nov 28 '18 at 23:46
  • Copy that; to prevent confusion I might remove the last tidbit where it says you're still having an issue. I believe I understand you correctly in that you did answer the question so the extra may leave it to leaving unanswered (which is what I originally assumed myself) – Michael Puckett II Nov 28 '18 at 23:50
  • Well, I have add that last information because if some person are looking just for my original question they get the answer but in case someone is trying to do the same thing with outlined text then he is warned that this code still have that problem. But I get your point and now I'm not sure if I have to leave it or not. Probably I'll clarify it later but since I still work on this problem I'll update my answer later if I have some better news. Anyway thanks for your suggestion. – user2272143 Nov 29 '18 at 00:00
0

Try this

Rect rect = Rect.Union(inline.ElementStart.GetCharacterRect(LogicalDirection.Forward),                                                
                       inline.ElementEnd.GetCharacterRect(LogicalDirection.Backward));

Thanx

zznobody
  • 186
  • 2
  • 3
-1

Use the LineHeight property of the TextBlock:

formattedText.LineHeight = _textBlock.LineHeight;

So you can modify the LineHeight value programmatically.

sherl.lol
  • 11
  • 2
  • Thanks for your answer, however the problem is to get the size and position of the inline block not only the LineHeight. Each Inline block have a different size and position inside the TextBlock. – user2272143 Nov 28 '18 at 15:31
  • Try separating them into different vars, may be that would make any difference. – sherl.lol Nov 28 '18 at 15:33