views:

1247

answers:

2

I am working in C# and VS2008.

I have a WPF application containing a FlowDocument, which contains a paragraph, which contains a number of fairly short lines (i.e. Spans and LineBreaks). However the length of lines varies. I would like the FlowDocument's width to be large enough to accommodate the longest of these lines without wrapping, but no wider so as to not waste space.

Putting it into a grid column with Width=Auto doesn't work: the FlowDocument always consumes the max width allowed, since the FlowDocument can validly wrap to suit a number of different widths.

I could go through the lines as I generate them in code, and work out a width for each one, but I see no Width property on the Span or Run classes.

Any ideas on how to automatically or manually set a width for this document?


I used AndrewS's suggested idea and moved from a FlowDocument to a StackPanel of labels each containing a line. The XAML Markup is

<Border BorderThickness="2" Margin="2" BorderBrush="Black">
    <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
        <StackPanel 
         Orientation="Vertical"
            x:Name="itemsStackPanel" />
    </ScrollViewer>
</Border>

The code creates spans and adds them as follows:

        Label itemLabel = new Label();
        // pad left and right, keep close top and bottom
        itemLabel.Padding = new Thickness(2,0,2,0);
        itemLabel.Content = contentSpan;
        this.itemsStackPanel.Children.Add(itemLabel);

Sizing is perfect now, and the only snag is that the font doesn't look exactly the same as the FlowDocument next to it, despite the font Family and Font Size being the same (Segoe UI, 15pt), and that the Labels look much further apart than the lines in the FlowDocument. I have asked this as a separate question

+1  A: 

You could replace the lines with a different type of element such as TextBlocks or Labels (for which you can control wrapping) or Glyphs which "are designed for fixed-format document presentation and print scenarios".

I really don't think you want to try measuring lines and forcing the document width.

AndrewS
+1  A: 

I've got a similar situation, but I do want to measure the line width. Sadly, the RichTextBox has two important features bundled together - text foreground and background colors in runs and formatting. I need the colors and could care less about the formatting. I've got a "grid" of characters and I want to run several "cursors" over the characters. I plan on implementing the background colors as the individual cursors and use foreground colors for syntax coloring. It's a fixed width font and I don't want the grid to wrap so essentially no formatting. I suppose I could implement this as a grid that I set the rows and columns at runtime according to how big my text is, but that seems like a real waste of resources - especially if the file is really large - i.e., a 100x100 grid with a separate label in every cell. Originally, and perhaps a little naively, I thought that it would be a snap with RichTextBox but I'm beginning to believe otherwise. Another possibility is to put a canvas on top of a standard TextBox and drawing rects myself but having to deal with everything as stuff scrolls, etc. sounds ugly and error prone to me. My code right now is:

    private static void SetRichText(RichTextBox rtb, string[] arLines)
    {
        Contract.Assert(arLines.All(l => l.Length == arLines[0].Length));
        FlowDocument doc = new FlowDocument { PageWidth = 65535.0 };

        IEnumerable<Paragraph> iep = arLines.Select(l =>
                                            {
                                                Paragraph p = new Paragraph();
                                                p.Inlines.Add(new Run(l));
                                                return p;
                                            });
        foreach (Paragraph p in iep)
        {
            doc.Blocks.Add(p);
        }

        rtb.Document = doc;
        doc.PagePadding = new Thickness(0, 0, 0, 0);

        Rect rcEOL = rtb.Document.ContentStart.GetPositionAtOffset(arLines[0].Length).GetCharacterRect(LogicalDirection.Forward);
        // TODO : Figure why I have to add a fudge factor in the following (I'm guessing some margin I'm unaware of in the 
        // RichTextBox somewhere)...
        rtb.Document.PageWidth = rcEOL.Right + 18;
    }

As I state in the comments, this seems to work for my current case, but I haven't really tested it with many others so I'm not sure whether it works in general, plus it's just a big kludge anyway that I can't explain so I'd like to know how to handle this correctly. Thanks for any responses. Oh, I should point out as I stated above, that the for is fixed pitch and all the incoming strings are the same length as enforced by the contract so all lines should be exactly the same length.

Darrell Plank