wpftextblockfixeddocumentline-count

WPF TextBlock get lines after textwrapping


I have FixedDocument page and I want to place TextBlock on it, but it can be that Textblock doesn't fit on page by height.
So I want to take lines from generated TextBlock with TextWrapping, and then create new TextBlock, that fitted by height and place it on page.
TextBlock have LineCount private property, that mean that it has TextLines after wrapping and I can somehow get it.
Creating TextBlock with runs:

public TextItem(PageType pageType, Run[] runs, Typeface typeFace, double fontSize)
        : base(pageType)
{
     this.TextBlock = new TextBlock();
     this.TextBlock.Inlines.AddRange(runs);
     if (typeFace != null)
          this.TextBlock.FontFamily = typeFace.FontFamily;

     if (fontSize > 0)
           this.TextBlock.FontSize = fontSize;
     this.TextBlock.TextWrapping = TextWrapping.Wrap;   //wrapping
}

Creating TextBlock with text:

public TextItem(PageType pageType, String text, Typeface typeFace, double fontSize)
        : base(pageType)
{
    if (typeFace == null || fontSize == 0)
        throw new Exception("Wrong textitem parameters");

    this.TextBlock = new TextBlock();
    this.TextBlock.Text = text;
    this.TextBlock.FontFamily = typeFace.FontFamily;
    this.TextBlock.FontSize = fontSize;
    this.TextBlock.TextWrapping = TextWrapping.Wrap;
    this.TextBlock.TextAlignment = TextAlignment.Justify;

    this.TypeFace = typeFace;
}

Set width to TextBlock and get DesiredSize :

this.TextBlock.Width = document.CurrentPage.Content.ActualWidth;
this.TextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

Solution

  • I faced the exactly same problem, and for some time, I lost the hope and I thought there is no solution for this.
    BUT, I was wrong, there are many solutions for that (At least three)
    And you are right, one of them use the LineCount property by using reflection.
    And the second using it is own algorithm to get the lines.
    And the third, which is preferred to me, has very elegant way to get the result which you want.

    Please refer to this question, to see the three answers of this.
    Get the lines of the TextBlock according to the TextWrapping property?


    Here is a copy of the best solution (in my opinion)

    public static class TextUtils
    {
        public static IEnumerable<string> GetLines(this TextBlock source)
        {
            var text = source.Text;
            int offset = 0;
            TextPointer lineStart = source.ContentStart.GetPositionAtOffset(1, LogicalDirection.Forward);
            do
            {
                TextPointer lineEnd = lineStart != null ? lineStart.GetLineStartPosition(1) : null;
                int length = lineEnd != null ? lineStart.GetOffsetToPosition(lineEnd) : text.Length - offset;
                yield return text.Substring(offset, length);
                offset += length;
                lineStart = lineEnd;
            }
            while (lineStart != null);
        }
    }