FlowDocument PageWidth, PagePadding and ColumnWidth Oh My!

The ability to create a living, breathing, document right inside a WPF UI is pretty incredible to me. Even after working with it for the last year or so, I am still impressed by the power of this largely forgotten feature of WPF.

One thing to remember about the FlowDocument model is that this is not your Mama’s WPF. The FlowDocument objects inherit from FrameworkContentElement, not FrameworkElement as the rest of the WPF control suite does. So, while the document related controls do inherit the goodness of DependencyObject and can support DependencyProperties etc., they miss the features added by UIElement and FrameworkElement which include little things like Visibility, Height and Width.

frameworkElement

frameworkContentElement

FlowDocument elements are meant to, well … flow. They fill the available page width, or the width made available by whatever parent they are constrained by such as a TableCell or Paragraph. So, while FrameworkContentElements may support layout specific properties such as Margin and Padding, if you want to get explicit about Width and Height you are either out of luck, going to embed some UIElements in your document (you cad!) or about to write some custom code to make it happen.

When you insert a FlowDocument into a WPF window you do have some ability to affect the layout of the document as a whole at the top level. So, while a Paragraph does not have a Width or Height property, the FlowDocument does its best to act like a real document and gives you PageWidth, PagePadding and ColumnWidth properties that will affect the overall layout and container size of your document.

PageWidth: this, as it indicates, is the width of the document page. The amount is set in device independent pixels (a pixel is 1/96 of an inch so 1” = 96 pixels). Keep in mind, when setting this value, that the page margin must also be considered.

PagePadding: this name, that is much more fitting in WPF then in document land, is actually the page margin. The amount of pixels (1/96 of an inch) between the edge of the paper and the content. So basically, PagePadding + PageWidth should equal, or at least not be greater than, the paper width. If you have 8.5” wide paper (816 pixels) and you have 1/2 margins (48 pixels * 2 = 96) then you only have 720 pixels to play with for PageWidth. PagePadding is of Type Thickness, so you can set a uniform value that applies to all Margins, or set each separately if desired.

ColumnWidth: This one is not related to the size of the container as much as it is how the content within the container is laid out. As the name indicates, it sets the desired width of the columns of the document. It is only desired since, by default, the layout will adjust the ColumnWidth to make best use of the available width of the page. To enforce your column width setting you need to set IsColumnWidthFlexible = False.

Another gotcha to keep in mind with column width is that the calculation of columns is based on “perceived pressure” of the container. So, just like ScrollBars in normal WPF windows, if you wrap your content in a control that assumes infinite space such as  a ScrollViewer, StackPanel, or FlowDocumentScrollViewer the content inside will feel no need to form up columns since it thinks it has infinity to render itself in.

The best way to get a feel for how these properties interact and affect each other is to play around with the options and view the results in the UI. To help with that I have included a sample WPF Window that allows you to dynamically change these values and get real time feedback.

The window xaml:

   1: <Window x:Class="FlowPageLayout"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="FlowPageLayout"
   5:      Width="900">
   6:     <Window.Resources>
   7:         <Style TargetType="TextBox">
   8:             <Setter Property="Width" Value="50" />
   9:             <Setter Property="Margin" Value="10,0,15,0" />
  10:         </Style>
  11:     </Window.Resources>
  12:     
  13:     <DockPanel>
  14:         <StackPanel Orientation="Horizontal"
  15:                     DockPanel.Dock="Top">
  16:             <TextBlock Text="Reader (Paper) Width" />
  17:             <TextBox Text="816" Name="ReaderWidthText" LostFocus="ReaderWidthText_LostFocus"  />
  18:             <TextBlock Text="PageWidth (Pixels)" />
  19:             <TextBox Text="720" Name="PageWidthText" />
  20:             <TextBlock Text="PagePadding (Pixels)" />
  21:             <TextBox Text="48" Name="PagePaddingText" />
  22:             <TextBlock Text="ColumnWidth (Pixels)" />
  23:             <TextBox Text="100" Name="ColumnWidthText" />
  24:             <TextBlock Text="IsColumnWidthFlexible" />
  25:             <CheckBox IsChecked="True" Name="FlexibleChecked" />
  26:         </StackPanel>
  27:             <FlowDocumentReader Name="Reader" 
  28:                                 BorderBrush="Black" 
  29:                                 BorderThickness="1" 
  30:                                 Width="{Binding ElementName=ReaderWidthText, Path=Text, UpdateSourceTrigger=LostFocus}"
  31:                                 Margin="20">
  32:                 <FlowDocument Name="MainDocument"  
  33:                                       PageWidth="{Binding ElementName=PageWidthText, Path=Text}" 
  34:                                       PagePadding="{Binding ElementName=PagePaddingText, Path=Text}"
  35:                                       ColumnWidth="{Binding ElementName=ColumnWidthText, Path=Text}"
  36:                                       IsColumnWidthFlexible="{Binding ElementName=FlexibleChecked, Path=IsChecked}" 
  37:                                       FontSize="10" 
  38:                                       FontFamily="Arial" 
  39:                                       Background="White" >
  40:  
  41:                     <Paragraph >
  42:                         Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dignissim accumsan purus, ut faucibus turpis ultricies vel. Phasellus quis orci eu massa vulputate placerat. Nunc congue varius lacus in tincidunt. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec ut lorem ante, sed faucibus ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pharetra, mauris vitae mollis congue, tortor libero sollicitudin justo, id posuere arcu arcu sed lorem.
  43:                     </Paragraph>
  44:  
  45:                     <Paragraph>
  46:                         Phasellus molestie dui id lorem cursus luctus. Nulla molestie urna vulputate est sodales sed tempus leo posuere. Suspendisse pharetra, odio et eleifend pulvinar, enim felis faucibus lectus, sit amet consequat turpis magna eget nunc. Aenean eget urna quis justo vestibulum rutrum vel non ligula. Donec blandit dolor quis orci vulputate pulvinar blandit diam dignissim. Nam nec sapien magna. Phasellus faucibus urna eu quam fermentum mollis. Quisque sit amet dolor nulla, eget vehicula nulla.
  47:                     </Paragraph>
  48:                     <Paragraph>
  49:                         Nam magna metus, vulputate nec condimentum non, pellentesque in ante. Etiam et enim vel massa semper luctus ac ut quam. Nulla libero nisi, molestie eu lacinia ac, tincidunt non mi. Etiam ornare mi lobortis nunc adipiscing non interdum metus pretium. Morbi felis nulla, dignissim ut blandit vestibulum, euismod ut ipsum. Mauris interdum commodo nisi, vitae pharetra lacus scelerisque eget. Aliquam vel neque nisi. Vivamus consequat scelerisque dignissim. Maecenas lobortis suscipit massa, quis feugiat tortor pulvinar vel. Proin sagittis felis sit amet nunc dignissim semper. Quisque lacinia, tellus sed placerat sagittis, velit justo consectetur est, id aliquam nunc est ac nisl. Curabitur convallis elementum ligula eu ullamcorper.
  50:                     </Paragraph>
  51:  
  52:                     <Paragraph>
  53:                         Suspendisse ac hendrerit enim. Curabitur non lacus sapien, a consequat magna. Mauris tempor, eros ac mollis dapibus, sem lacus facilisis mi, et tristique arcu felis quis ipsum. Nam lobortis, nisl at commodo feugiat, risus enim bibendum odio, quis feugiat libero neque nec quam. Nullam auctor, nibh in sodales adipiscing, tortor odio tristique diam, at molestie lorem eros eget tortor. Cras at velit et lectus malesuada faucibus id sit amet augue. Aliquam rutrum turpis velit. Nunc sit amet faucibus erat.
  54:                     </Paragraph>
  55:  
  56:                     <Paragraph>
  57:                         Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eu elit a ipsum malesuada imperdiet vitae nec libero. Donec sagittis quam vitae leo consequat id rutrum lacus rutrum. Nullam euismod, velit sit amet porta malesuada, sem libero tempor leo, egestas dignissim diam dolor faucibus massa. Suspendisse vel urna leo, sed vulputate dui. Pellentesque rutrum auctor dolor, blandit lobortis ligula hendrerit eget. In laoreet porttitor mollis. Nam et ligula est. Proin vel nisl quam. Donec posuere, sem a venenatis commodo, lorem massa pulvinar nisi, eget porta quam tellus nec turpis. Donec fringilla tincidunt nibh ut porttitor. Ut lacinia justo nulla. Maecenas ac nisi iaculis metus hendrerit tempus ac vel ante. Aliquam nec erat enim. Aenean turpis turpis, fringilla et auctor id, ullamcorper eget velit. Duis et dui odio, eget tincidunt metus.
  58:                     </Paragraph>
  59:  
  60:                     <Paragraph>
  61:                         Fusce aliquam nisi nec felis aliquam convallis. Pellentesque laoreet molestie nibh, nec cursus sem tempor eu. Donec sit amet purus orci. Pellentesque vehicula neque nec orci pulvinar elementum. Curabitur massa tellus, imperdiet sit amet rutrum ut, posuere porttitor urna. Aliquam condimentum lobortis felis. In sed feugiat ante. Praesent at diam nibh, sit amet blandit lorem. Nam consequat tellus non arcu porta aliquet. Vivamus orci ante, elementum vitae cursus eget, pulvinar nec mi. Curabitur eu lorem justo. Ut facilisis fringilla metus id tristique.
  62:                     </Paragraph>
  63:  
  64:                     <Paragraph>
  65:                         Aliquam sed nibh odio, et cursus augue. Aliquam tempor scelerisque eros, tempor scelerisque orci eleifend quis. Nullam vehicula nunc tortor. Donec id suscipit tellus. Etiam venenatis, ipsum vel porttitor porttitor, mi dui consectetur purus, at consequat nunc elit eget elit. Cras nec nibh sit amet ligula ullamcorper imperdiet. Sed hendrerit tempus eleifend. Sed blandit aliquet pretium. Ut porttitor, lacus quis pharetra imperdiet, neque nisl mollis velit, a dapibus erat lectus posuere turpis. In cursus rutrum erat. Nam mauris nisi, posuere quis mollis non, scelerisque ac sem. Phasellus vitae orci velit, id auctor quam.
  66:                     </Paragraph>
  67:                 </FlowDocument>
  68:  
  69:             </FlowDocumentReader>
  70:     </DockPanel>
  71: </Window>

The code behind that resizes the window to stay somewhat in line with the “paper” width

   1: Public Class FlowPageLayout
   2:  
   3:     Private Sub ReaderWidthText_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
   4:         If Me.Reader Is Nothing Then Exit Sub
   5:         Me.Width = Me.Reader.Width + 80
   6:     End Sub
   7:  
   8: End Class

The window allows changing the key properties in real time to test various combinations of settings. The FlowDocumentReader has a border defined, and a margin as well to help isolate the edge of the “paper” and make the document margin more visible.

flowLayoutWindow

No Comments