Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF RichTextBox with no width set

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?

like image 581
Michael Avatar asked Dec 08 '08 20:12

Michael


5 Answers

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.

like image 85
lambinator Avatar answered Nov 19 '22 20:11

lambinator


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

Worked for me...

like image 10
azazeal Avatar answered Nov 19 '22 19:11

azazeal


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();
    }
  }
like image 7
arolson101 Avatar answered Nov 19 '22 19:11

arolson101


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.

like image 3
Artur Carvalho Avatar answered Nov 19 '22 19:11

Artur Carvalho


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.

like image 3
patrickbadley Avatar answered Nov 19 '22 20:11

patrickbadley