How to Print a WPF Flow Document ?

Using (or providing) Microsoft.NET Classes
Post Reply
User avatar
PGilbert
Posts: 440
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

How to Print a WPF Flow Document ?

Post by PGilbert »

Based on the article here I would like to print a flow document. The first two lines are going well:

printDialog ← ⎕NEW System.Windows.Controls.PrintDialog

doc←⎕NEW FlowDocument(⎕NEW Paragraph(⎕NEW Run(⊂'Some text goes here')))

The following line does not work and cause a DOMAIN ERROR

printDialog.PrintDocument(doc.DocumentPaginator,(⊂'Hello WPF Printing')) ⍝ DOMAIN ERROR

The constructor is:
Void PrintDocument(System.Windows.Documents.DocumentPaginator, System.String)

What should I do to have the last line working ?

Thanks in advance,

Pierre Gilbert
User avatar
MikeHughes
Posts: 86
Joined: Thu Nov 26, 2009 9:03 am
Location: Market Harborough, Leicestershire, UK

Re: How to Print a WPF Flow Document ?

Post by MikeHughes »

Hi Pierre,
Try

[1] PrintDialog←⎕new PrintDialog
[2] doc←⎕NEW FlowDocument(⎕NEW Paragraph(⎕NEW Run(⊂'Some text goes here')))
[3] dps←IDocumentPaginatorSource ⎕class doc
[4] PrintDialog.PrintTicket←⎕NEW PrintTicket
[5] PrintDialog.PrintDocument(dps.DocumentPaginator'Hello WPF Printing')


Line [4] may not be needed - it depends on your printer drivers I gather. Basically if you leave them out and get an exception saying something about notbeing able to retrieve Print Ticket then you need [4]

This works for me
User avatar
PGilbert
Posts: 440
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: How to Print a WPF Flow Document ?

Post by PGilbert »

Thanks Mike it worked for me too and without the ticket, I was not aware to use ⎕CLASS to cast a IDocumentPaginatorSource interface. Quite interesting. So the third line of the example on the web:

// Create IDocumentPaginatorSource from FlowDocument
IDocumentPaginatorSource idpSource = doc;

is translated to this with Dyalog APL:
idpSource←IDocumentPaginatorSource ⎕class doc

and the last line on the example:
// Call PrintDocument method to send document to printer
printDlg.PrintDocument(idpSource.DocumentPaginator, "Hello WPF Printing.");

becomes in Dyalog APL:
printDlg.PrintDocument(idpSource.DocumentPaginator 'Hello WPF Printing.')

This is pretty neat Dyalog, congratulations.

Pierre Gilbert

PS: Be aware that oddly there is 2 PrintDialog in .Net, one in System.Windows.Forms and one in System.Windows.Controls. If your ⎕USING point towards the 2 namespaces, you have to specify wich one you want for WPF.

PPS: The code used to setup ⎕USING as a global variable for this example is:

Setup30
⍝ Setup ⎕USING for the .Net 3.0 Framework

⍝ Value to Access the Program File Folder
⎕USING←,⊂'System'

⍝ Find the Program File Folder
progFiles←Environment.GetFolderPath Environment.SpecialFolder.ProgramFiles

⍝ Find the Path of the .Net 3.0 Framework
path30←progFiles,'\Reference Assemblies\Microsoft\Framework\v3.0\'

⍝ Add the Assemblies Used
use←'System,System.dll' 'System.IO,mscorlib.dll' 'System.Xml,system.xml.dll'
use,←'System.Drawing,system.drawing.dll' 'System.Windows.Forms,system.windows.forms.dll'
use,←'System.Text,mscorlib.dll' 'System.Collections,mscorlib.dll'
use,←⊂'System.Windows,',path30,'PresentationFramework.dll'
use,←⊂'System.Windows,',path30,'PresentationCore.dll'
use,←⊂'System.Windows,',path30,'WindowsFormsIntegration.dll'
use,←⊂'System.Windows,',path30,'PresentationCore.dll'
use,←⊂'System.Windows,',path30,'WindowsBase.dll'

use,←⊂'System.Windows.Controls'
use,←⊂'System.Windows.Markup'
use,←⊂'System.Windows.Navigation'
use,←⊂'System.Windows.Media'
use,←⊂'System.Windows.Media.Imaging'
use,←⊂'System.Windows.Documents'
use,←⊂'Dyalog'
use,←⊂''

⍝ Set ⎕USING
⎕USING,←use
User avatar
PGilbert
Posts: 440
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: How to Print a WPF Flow Document ?

Post by PGilbert »

Based on this article find the following code to preview a FlowDocument in a DocumentViewer window. This method use a MemoryStream to hold the required intermediary generated XPS document.

Enjoy,

Code: Select all

PrintPreview flowDoc;docViewer;flowDocPaginator;NetPath30;package;packageUri;packageUriString;printDLG;progFiles;sink;wDV;xpsDoc;xpsSm;xpsStream;⎕USING
 ⍝ Program to Preview a FlowDocument in a DocumentViewer before Printing.
 ⍝ Requirement: .Net 3.0
 ⍝ Based On: http://khason.net/blog/printing-more-then-one-page-creation-in-memory-xps-document-and-documentviewer-customization/

 ⎕USING←'System,mscorlib.dll'
 progFiles←Environment.GetFolderPath Environment.SpecialFolder.ProgramFiles
 NetPath30←progFiles,'\Reference Assemblies\Microsoft\Framework\v3.0\'

 ⎕USING←'System.IO,mscorlib.dll' 'System,System.dll'
 ⎕USING,←⊂'System.Windows.Documents,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows.Controls,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows.Documents,',NetPath30,'PresentationCore.dll'
 ⎕USING,←⊂'System.IO.Packaging,',NetPath30,'PresentationCore.dll'
 ⎕USING,←⊂'System.IO.Packaging,',NetPath30,'WindowsBase.dll'
 ⎕USING,←⊂'System.Windows.Xps.Packaging,',NetPath30,'ReachFramework.dll'
 ⎕USING,←⊂'System.Windows.Xps.Serialization,',NetPath30,'ReachFramework.dll'

 ⍝ OBTAIN THE PROPERTIES OF THE DEFAULT PRINTER
 printDLG←⎕NEW PrintDialog

 ⍝ SET THE FlowDocument ACCORDING TO THE PROPERTIES OF THE DEFAULT PRINTER
 flowDoc.PageHeight←printDLG.PrintableAreaHeight
 flowDoc.PageWidth←printDLG.PrintableAreaWidth
 flowDoc.Document.PagePadding←(⎕NEW Thickness 50)
 flowDoc.ColumnWidth←printDLG.PrintableAreaWidth
 flowDoc.Document.ColumnGap←0

 ⍝ GET A DocumentPaginator FROM THE FlowDocument
 flowDocPaginator←(IDocumentPaginatorSource ⎕CLASS flowDoc).DocumentPaginator

 ⍝ PREPARE A package FROM A MemoryStream
 xpsStream←⎕NEW MemoryStream
 package←Package.Open(xpsStream,FileMode.Create,FileAccess.ReadWrite)
 packageUriString←'memorystream://printstream'
 packageUri←⎕NEW Uri(⊂packageUriString)
 PackageStore.AddPackage(packageUri,package)

 ⍝ CREATE A XpsDocument WITH THE Package
 xpsDoc←⎕NEW XpsDocument(package,(CompressionOption.SuperFast),⊂packageUriString)
 xpsSm←⎕NEW XpsSerializationManager((⎕NEW XpsPackagingPolicy(xpsDoc)),0)
 xpsSm.SaveAsXaml flowDocPaginator

 ⍝ INITIALIZE A DocumentViewer FROM THE XpsDocument
 docViewer←⎕NEW DocumentViewer
 docViewer.Document←xpsDoc.GetFixedDocumentSequence

 ⍝ ADD THE DocumentViewer TO A Window
 wDV←⎕NEW Window
 wDV.Title←'Print Preview'
 wDV.Content←docViewer

 ⍝ SHOW THE Window AS MODAL
 sink←wDV.ShowDialog

 ⍝ CLEAN-UP AFTER THE WINDOW IS CLOSED
 PackageStore.RemovePackage(packageUri)
 xpsDoc.Close
 package.Close
 xpsStream.Close
 xpsStream.Dispose
User avatar
Dick Bowman
Posts: 235
Joined: Thu Jun 18, 2009 4:55 pm
Contact:

Re: How to Print a WPF Flow Document ?

Post by Dick Bowman »

This series of posts is invaluable for those of us trying to make sense of WPF, I've made a small adaptation of the <PrintPreview> function to take a character vector (with embedded newlines) as argument, modified lines are...

flowDoc←⎕NEW FlowDocument(⎕NEW Paragraph(⎕NEW Run(⊂doc)))
...
flowDoc.PagePadding←⎕NEW Thickness 50 ⍝ was flowDoc.Document.PagePadding←(⎕NEW Thickness 50)
...
flowDoc.ColumnGap←0 ⍝ was flowDoc.Document.ColumnGap←0

At a guess my level of ignorance (I'm using my subtle approach of charging into the problem and seeing what goes wrong) is what's led me to needing to make the changes from flowDoc.Document (which gave me VALUE ERROR).

Should I be more subtle in the way I make the FlowDocument - my code was lifted from earlier in the thread?

The code seems to do what I want it to
Visit http://apl.dickbowman.com to read more from Dick Bowman
User avatar
PGilbert
Posts: 440
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: How to Print a WPF Flow Document ?

Post by PGilbert »

Hello Dick, I think you are correct with your comments, and the code should be like you show it (without the .Document). I took my example from here and they are referencing a ScrollViewer in the code with .Document and not a FlowDocument like I do, so I did not apply their example correctly into my APL code. But why its working anyway for me and not for you, I don't know.

Thanks for sharing your observations. Here is the revised code following Dick intervention:

Code: Select all

PrintPreview flowDoc;docViewer;flowDocPaginator;NetPath30;package;packageUri;packageUriString;printDLG;progFiles;sink;wDV;xpsDoc;xpsSm;xpsStream;⎕USING
 ⍝ Program to Preview a FlowDocument in a DocumentViewer before Printing.
 ⍝ Requirement: .Net 3.0
 ⍝ Based On: http://khason.net/blog/printing-more-then-one-page-creation-in-memory-xps-document-and-documentviewer-customization/

 ⎕USING←'System,mscorlib.dll'
 progFiles←Environment.GetFolderPath Environment.SpecialFolder.ProgramFiles
 NetPath30←progFiles,'\Reference Assemblies\Microsoft\Framework\v3.0\'

 ⎕USING←'System.IO,mscorlib.dll' 'System,System.dll'
 ⎕USING,←⊂'System.Windows.Documents,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows.Controls,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows,',NetPath30,'PresentationFramework.dll'
 ⎕USING,←⊂'System.Windows.Documents,',NetPath30,'PresentationCore.dll'
 ⎕USING,←⊂'System.IO.Packaging,',NetPath30,'PresentationCore.dll'
 ⎕USING,←⊂'System.IO.Packaging,',NetPath30,'WindowsBase.dll'
 ⎕USING,←⊂'System.Windows.Xps.Packaging,',NetPath30,'ReachFramework.dll'
 ⎕USING,←⊂'System.Windows.Xps.Serialization,',NetPath30,'ReachFramework.dll'

 ⍝ OBTAIN THE PROPERTIES OF THE DEFAULT PRINTER
 printDLG←⎕NEW PrintDialog

 ⍝ SET THE FlowDocument ACCORDING TO THE PROPERTIES OF THE DEFAULT PRINTER
 flowDoc.PageHeight←printDLG.PrintableAreaHeight
 flowDoc.PageWidth←printDLG.PrintableAreaWidth
 flowDoc.PagePadding←(⎕NEW Thickness 50)   ⍝ was flowDoc.Document.PagePadding←(⎕NEW Thickness 50)
 flowDoc.ColumnWidth←printDLG.PrintableAreaWidth
 flowDoc.ColumnGap←0   ⍝ was flowDoc.Document.ColumnGap←0

 ⍝ GET A DocumentPaginator FROM THE FlowDocument
 flowDocPaginator←(IDocumentPaginatorSource ⎕CLASS flowDoc).DocumentPaginator

 ⍝ PREPARE A package FROM A MemoryStream
 xpsStream←⎕NEW MemoryStream
 package←Package.Open(xpsStream,FileMode.Create,FileAccess.ReadWrite)
 packageUriString←'memorystream://printstream'
 packageUri←⎕NEW Uri(⊂packageUriString)
 PackageStore.AddPackage(packageUri,package)

 ⍝ CREATE A XpsDocument WITH THE Package
 xpsDoc←⎕NEW XpsDocument(package,(CompressionOption.SuperFast),⊂packageUriString)
 xpsSm←⎕NEW XpsSerializationManager((⎕NEW XpsPackagingPolicy(xpsDoc)),0)
 xpsSm.SaveAsXaml flowDocPaginator

 ⍝ INITIALIZE A DocumentViewer FROM THE XpsDocument
 docViewer←⎕NEW DocumentViewer
 docViewer.Document←xpsDoc.GetFixedDocumentSequence

 ⍝ ADD THE DocumentViewer TO A Window
 wDV←⎕NEW Window
 wDV.Title←'Print Preview'
 wDV.Content←docViewer

 ⍝ SHOW THE Window AS MODAL
 sink←wDV.ShowDialog

 ⍝ CLEAN-UP AFTER THE WINDOW IS CLOSED
 PackageStore.RemovePackage(packageUri)
 xpsDoc.Close
 package.Close
 xpsStream.Close
 xpsStream.Dispose
Post Reply