Convert WPF (XAML) Control to XPS Document
Asked Answered
D

1

50

Can I take an Existing WPF (XAML) Control, databind it and turn it into an XPS document that can be displayed and printed using the WPF XPS Document Viewer? If so, how? If not, how should I be doing ‘reporting’ in WPF using XPS/PDF/etc?

Basically I want to take an existing WPF control, databind it to get useful data into it and then make it printable and saveable for the end user. Ideally the document creation would be done in memory and wouldn’t hit the disk unless the user specifically saved the document. Is this feasible?

Debit answered 2/2, 2009 at 4:36 Comment(1)
D
75

Actually after messing around with heaps of different samples, all of which are incredibly convoluted and require the use of Document Writers, Containers, Print Queues and Print Tickets, I found Eric Sinks article about Printing in WPF
The simplified code is a mere 10 lines long

public void CreateMyWPFControlReport(MyWPFControlDataSource usefulData)
{
  //Set up the WPF Control to be printed
  MyWPFControl controlToPrint;
  controlToPrint = new MyWPFControl();
  controlToPrint.DataContext = usefulData;

  FixedDocument fixedDoc = new FixedDocument();
  PageContent pageContent = new PageContent();
  FixedPage fixedPage = new FixedPage();

  //Create first page of document
  fixedPage.Children.Add(controlToPrint);
  ((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
  fixedDoc.Pages.Add(pageContent);
  //Create any other required pages here

  //View the document
  documentViewer1.Document = fixedDoc;
}

My sample is fairly simplistic, it doesn't include Page Sizing and Orientation which contains a whole different set of issues that don't work as you would expect. Nor does it contain any save functionality as MS seem to have forgotten to include a Save button with the Document Viewer.

Save Functionality is relatively simple (and is also from Eric Sinks article)

public void SaveCurrentDocument()
{
 // Configure save file dialog box
 Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
 dlg.FileName = "MyReport"; // Default file name
 dlg.DefaultExt = ".xps"; // Default file extension
 dlg.Filter = "XPS Documents (.xps)|*.xps"; // Filter files by extension

 // Show save file dialog box
 Nullable<bool> result = dlg.ShowDialog();

 // Process save file dialog box results
 if (result == true)
 {
   // Save document
   string filename = dlg.FileName;

  FixedDocument doc = (FixedDocument)documentViewer1.Document;
  XpsDocument xpsd = new XpsDocument(filename, FileAccess.ReadWrite);
  System.Windows.Xps.XpsDocumentWriter xw = XpsDocument.CreateXpsDocumentWriter(xpsd);
  xw.Write(doc);
  xpsd.Close();
 }
}

So the answer is Yes, you can take an Existing WPF (XAML) Control, databind it and turn it into an XPS document - and its not all that difficult.

Debit answered 3/2, 2009 at 1:21 Comment(8)
Could you please provide the definitions for MyWPFControl and MyWPFControlDataSource ??? The example code is worthless without them and the Sinks article does not seem to contain them.Surcease
Jim, MyWPFControl is any control (custom, composite, standalone or otherwise) that you want to have rendered as a page within your XPS document. MyWPFControlDataSource is obviously any data, (usually a ViewModel), you want to bind to that control. The example has deliberately been left in a generic form so that its useful for anyone looking at it, not just someone looking to render a specific control to xaml.Debit
This is a lucky day, found in your great simple answer the solution of two of my problems for this week.Gallego
Absolutely Awesome!!! Been killing myself searching for this the ENTIRE WEEK and none of the other examples were anywhere near as simple and effective as this! Id up-vote you twice if I could! Only one thing; during my searching I see that IAddChild is obsolete and that one should "apply the ContentPropertyAttribute to a custom class instead." http://msdn.microsoft.com/en-us/library/system.windows.markup.iaddchild.aspx The solution still works but what does this mean going forward and how do you implement it?Bisexual
@JasonEbersey While I haven't tried it, a quick look at the framework code in JustDecompile shows that you can now set the Child property of a PageContent and it executes basically the same code as AddChild did/does. The Child property was read-only prior to .Net 4.0Debit
Is this working with large WPF control or i need to add some extra code for multiple paging?Galloglass
@Scott, I'm looking for a way to create something like this but with an automated pagination (no need to create pages and add them to a FixedDocument manually), is there any way to do that?Subclavius
The code works, but I only get a blank page. I'm trying to pass the MainWindow as a FrameworkElement.Macleod

© 2022 - 2024 — McMap. All rights reserved.