Multi-Page Document Generation with XPS Part I: The Problem

Here is a sample of the multi-page xps document generation code. I tried a wide variety of techniques, some more elegant than others.

Method 1: Single Xps.DocumentWriter.Write(Visual)

This approach is by far the most elegant and would be perfect if it actually worked as I expect it to. However, everything that I am seeing is that this will not automatically paginate the visual that you pass into your document.

Package package = Package.Open(documentStream, FileMode.Create);
XpsDocument xpsDoc = new XpsDocument(package);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);

xpsWriter.Write(element);
xpsDoc.Close();
package.Close();

 

Result: All content displays correctly but appears to run off the page. The document is always a single page.

Method 2: Manual FixedPage Document Construction

This technique is probably the most work in terms of code generation. It uses the structures that you will actually see in an XPS document’s XAML file. Manually constructing these is obviously very tedious and error prone, therefore not an ideal solution. Nevertheless, I wanted to try it out to see if I could break through the 1 page barrier.

#region FixedPage Sequence
Package package = Package.Open(documentStream, FileMode.Create);
XpsDocument xpsDoc = new XpsDocument(package);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);

// create the empty XPS Document objects
FixedDocumentSequence fixedDocSeq =
    new FixedDocumentSequence();

#region Page 1
DocumentReference docRef1 = new DocumentReference();
FixedDocument fixedDoc1 = new FixedDocument();
PageContent pageContent1 = new PageContent();
FixedPage fixedPage1 = new FixedPage();

// add the visual object the FixedPage
element.AllContent.Children.Remove(element.Page1Content);
fixedPage1.Children.Add(element.Page1Content);

// add the FixedPage to the PageContent collection
pageContent1.BeginInit();
((IAddChild)pageContent1).AddChild(fixedPage1);
pageContent1.EndInit();

// add the page content to the document reference collection
((IAddChild)fixedDoc1).AddChild(pageContent1);
docRef1.BeginInit();
docRef1.SetDocument(fixedDoc1);
docRef1.EndInit();
#endregion

#region Page 2
DocumentReference docRef2 = new DocumentReference();
FixedDocument fixedDoc2 = new FixedDocument();
PageContent pageContent2 = new PageContent();
FixedPage fixedPage2 = new FixedPage();

element.AllContent.Children.Remove(element.Page2Content);
// add the visual object the FixedPage
fixedPage1.Children.Add(element.Page2Content);

// add the FixedPage to the PageContent collection
pageContent1.BeginInit();
((IAddChild)pageContent2).AddChild(fixedPage2);
pageContent1.EndInit();

// add the page content to the document reference collection
((IAddChild)fixedDoc2).AddChild(pageContent2);
docRef2.BeginInit();
docRef2.SetDocument(fixedDoc2);
docRef2.EndInit();
#endregion

// add the document reference to the FixedDocumentSequence
(fixedDocSeq as IAddChild).AddChild(docRef1);
(fixedDocSeq as IAddChild).AddChild(docRef2);

// write the fixed document sequence as an XPS Document
// using the XpsDocumentWriter created earlier
xpsWriter.Write(fixedDocSeq);

// and close the document
xpsDoc.Close();
package.Close();
#endregion

 

Result: This technique resulted in the content from page 1 and page 2 overlapping each other on the first page of the document, with the second page remaining blank. At least we got a second page this time! But why is the content not being rendered there?

Method 3: VisualsToXpsDocument batch writes

This is a little more elegant than Method #2 but still not as clean as the single operation approach of Method #1. According to the documentation that I found, this is the recommended approach for multi-page document generation.

Package package = Package.Open(documentStream, FileMode.Create);
XpsDocument xpsDoc = new XpsDocument(package);
XpsDocumentWriter xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
VisualsToXpsDocument visWriter = xpsWriter.CreateVisualsCollator() as VisualsToXpsDocument;

List<UIElement> elementsToWrite = new List<UIElement>();

FrameworkElement page1 = element.Page1Content;
element.AllContent.Children.Remove(page1);

FrameworkElement page2 = element.Page2Content;
element.AllContent.Children.Remove(page2);

UserControl uc1 = new UserControl();
uc1.DataContext = element.DataContext;
uc1.Content = page1;
uc1.UpdateLayout();

UserControl uc2 = new UserControl();
uc2.DataContext = element.DataContext;
uc2.Content = new Rectangle() { Fill = Brushes.Red, Width = 100, Height = 100 };
uc2.UpdateLayout();

elementsToWrite.Add(uc1);
elementsToWrite.Add(uc2);
visWriter.BeginBatchWrite();
foreach (UIElement subElement in elementsToWrite)
{
    visWriter.Write(subElement);
}
visWriter.EndBatchWrite();

xpsDoc.Close();
package.Close();

 

Result: With this approach my first page was pristine—all the data bindings fired and the layout was accurate but again, from the 2nd page and on we get nothing but blank white pages.

THE PROBLEM: How do I get more than one page to render?

THE SOLUTION: Coming Up…

6 thoughts on “Multi-Page Document Generation with XPS Part I: The Problem

  1. Method 2 goes haywire because you are using the wrong FixedPage:

    fixedPage1.Children.Add(element.Page2Content);

    should be:

    fixedPage2.Children.Add(element.Page2Content);

    There are a number of subsequent locations in the code where “1” was not correctly replaced with “2”.

    Like

  2. Method 3 WILL work for page 2 and on, if you will run this method per UIElement:
    subElement.Arrange(new Rect(new Size(subElement.ActualWidth, subElement.ActualHeight)));
    visWriter.Write(subElement);

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s