0 out of 1 rated this helpful - Rate this topic

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.

System.Object
  System.Windows.Controls.PrintDialog

Namespace:  System.Windows.Controls
Assembly:  PresentationFramework (in PresentationFramework.dll)
XMLNS for XAML: http://schemas.microsoft.com/winfx/2006/xaml/presentation, http://schemas.microsoft.com/netfx/2007/xaml/presentation
public class PrintDialog
<PrintDialog .../>

The PrintDialog type exposes the following members.

  Name Description
Public method PrintDialog Initializes a new instance of the PrintDialog class.
Top
  Name Description
Public property MaxPage Gets or sets the highest page number that is allowed in page ranges.
Public property MinPage Gets or sets the lowest page number that is allowed in page ranges.
Public property PageRange Gets or sets the range of pages to print when PageRangeSelection is set to UserPages.
Public property PageRangeSelection Gets or sets the PageRangeSelection for this instance of PrintDialog.
Public property PrintableAreaHeight Gets the height of the printable area of the page.
Public property PrintableAreaWidth Gets the width of the printable area of the page.
Public property PrintQueue Gets or sets a PrintQueue that represents the printer that is selected.
Public property PrintTicket Gets or sets the PrintTicket that is used by the PrintDialog when the user clicks Print for the current print job.
Public property 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.
Top
  Name Description
Public method Equals(Object) Determines whether the specified Object is equal to the current Object. (Inherited from Object.)
Protected method Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)
Public method GetHashCode Serves as a hash function for a particular type. (Inherited from Object.)
Public method GetType Gets the Type of the current instance. (Inherited from Object.)
Protected method MemberwiseClone Creates a shallow copy of the current Object. (Inherited from Object.)
Public method PrintDocument Prints a DocumentPaginator object to the PrintQueue that is currently selected.
Public method PrintVisual Prints a visual (non-text) object, which is derived from the Visual class, to the PrintQueue that is currently selected.
Public method ShowDialog Invokes the PrintDialog as a modal dialog box.
Public method ToString Returns a string that represents the current object. (Inherited from Object.)
Top

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.


<Button Width="200" Click="InvokePrint">Invoke PrintDialog</Button>


...


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");
        }
    }


.NET Framework

Supported in: 4, 3.5, 3.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

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.
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
An XP Pitfall
It turns out that printing in a non-default appdomain does not work properly on Windows XP for some printers; an Okidata C5500 will print with the wrong font somehow, and the result looks very bad, since the characters are placed as if the right font were used. I don't know what causes this, but other printers I've tried don't exhibit the problem. Also, it works fine on Vista or Windows 7.

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.
Use with PrintQueue.AddJob
You can use PrintQueue.AddJob to print, rather than calling PrintDocument on this class. There is a PrintQueue property on the PrintDialog you can use, but there's a trick- that print queue is not configured with the PrintTicket that the dialog has filled out. Therefore, you must so configure it:

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.
It can be made to work!
But it is retardedly difficult. Seriously, Microsoft, you should be ashamed of yourself over this.

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.
Consider System.Windows.Forms.PrintDialog instead
This class has a serious bug, or set of bugs, that make it pretty near useless. It sets the owner of the print-dialog to Application.Current.MainWindow. This is wrong in two ways: this will just fail with an exception if you try to use this class on any thread but the application thread, so forget background printing, and forget having second UI thread.

(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.