如何:以编程方式打印 XPS 文件
更新:2007 年 11 月
可以使用 AddJob 方法的一个重载来打印 XML 纸张规范 (XPS) 文件,而不必打开 PrintDialog,或者从原则上来说,根本不必打开任何用户界面 (UI)。
您还可以使用 XpsDocumentWriter 的许多 Write 和 WriteAsync 方法来打印 XML 纸张规范 (XPS) 文件。有关更多信息,请参见打印 XPS 文档。
打印 XML 纸张规范 (XPS) 的另一种方法是使用 PrintDialog 控件的 PrintDocument 或 PrintVisual 方法。请参见如何:调用打印对话框。
示例
使用三参数 AddJob(String, String, Boolean) 方法的主要步骤如下。下面的示例给出了详细信息。
确定打印机是否是 XPSDrv 打印机 (有关 XPSDrv 的更多信息,请参见打印概述)。
如果打印机不是 XPSDrv 打印机,将线程的单元设置为单线程。
实例化一个打印服务器和打印队列对象。
调用该方法,并指定作业名称、要打印的文件以及指示打印机是否是 XPSDrv 打印机的 Boolean 标志。
下面的示例演示如何以批处理方式打印一个目录中的所有 XPS 文件。尽管应用程序提示用户指定目录,但是三参数的 AddJob(String, String, Boolean) 方法不需要用户界面 (UI)。它可以用在满足以下条件的任何代码路径中:您在其中具有 XPS 文件名和路径,并且可以将该文件名和路径传递给该方法。
只要 Boolean 参数是 false(当使用的不是 XPSDrv 打印机时,该参数必须为此值),AddJob 的三参数 AddJob(String, String, Boolean) 重载就必须运行在单个线程单元中。但是,Microsoft .NET 的默认单元状态是多线程。必须将此默认设置反过来,因为本示例采用的不是 XPSDrv 打印机。
有两种方法来更改默认设置。一种方法是在应用程序的 Main 方法的第一行(通常是“static void Main(string[] args)”)的紧上方添加 STAThreadAttribute(即“[System.STAThreadAttribute()]”)。但是,许多应用程序都要求 Main 方法有多线程单元状态,因此存在第二种方法,即:在一个单独的线程中放置对 AddJob(String, String, Boolean) 的调用,该线程的单元状态通过 SetApartmentState 设置为 STA。下面的示例使用第二种方法。
因此,示例的开头实例化一个 Thread 对象,并将一个 PrintXPS 方法作为 ThreadStart 参数传递给它 (PrintXPS 方法在本示例的稍后部分定义)。接下来,线程设置为单线程单元。Main 方法的剩余代码启动新线程。
该示例的主要内容在 staticBatchXPSPrinter.PrintXPS 方法中。在创建打印服务器和队列后,该方法会提示用户输入包含 XPS 文件的目录。在确认该目录存在并且其中存在 *.xps 文件后,该方法将每个这样的文件添加到打印队列中。该示例假定打印机不是 XPSDrv,因此我们将 false 传递给 AddJob(String, String, Boolean) 方法的最后一个参数。为此,该方法在尝试将文件转换为打印机的页面描述语言之前,会先验证文件中的 XPS 标记。如果验证失败,将引发一个异常。示例代码将捕捉此异常,将有关此异常的信息通知给用户,然后继续处理下一个 XPS 文件。
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
如果您使用的是 XPSDrv 打印机,则可以将最后一个参数设置为 true。在这种情况下,因为 XPS 是打印机的页面描述语言,所以此方法会将文件发送到打印机,而不对它进行验证或将它转换为另一种页面描述语言。如果您在设计时不确定应用程序是否将使用 XPSDrv 打印机,则可以修改应用程序,使它读取 IsXpsDevice 属性,并根据它找到的内容进行分支。
因为在 Windows Vista 和 Microsoft .NET Framework 发行之后,最初几乎没有 XPSDrv 打印机可以立即投入使用,因此,可能需要将非 XPSDrv 打印机假装成 XPSDrv 打印机。为此,应将 Pipelineconfig.xml 添加到运行您的应用程序的计算机的以下注册表项中的文件列表:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Drivers\Version-3\<PseudoXPSPrinter>\DependentFiles
其中 <PseudoXPSPrinter> 是任一打印队列。然后必须重新启动计算机。
这种假装使您可以将 true 作为 AddJob(String, String, Boolean) 的最后一个参数来传递,而不会导致异常,但因为 <PseudoXPSPrinter> 并不是真正的 XPSDrv 打印机,因此打印出来的将仅仅是乱码。
注意 为简单起见,上面的示例通过检查是否存在扩展名 *.xps 来检测文件是否是 XPS。但是,XPS 文件不一定有此扩展名。isXPS 一致性工具 也是一种测试文件是否是 XPS 的方法。
请参见
任务
概念
Windows Presentation Foundation 中的文档