WPF FAQ - Flow Content

How do I load a FlowDocument XAML content from outside the application directory?

Let us say you have your application running in a folder called C:\MyApp. Then if you want to load a FlowDocumentContent.xaml from a folder called C:\Contentthen this is easily done by first defining a FlowDocumentReader in your app like so:

 [XAML]
              <FlowDocumentReader Name='myFlowDocument' ViewingMode='Scroll'  IsTwoPageViewEnabled='False' IsPageViewEnabled='False'>
              </FlowDocumentReader>

And load the xaml file as follows:

 [C#]
            	FileStream xamlFile = new FileStream(filePath, FileMode.Open, FileAccess.Read);
	// This will work, only if the top-level element in the document is FlowDocument
            	FlowDocument content = XamlReader.Load(xamlFile) as FlowDocument;

	this.myFlowDocument.Document = content;

            	xamlFile.Close();

But if you have references to sub-folders in your document. For example, if you are referring to images under this folder C:\Content\Images, then those images wouldn’t be found by the parser because the parser is executing with the application directory context whereas the document contents are in a different directory. To make this work, you can force a context on the parser as follows:

 [C#]
            	ParserContext context = new ParserContext();
            	context.BaseUri = new Uri(filePath, UriKind.Absolute);
            	FlowDocument content = XamlReader.Load(xamlFile, context) as FlowDocument;
Permalink
How can I save the contents of a FlowDocumentReader as an XAML file ?

This example demonstrates how to save the contents of a FlowDocumentReader (represented by the ‘Document’ property) as an XAML file.

[XAML]

<FlowDocumentReader
Name='flowDocRdr'
IsFindEnabled='True'
IsPrintEnabled='True'
MinZoom='50' MaxZoom='1000'
Zoom='120' ZoomIncrement='5'
/>

To save the contents of the FlowDocumentReader to a file, open or create the file stream and use the ‘Save()’ method provided by the ‘XAMLWriter’ class to write the FlowDocument to the file stream.

[C#]

void SaveFlowDocumentReaderWithXAMLFile(string fileName)
{
// Open or create the output file.
FileStream xamlFile = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
// Save the contents of the FlowDocumentReader to the file stream that was just opened.
XamlWriter.Save(flowDocRdr.Document, xamlFile);

xamlFile.Close();
}
Permalink
How can I load an XAML file into a FlowDocumentReader?

This example demonstrates how to parse an XAML file that contains a FlowDocument and display the loaded file in a FlowDocumentReader.

[XAML]

<FlowDocumentReader
  Name='flowDocRdr' 
  IsFindEnabled='True'  
  IsPrintEnabled='True'
  MinZoom='50' MaxZoom='1000'
  Zoom='120' ZoomIncrement='5'
/>

And write the following FlowDocument to the file stream :

[C#]

void LoadFlowDocumentReaderWithXAMLFile(string fileName)
{
    // Open the file that contains the FlowDocument...
    FileStream xamlFile = new FileStream(fileName, FileMode.Open, FileAccess.Read);
    // and parse the file with the XamlReader.Load method.
    FlowDocument content = XamlReader.Load(xamlFile) as FlowDocument;
    // Finally, set the Document property to the FlowDocument object that was
    // parsed from the input file.
    flowDocRdr.Document = content;

    xamlFile.Close();
}
Permalink
How can I use the BlockUIContainer element to host UIElement objects within Flow content ?

A BlockUIContainer is a flow content element that allows UIElement to be hosted inside the flow content. This can be done as follows.

[XAML]

<FlowDocument ColumnWidth='400'>
  <Section Background='GhostWhite'>
    <Paragraph>
      A UIElement element may be embedded directly in flow content
      by enclosing it in a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <Button>Click me!</Button>
    </BlockUIContainer>
    <Paragraph>
      The BlockUIContainer element may host no more than one top-level
      UIElement.  However, other UIElements may be nested within the
      UIElement contained by an BlockUIContainer element.  For example,
      a StackPanel can be used to host multiple UIElement elements within
      a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <StackPanel>
        <Label Foreground='Blue'>Choose a value:</Label>
        <ComboBox>
          <ComboBoxItem IsSelected='True'>a</ComboBoxItem>
          <ComboBoxItem>b</ComboBoxItem>
          <ComboBoxItem>c</ComboBoxItem>
        </ComboBox>
        <Label Foreground ='Red'>Choose a value:</Label>
        <StackPanel>
          <RadioButton>x</RadioButton>
          <RadioButton>y</RadioButton>
          <RadioButton>z</RadioButton>
        </StackPanel>
        <Label>Enter a value:</Label>
        <TextBox>
          A text editor embedded in flow content.
        </TextBox>
      </StackPanel>
    </BlockUIContainer>
  </Section>
</FlowDocument>
Permalink
Can I embed a figure into a paragraph of text ?

Figure and Floater are used to embed content in Flow Documents with placement properties that can be customized independent of the primary content flow. Figure or Floater elements are often used to highlight or accentuate portions of content to host supporting images or other content within the main content flow or to inject loosely related content such as advertisements.

The following example shows how to embed a Figure into a paragraph of text.

[XAML]

<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>

  <Paragraph>
    <Figure 
      Width='300' Height='100' 
      Background='GhostWhite' HorizontalAnchor='PageLeft' >
      <Paragraph FontStyle='Italic' Background='Beige' Foreground='DarkGreen' >
        A Figure embeds content into flow content with placement properties 
        that can be customized independently from the primary content flow
      </Paragraph>
    </Figure>
  </Paragraph>

</FlowDocument>
Permalink
How can I define three paragraphs under one section ?

We can define three paragraphs under one section as follows.

In the following example, the section has a “Background” property value of Red, therefore the background color of the paragraphs is also red.

[XAML]

<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <!-- By default, Section applies no formatting to elements contained
       within it. However, in this example, the section has a Background
       property value of 'Red', therefore, the three paragraphs (the block)  
       inside the section also have a red background. -->
  <Section Background='Red'>
    <Paragraph>
      Paragraph 1
    </Paragraph>
    <Paragraph>
      Paragraph 2
    </Paragraph>
    <Paragraph>
      Paragraph 3
    </Paragraph>
  </Section>
</FlowDocument>
Permalink
How can I use MemoryStream to create a deep clone of a serializable object ?

This can be done as follows.

[C#]

public static class SerializationUtil
{
   static public T Clone<t>(T source)
   {
      Debug.Assert(typeof(T).IsSerializable);

      IGenericFormatter formatter = new GenericBinaryFormatter(); 
      Stream stream = new MemoryStream(); 
      formatter.Serialize(stream,source);
      stream.Seek(0,SeekOrigin.Begin);

      T clone = formatter.Deserialize<t>(stream);
      stream.Close(); 
      return clone;
   }
//Rest of SerializationUtil
}
</t></t>
Permalink
How can I store a given document in a specific format using CreateSerializerWriter methods ?

You can store a given document format using CreateSerializerWriter method as follows.

[C#]

// Create a SerializerProvider for accessing plug-in serializers.
SerializerProvider serializerProvider = new SerializerProvider();

// Locate the serializer that matches the fileName extension.
SerializerDescriptor selectedPlugIn = null;
foreach ( SerializerDescriptor serializerDescriptor in
                serializerProvider.InstalledSerializers )
{
    if ( serializerDescriptor.IsLoadable &&
         fileName.EndsWith(serializerDescriptor.DefaultFileExtension) )
    {   // The plug-in serializer and fileName extensions match.
        selectedPlugIn = serializerDescriptor;
        break; // foreach
}
}

// If a match for a plug-in serializer was found,
// use it to output and store the document.
if (selectedPlugIn != null)
{
    Stream package = File.Create(fileName);
    SerializerWriter serializerWriter =
        serializerProvider.CreateSerializerWriter(selectedPlugIn,
                                                  package);
    IDocumentPaginatorSource idoc =
        flowDocument as IDocumentPaginatorSource;
    serializerWriter.Write(idoc.DocumentPaginator, null);
    package.Close();
    return true;
}
Permalink