How to: Programmatically Print XPS Files
You can use one overload of the AddJob method to print XML Paper Specification (XPS) files without opening a PrintDialog or, in principle, any user interface (UI) at all.
You can also print XML Paper Specification (XPS) files using the many Write and WriteAsync methods of the XpsDocumentWriter. For more about this, Printing an XPS Document.
Another way of printing XML Paper Specification (XPS) is to use thePrintDocument or PrintVisual methods of the PrintDialog control. See How to: Invoke a Print Dialog.
Example
The main steps to using the three-parameter AddJob(String, String, Boolean) method are as follows. The example below gives details.
Determine if the printer is an XPSDrv printer. (See Printing Overview for more about XPSDrv.)
If the printer is not an XPSDrv printer, set the thread's apartment to single thread.
Instantiate a print server and print queue object.
Call the method, specifying a job name, the file to be printed, and a Boolean flag indicating whether or not the printer is an XPSDrv printer.
The example below shows how to batch print all XPS files in a directory. Although the application prompts the user to specify the directory, the three-parameter AddJob(String, String, Boolean) method does not require a user interface (UI). It can be used in any code path where you have an XPS file name and path that you can pass to it.
The three-parameter AddJob(String, String, Boolean) overload of AddJob must run in a single thread apartment whenever the Boolean parameter is false, which it must be when a non-XPSDrv printer is being used. However, the default apartment state for Microsoft .NET is multiple thread. This default must be reversed since the example assumes a non-XPSDrv printer.
There are two ways to change the default. One way is to simply add the STAThreadAttribute (that is, "[System.STAThreadAttribute()]") just above the first line of the application's Main method (usually "static void Main(string[] args)"). However, many applications require that the Main method have a multi-threaded apartment state, so there is a second method: put the call to AddJob(String, String, Boolean) in a separate thread whose apartment state is set to STA with SetApartmentState. The example below uses this second technique.
Accordingly, the example begins by instantiating a Thread object and passing it a PrintXPS method as the ThreadStart parameter. (The PrintXPS method is defined later in the example.) Next the thread is set to a single thread apartment. The only remaining code of the Main method starts the new thread.
The meat of the example is in the static BatchXPSPrinter.PrintXPS method. After creating a print server and queue, the method prompts the user for a directory containing XPS files. After validating the existence of the directory and the presence of *.xps files in it, the method adds each such file to the print queue. The example assumes that the printer is non-XPSDrv, so we are passing false to the last parameter of AddJob(String, String, Boolean) method. For this reason, the method will validate the XPS markup in the file before it attempts to convert it to the printer's page description language. If the validation fails, an exception is thrown. The example code will catch the exception, notify the user about it, and then go on to process the next XPS file.
class Program
{
[System.MTAThreadAttribute()] // Added for clarity, but this line is redundant because MTA is the default.
static void Main(string[] args)
{
// Create the secondary thread and pass the printing method for
// the constructor's ThreadStart delegate parameter. The BatchXPSPrinter
// class is defined below.
Thread printingThread = new Thread(BatchXPSPrinter.PrintXPS);
// Set the thread that will use PrintQueue.AddJob to single threading.
printingThread.SetApartmentState(ApartmentState.STA);
// Start the printing thread. The method passed to the Thread
// constructor will execute.
printingThread.Start();
}//end Main
}//end Program class
public class BatchXPSPrinter
{
public static void PrintXPS()
{
// Create print server and print queue.
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
// Prompt user to identify the directory, and then create the directory object.
Console.Write("Enter the directory containing the XPS files: ");
String directoryPath = Console.ReadLine();
DirectoryInfo dir = new DirectoryInfo(directoryPath);
// If the user mistyped, end the thread and return to the Main thread.
if (!dir.Exists)
{
Console.WriteLine("There is no such directory.");
}
else
{
// If there are no XPS files in the directory, end the thread
// and return to the Main thread.
if (dir.GetFiles("*.xps").Length == 0)
{
Console.WriteLine("There are no XPS files in the directory.");
}
else
{
Console.WriteLine("\nJobs will now be added to the print queue.");
Console.WriteLine("If the queue is not paused and the printer is working, jobs will begin printing.");
// Batch process all XPS files in the directory.
foreach (FileInfo f in dir.GetFiles("*.xps"))
{
String nextFile = directoryPath + "\\" + f.Name;
Console.WriteLine("Adding {0} to queue.", nextFile);
try
{
// Print the Xps file while providing XPS validation and progress notifications.
PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob(f.Name, nextFile, false);
}
catch (PrintJobException e)
{
Console.WriteLine("\n\t{0} could not be added to the print queue.", f.Name);
if (e.InnerException.Message == "File contains corrupted data.")
{
Console.WriteLine("\tIt is not a valid XPS file. Use the isXPS Conformance Tool to debug it.");
}
Console.WriteLine("\tContinuing with next XPS file.\n");
}
}// end for each XPS file
}//end if there are no XPS files in the directory
}//end if the directory does not exist
Console.WriteLine("Press Enter to end program.");
Console.ReadLine();
}// end PrintXPS method
}// end BatchXPSPrinter class
If you are using an XPSDrv printer, then you can set the final parameter to true. In that case, since XPS is the printer's page description language, the method will send the file to the printer without validating it or converting it to another page description language. If you are uncertain at design time whether the application will be using an XPSDrv printer, you can modify the application to have it read the IsXpsDevice property and branch according to what it finds.
Since there will initially be few XPSDrv printers available immediately after the release of Windows Vista and Microsoft .NET Framework, you may need to disguise a non-XPSDrv printer as an XPSDrv printer. To do so, add Pipelineconfig.xml to the list of files in the following registry key of the computer running your application:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Drivers\Version-3\<PseudoXPSPrinter>\DependentFiles
where <PseudoXPSPrinter> is any print queue. The machine must then be rebooted.
This disguise will enable you to pass true as the final parameter of AddJob(String, String, Boolean) without causing an exception, but since <PseudoXPSPrinter> is not really an XPSDrv printer, only garbage will print.
Note For simplicity, the example above uses the presence of an *.xps extension as its test that a file is XPS. However, XPS files do not have to have this extension. The isXPS Conformance Tool is one way of testing a file for XPS validity.
See Also
Tasks
Concepts
Managed and Unmanaged Threading
Documents in Windows Presentation Foundation