PrintDialog Class
Invokes a standard Microsoft Windows print dialog box that configures a PrintTicket and PrintQueue according to user input and then prints a document.
Assembly: PresentationFramework (in PresentationFramework.dll)
XMLNS for XAML: http://schemas.microsoft.com/winfx/2006/xaml/presentation, http://schemas.microsoft.com/netfx/2007/xaml/presentation
The PrintDialog type exposes the following members.
| Name | Description | |
|---|---|---|
|
MaxPage | Gets or sets the highest page number that is allowed in page ranges. |
|
MinPage | Gets or sets the lowest page number that is allowed in page ranges. |
|
PageRange | Gets or sets the range of pages to print when PageRangeSelection is set to UserPages. |
|
PageRangeSelection | Gets or sets the PageRangeSelection for this instance of PrintDialog. |
|
PrintableAreaHeight | Gets the height of the printable area of the page. |
|
PrintableAreaWidth | Gets the width of the printable area of the page. |
|
PrintQueue | Gets or sets a PrintQueue that represents the printer that is selected. |
|
PrintTicket | Gets or sets the PrintTicket that is used by the PrintDialog when the user clicks Print for the current print job. |
|
UserPageRangeEnabled | Gets or sets a value that indicates whether users of the Print dialog box have the option to specify ranges of pages to print. |
| Name | Description | |
|---|---|---|
|
Equals(Object) | Determines whether the specified Object is equal to the current Object. (Inherited from Object.) |
|
Finalize | Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.) |
|
GetHashCode | Serves as a hash function for a particular type. (Inherited from Object.) |
|
GetType | Gets the Type of the current instance. (Inherited from Object.) |
|
MemberwiseClone | Creates a shallow copy of the current Object. (Inherited from Object.) |
|
PrintDocument | Prints a DocumentPaginator object to the PrintQueue that is currently selected. |
|
PrintVisual | Prints a visual (non-text) object, which is derived from the Visual class, to the PrintQueue that is currently selected. |
|
ShowDialog | Invokes the PrintDialog as a modal dialog box. |
|
ToString | Returns a string that represents the current object. (Inherited from Object.) |
A user can use the Print dialog box to select a printer, configure it, and perform a print job.
Strictly speaking, you can use the PrintDocument method without ever opening the dialog. In that sense, the control can be used as an unseen printing component. But for performance reasons, it would be better to use either the AddJob method or one of the many Write and WriteAsync methods of the XpsDocumentWriter. For more about this, see How to: Programmatically Print XPS Files.
Do not confuse this class, System.Windows.Controls.PrintDialog, with System.Windows.Forms.PrintDialog. The latter is used with Windows Forms applications. System.Windows.Controls.PrintDialog is used with Windows Presentation Foundation (WPF) applications.
The following example shows how to create an instance of and display a simple PrintDialog by using Extensible Application Markup Language (XAML) markup and code.
...
private void InvokePrint(object sender, RoutedEventArgs e) { // Create the print dialog object and set options PrintDialog pDialog = new PrintDialog(); pDialog.PageRangeSelection = PageRangeSelection.AllPages; pDialog.UserPageRangeEnabled = true; // Display the dialog. This returns true if the user presses the Print button. Nullable<Boolean> print = pDialog.ShowDialog(); if (print == true) { XpsDocument xpsDocument = new XpsDocument("C:\\FixedDocumentSequence.xps", FileAccess.ReadWrite); FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence(); pDialog.PrintDocument(fixedDocSeq.DocumentPaginator, "Test print job"); } }
Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2
The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.
For maximum compatibility, you can work around this by printing using PrintQueue.AddJob() in the default app domain only. You still need to display PrintDialog in a special app domain, but you can copy the print ticket back to the default app domain as XML (see PrintQueue.SaveTo, etc) and you can create a new PrintQueue there if you know the full name of the old one.
Watch out for the XPS document writer driver; this drivier is bugged and will fail if you print with it on any thread but the one that owns the application object. You can overcome this by printing in the print-dialog app domain; even on XP this driver works there.
I am not sure this is a widespread problem, but better safe than sorry I guess.
- 1/7/2012
- Daniel Normal Johnson
- 1/7/2012
- Daniel Normal Johnson
dialog.PrintQueue.UserPrintTicket = dialog.PrintTicket;
dialog.PrintQueue.AddJob(...);
Counterintuitively, there is not one PrintQueue object per printer or anything like that. You can modify the PrintQueue you have without affecting any other code that isn't using your dialog instance.
AddJob() does not appear to care about any print ticket you have embedded in the .XPS file you are printing, though it is probably a good idea to embed one, since presumably something, somewhere cares about it. I've no idea what does, though.
- 1/7/2012
- Daniel Normal Johnson
Anyway, the trick is that you can create an AppDomain, then run the PrintDialog (and actually print) inside of this app domain. You'll need to marshal whatever data you need to print into this app domain, but for printing stuff, copying it all over should do fine. You may want to apply the LoaderOptimizationAttribute to make this faster; PrintDialog takes a while to load if you don't do this.
The retardly difficult part is making a print dialog act as modal dialog. PrintDialog tries to use Application.MainWindow as its owner; this is a static property, but each app-domain gets its own statics. Each app domain can have its own Application object, with its own MainWindow. But WPF windows aren't marshallable directly; instead you need to create a pair of fake dialogs; one in the default app domain and one in the printing app domain you create.
Both fake dialogs should be invisible. You can make then 0 height and width; you can set AllowTransparency to true and Opacity to 0. You can turn off the border with the WindowStyle property. You can set the background to Brushes.Transparent. You can set ShowInTaskbar to false.
Create the first fake dialog in your main app domain, set its Owner property to your real owner window (ie, whatever window is displaying the print dialog), and show it with ShowDialog(). This will disable your real owner so the user can't interact with it. You should hook the Loaded event of this fake dialog so it will extract the fake dialogs window handle (use WindowInteroapHelper), and spawn a thread to do the rest of this.
The thread will create the printing app domain and call into it, somehow marshalling over all the data you'll use, including that window handle.
Inside the printing app domain, you create another fake window, but you set its owner to that window handle (use WindowInteropHelper for this; I find you need to call EnsureHandle() first or it does not work). Finally, create an Application object and set its MainWindow to this second fake window.
Finally, you can use the print dialog. It will see this substitute Application object, take its MainWindow, and follow its chain of owners to provide the right window ordering. The real owner window will be disabled by the first fake, and the extra windows will not be visible.
When all done, you need to close the first fake window, and you need to unload the printing app-domain. That's the only way to clean up the fake Application object you created.
If you are getting the idea that this is retarded, you are getting the idea. But it can be done.
- 12/15/2011
- Daniel Normal Johnson
- 1/7/2012
- Daniel Normal Johnson
(And don't forget, you may need a second UI thread to avoid ShowDialog() locking your entire application UI!)
Even if you are purely single-threaded, you may wish to open a print dialog from another window than your MainWindow. Might be another dialog, for instance. To do this you have to temporarily change your MainWindow, which may affect other parts of your program.
So, it's just a lot safer to use the Windows Forms class of the same name. It's a wrapper around the same Win32 dialog in the end.
- 2/1/2011
- Daniel Normal Johnson
- 2/1/2011
- Daniel Normal Johnson