3

Strictly theoretical question concerning WPF's DocumentPaginator:

When using the WPF DocumentPaginator class to print a multi-page document, does the paginator keep all of the DocumentPages it requests over the history of the print job in memory until the document finishes printing? Or does it ever dispose of any DocumentPages it no longer needs before the print job is finished (i.e. to free up memory)? Is there a way to manually tell the paginator to free old/unneeded DocumentPages in the course of printing without hitting an exception?

Many thanks for your help with this!

Steven D.
  • 311
  • 4
  • 10

2 Answers2

2

I had exactly the same problem as you have.

It doesnt dispose the Pages which got loaded earlier. What I did to solve this issue was to hold a reference to the loaded page at the end of the GetPage() Method and dispose the last loaded page in the beginning of the GetPage Method.

here the answer to your aditional question:

I have the impression the implementation of System.Windows.Controls.PrintDialog.Print(DocumentPaginator, title) is something like that:

Public void PrintDocument(DocumentPaginator paginator, string title)
{
   Dictionary<int, DocumentPage> pages = new Dictionary<int DocumentPage>();
   for(int i=0; i<paginator.PageCount(); i++)
   {
      pages.Add(i, paginator.GetPage(i));
      UnknownPrinterEngine.SendPageToPrinter(pages(i)); //this is just imagination
   }
}

if the implementation is really something like that, a local reference to each processed page stays alive (in the dictionary) until the method execution finished. --> No memory will get freed.

What i did to avoid that (GetPage implementation in the class which extends DocumentPaginator):

DocumentPage lastLoadedPage = null;
public DocumentPage GetPage(int pageNumber)
{
   if(lastLoadedPage != null)
   {
      lastLoadedPage.Dispose()
   }
   //MyPrintControl should be your custom UserControl which represents the page to print
   myPrintControl pageContent = new MyPrintControl();
   pageContent.Width = PageSize.Width;
   pageContent.Height = PageSize.Height;

   pageContent.Measure(PageSize);
   pageContent.Arrange(new Rect(new Point(0,0), PageSize));

   DocumentPage actualPage = New DocumentPage(pageContent);
   lastLoadedPage = actualPage;
   return actualPage;
}

And at the end you should implement the IDisposable interface and in the Dispose Method clean up the lastLoadedPage field to free the memory of the last page too.

fixagon
  • 5,506
  • 22
  • 26
  • Thanks for the speedy reply, fantasticfix. Could you please clarify what you mean by "gard[ing] (guarding?) a reference to the loaded page at the end of the GetPage() method?" Did you do this using a static variable to store the reference (i.e. so it would be preserved until the next call to GetPage...though I'm not sure this is right since I'm new to C#)? Did you pass the previous page in by reference as another argument (i.e. changing the function signature from GetPage (int pageNumber) to something like GetPage (int pageNumber, ref DocumentPage previous))? – Steven D. Mar 22 '11 at 13:16
  • edited the answers inside, i hope you understand what i mean. – fixagon Mar 22 '11 at 15:31
  • Hmmm...I tried this, but it didn't seem to release any memory (I verified performance by watching private byte allocation using Performance Manager). – Steven D. Mar 22 '11 at 16:45
  • One note: according to MSDN documentation (and code which I've run using Visual Studio), the constructor to DocumentPage always takes at least one argument (at minimum, a Visual object). So I'm guessing your code for creating a new DocumentPage didn't look exactly what you have above (i.e. using syntax like new DocumentPage())? – Steven D. Mar 22 '11 at 16:47
  • yes thats truem i wrote the code just like that, i used it with a Visual. So it would be DocumentPage page = new DocumentPage(visualToPrint). – fixagon Mar 22 '11 at 17:29
  • Well, I tried your solution and for all intensive purposes, our code was the same (minus the preparation of the Visual fed to DocumentPage as an argument, obviously), but an analysis with Performance Manager doesn't show my Visual Studio process releasing any private bytes when I call Dispose(). – Steven D. Mar 22 '11 at 20:28
  • Just out of curiosity: what exact problem were you having with your DocumentPaginator that led you to this fix? My problem is that I'm getting an OutOfMemoryException when I run a large print job that uses some BitmapObjects in its DocumentPages; I'm also working on finding a way to deal with the size of the bitmaps (and/or find a smaller image format for this problem in general, perhaps Windows Metafiles), but I thought it would also be worthwhile to see if I could clean up the paginator's memory usage as well. – Steven D. Mar 22 '11 at 20:31
  • One other note: I take it you could also condense the last three lines of your code into two (see below)? – Steven D. Mar 22 '11 at 20:34
  • lastLoadedPage = new DocumentPage(pageContent); return lastLoadedPage; – Steven D. Mar 22 '11 at 20:35
0

Look into System.Windows.Xps.VisualsToXpsDocument This fixes the problem I have running out of memory when creating large xps document. Also look into PrintQueue.CreateXpsDocumentWriter(somePrintQueue) . Sorry, I don't have enough reps to just make this a comment.

M312V
  • 193
  • 1
  • 14