WPF RichTextBox with no width set

0 votes
asked Dec 8, 2008 by michael

I have the following XAML code:

<Window x:Class="RichText_Wrapping.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Grid>
    <RichTextBox Height="100" Margin="2" Name="richTextBox1">
        <FlowDocument>
            <Paragraph>
                This is a RichTextBox - if you don't specify a width, the text appears in a single column
            </Paragraph>
        </FlowDocument>
    </RichTextBox>
</Grid>

... If you create this window in XAML, you can see that when you don't specify a width for the window, it wraps the text in a single column, one letter at a time. Is there something I'm missing? If it's a known deficiency in the control, is there any workaround?

7 Answers

0 votes
answered Dec 8, 2008 by artur-carvalho

I copy pasted your code and its not in a single column, Do you have a width somewhere that is small? Maybe defined on the code behind for instance.

0 votes
answered Dec 3, 2009 by azazeal

Try binding the FlowDocument's width (one way) to the width of the container RichTextBox.

Worked for me...

0 votes
answered Dec 8, 2009 by tom-deleu

Hmm I have the same issue. Setting a width on the RichEdit fixed the 1 letter column. But when there is no width set, it displays one letter per line. I also set the background color to something very flashy, to see the control takes the whole width of the page. (but with only one column in it.

Hmmm

0 votes
answered Dec 14, 2010 by lambinator

This is a confirmed bug with the WPF RichTextBox. To fix it, Bind the PageWidth of the FlowDocument to the RichTextBox width, i.e.

<RichTextBox Name="rtb">
    <FlowDocument Name="rtbFlowDoc" PageWidth="{Binding ElementName=rtb, Path=ActualWidth}" />
</RichTextBox>

EDIT: Give the FlowDocument a name so that you can access it in the code behind and never new the flow document in codebehind.

0 votes
answered Dec 8, 2012 by arolson101

The approach in this article worked for me:

WPF RichTextBox doesn't provide the functionality to adjust its width to the text. As far as I know, RichTextBox use a FlowDocumentView in its visual tree to render the Flowdocument. It will take the available space to render its content, so it won't adjust its size to the content. Since this is an internal class, it seems we cannot override the layout process to let a RichTextBox to adjust its size to the text.

Therefore, I think your approach is in the right direction. Unfortunelately, based on my research, there is no straightforward way to measure the size of the rendered text in a RichTextBox.

There is a workaround we can try. We can loop through the flowdocument in RichTextBox recursively to retrieve all Run and Paragraph objects. Then we convert them into FormattedText to get the size.

This article demonstrates how to convert a FlowDocument to FormattedText. I also write a simple sample using the FlowDocumentExtensions class in that article.

    public Window2()
    {
      InitializeComponent();

      StackPanel layoutRoot = new StackPanel();
      RichTextBox myRichTextBox = new RichTextBox() { Width=20};

      this.Content = layoutRoot;
      layoutRoot.Children.Add(myRichTextBox);

      myRichTextBox.Focus();
      myRichTextBox.TextChanged += new TextChangedEventHandler((o,e)=>myRichTextBox.Width=myRichTextBox.Document.GetFormattedText().WidthIncludingTrailingWhitespace+20);
    }


  public static class FlowDocumentExtensions
  {
    private static IEnumerable<TextElement> GetRunsAndParagraphs(FlowDocument doc)
    {
      for (TextPointer position = doc.ContentStart;
        position != null && position.CompareTo(doc.ContentEnd) <= 0;
        position = position.GetNextContextPosition(LogicalDirection.Forward))
      {
        if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd)
        {
          Run run = position.Parent as Run;

          if (run != null)
          {
            yield return run;
          }
          else
          {
            Paragraph para = position.Parent as Paragraph;

            if (para != null)
            {
              yield return para;
            }
          }
        }
      }
    }

    public static FormattedText GetFormattedText(this FlowDocument doc)
    {
      if (doc == null)
      {
        throw new ArgumentNullException("doc");
      }

      FormattedText output = new FormattedText(
        GetText(doc),
        CultureInfo.CurrentCulture,
        doc.FlowDirection,
        new Typeface(doc.FontFamily, doc.FontStyle, doc.FontWeight, doc.FontStretch),
        doc.FontSize,
        doc.Foreground);

      int offset = 0;

      foreach (TextElement el in GetRunsAndParagraphs(doc))
      {
        Run run = el as Run;

        if (run != null)
        {
          int count = run.Text.Length;

          output.SetFontFamily(run.FontFamily, offset, count);
          output.SetFontStyle(run.FontStyle, offset, count);
          output.SetFontWeight(run.FontWeight, offset, count);
          output.SetFontSize(run.FontSize, offset, count);
          output.SetForegroundBrush(run.Foreground, offset, count);
          output.SetFontStretch(run.FontStretch, offset, count);
          output.SetTextDecorations(run.TextDecorations, offset, count);

          offset += count;
        }
        else
        {
          offset += Environment.NewLine.Length;
        }
      }

      return output;
    }

    private static string GetText(FlowDocument doc)
    {
      StringBuilder sb = new StringBuilder();

      foreach (TextElement el in GetRunsAndParagraphs(doc))
      {
        Run run = el as Run;
        sb.Append(run == null ? Environment.NewLine : run.Text);
      }
      return sb.ToString();
    }
  }
0 votes
answered Dec 25, 2014 by patrickbadley

I noticed that I only had this issue when my default ScrollViewer style explicitly set HorizontalScrollBarVisibility=Hidden. Removing this setter (default value is Hidden anyway) fixed the single column issue for me in my RichTextBox.

0 votes
answered Sep 15, 2017 by vev

Just for the record as I think this thread is missing some explanations as per the why: RichTextBox MeasureOverride implementation is like that. I won't call that a bug, maybe just a poor design behavior justified by the fact that just like mentioned above the FlowDocument is not cheap to measure due to its complexity. Bottom line, avoid unlimited Width constraint by binding MinWidth or wrap it in a limiting container.

    /// <summary>
    /// Measurement override. Implement your size-to-content logic here.
    /// </summary>
    /// <param name="constraint">
    /// Sizing constraint.
    /// </param>
    protected override Size MeasureOverride(Size constraint)
    {
        if (constraint.Width == Double.PositiveInfinity)
        {
            // If we're sized to infinity, we won't behave the same way TextBox does under
            // the same conditions.  So, we fake it.
            constraint.Width = this.MinWidth;
        }
        return base.MeasureOverride(constraint);
    }
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...