在WPF中实现XPS打印和预览功能

     XPS是.net中的一个全新的打印架构,它是一个固定布局的描述格式,不仅它是WPF打印输出的基础,而且还可以把它当做独立的文件格式来使用。于是我们在设计WPF的打印功能时首先不得不想到XPS打印。由于在网上关于WPF设计的XPS打印的介绍不多,而且在MSDN上介绍的XPS打印设计也是基于.net硬编码的方式来完成的,对所见即所得的支持不好。于是笔者根据XPS打印API做了一个自己的基于FixedDocument的打印服务。并调用了内置的DocumentViewer的文档显示服务和打印功能。

一、首先看一下WPF中对XPS提供的三种文档的打印服务:

    1、 打印固定的文档类,包括FixedPage、FixedDocument、FixedDocumentSequence类,这些类提供了用于提交一个单独的页面、一整个文档或者一个含有若干文件的打印。

    2、 打印流式的文档类,主要是FlowDocument类,提供对文档流的打印。

    3、 打印可视元素类,主要是Visual及派生类对象,提供对可视树的打印。

二、接下来我们看一下XPS打印的服务的重要的类:

    1、 XPSDocument:是一个打印包方面的服务文档类,如装载和保存XPS文档或者添加缩微图等。

     public XpsDocument(string path,FileAccess packageAccess)

     用默认的隔行扫描、资源和压缩选项初始化指定的 Package 文件中包含的XpsDocument 类的一个新实例。

     public static XpsDocumentWriter CreateXpsDocumentWriter(XpsDocument xpsDocument)

     创建一个用于编写 XpsDocument 的 XpsDocumentWriter。

     public FixedDocumentSequence GetFixedDocumentSequence()

     返回位于包根位置的固定文档序列。

    2、XPSDocumentWriter:打印操作的基类,用于对打印磁盘的操作及对象树的组织结构。

    void Write()同步向 XPS 文档或打印队列中写入数据。

    void WriteAsync()异步写入已创建 XpsDocumentWriter 的 XpsDocument 或 PrintQueue

三、基本思路:

    为了将打印文档提供给用户并使用WPF内置的打印显示框架,我们使用了WPF的DocumentViewer类提供文档的显示和打印服务。这里实现了两个主要的功能:

无标题

四、具体实现:

  1、 定义一个文件打印的帮助类:

    public class FileHelper{……}

   在这个类中我们实现了两个方法

打开XPS文件
public static string OpenXPSFileFromDialog()

{

OpenFileDialog openFileDialog
= new OpenFileDialog();

openFileDialog.Filter
= "XPS Document Files(*.xps)|*.xps";

if (openFileDialog.ShowDialog() == true)

return openFileDialog.FileName;

else

return null;

}

这个静态方法主要是显示选择对话框以提供文件的保存位置。

保存XPS文件
public static bool SaveXPS(FixedPage page,bool isSaved) {

FixedDocument fixedDoc
= new FixedDocument();//创建一个文档 fixedDoc.DocumentPaginator.PageSize = new Size(96 * 8.5, 96 * 11);

PageContent pageContent
= new PageContent();

((IAddChild)pageContent).AddChild(page);

fixedDoc.Pages.Add(pageContent);
//将对象到当前文档中

string containerName = GetXPSFromDialog(isSaved);

if (containerName != null){

try{

File.Delete(containerName);

}

catch (Exception e) {

MessageBox.Show(e.Message);

}

XpsDocument _xpsDocument
= new XpsDocument(containerName, FileAccess.Write);

XpsDocumentWriter xpsdw
= XpsDocument.CreateXpsDocumentWriter(_xpsDocument);

xpsdw.Write(fixedDoc);
//写入XPS文件

_xpsDocument.Close();

return true;

}

else return false;

}

 这个方法主要是实现保存XPS文件。

装入到显示容器
static XpsDocument xpsPackage = null;

public static void LoadDocumentViewer(string xpsFileName, DocumentViewer viewer){

XpsDocument oldXpsPackage
= xpsPackage;//保存原来的XPS

xpsPackage
= new XpsDocument(xpsFileName, FileAccess.Read, CompressionOption.NotCompressed);//从文件中读取文档

FixedDocumentSequence fixedDocumentSequence
= xpsPackage.GetFixedDocumentSequence();//从XPS文档对象得到FixedDocumentSequence

viewer.Document
= fixedDocumentSequence as IDocumentPaginatorSource;

if (oldXpsPackage != null)

oldXpsPackage.Close();

xpsPackage.Close();

}

 这个方法是为了将XPS文件中文档装入到DocumentViewer显示容器中。

  2、 为了不至于通过硬编码的方式添加文档,我们添加了一个FixedPage页,注意由于FixedPage类明确标明是sealed,所以我们也不能添加代码隐藏文件,也只能在XAML添加元素,通过上下文DataContext来实现数据的绑定。这里我们添加了一个PrintView的FixedPage页,当然这里还用到了数据的绑定。相关的代码: 

FixedPage
<FixedPage

xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:i
="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

x:Name
="Window"

UseLayoutRounding
="True"

Width
="640" Height="480">

<Grid x:Name="LayoutRoot" DataContext="{Binding Collection, Source={StaticResource SimpleDataSource}}" >

<ComboBox x:Name="dataCombo" VerticalAlignment="Bottom" Margin="-26,0,22,-503" Opacity="0" ItemsSource="{Binding Mode=OneWay}"/>

<Grid x:Name="frame" Margin="0,0,0,0" >

<Canvas Margin="0,0,116,8">

<Rectangle Margin="0" Stroke="Black" Fill="#FF4B4B9F" Opacity="0.4" RadiusX="5" RadiusY="5" Width="637" Height="473" Canvas.Left="3" Canvas.Top="2"/>

<Grid Height="422" Width="506" Canvas.Left="70" Canvas.Top="34" DataContext="{Binding SelectedItem, ElementName=dataCombo}">

<Grid.RowDefinitions>

<RowDefinition Height="Auto"/>

<RowDefinition Height="Auto"/>

<RowDefinition/>

</Grid.RowDefinitions>

<Label x:Name="name" Content="{Binding Name}" FontSize="48" FontFamily="Script MT Bold" Margin="0" HorizontalAlignment="Center"/>

<Label x:Name="prescription" Content="{Binding Photo}" FontSize="48" FontFamily="Script MT Bold" Margin="0" HorizontalAlignment="Center" Grid.Row="1"/>

<Image x:Name="photo" Margin="8,0" Grid.Row="2" Source="{Binding Prescription}"/>

</Grid>

</Canvas>

</Grid>

</Grid>

</FixedPage>

 3、 添加了一个Window窗体,里面嵌入一个DocumentViewer对象以作为XPS文档的显示用的PrintWindow。

PrintWindow
<Window

xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"

x:Class
="Workpiece.PrintWindow"

x:Name
="Window"

Title
="PrintWindow"

UseLayoutRounding
="True"

Width
="640" Height="480" Loaded="Window_Loaded">

<Grid x:Name="LayoutRoot">

<DocumentViewer x:Name="docViewer" />

</Grid>

</Window>

  同时在后置代码文件中重载了Loaded事件,以装载文档

Loaded事件
//传递一个公共的数据类
public string fixedDocFile;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
FileHelper.LoadDocumentViewer(fixedDocFile, docViewer);
}

4、 为了传递数据到文档中,在Expression Blend中添加了一个示例的数据源。

5、 在主页面里我们实现了动态加载打印页面的方式,这里是通过Application类的LoadComponent()方法来实现的,在之前我也通过用XamlReader类的Load方法来加载过,不过使用这个方法,必须要将*.xaml文件设置成Building Action设置成Content,不然会出现发布时找不到数据文件的异常。相应的方法如下: 

显示调用
private void resutlBtn_Click(object sender, System.Windows.RoutedEventArgs e)

{

// TODO: Add event handler implementation here.

Uri printViewUri
= new Uri("/Workpiece;Component/PrintView.xaml", UriKind.Relative);

FixedPage printPage
=(FixedPage )Application.LoadComponent(printViewUri);

ComboBox combo
= printPage.FindName("dataCombo") as ComboBox;

combo.SelectedIndex
= nameCombo.SelectedIndex;//数据同步


FileHelper.SaveXPS(printPage,
false);

string xpsFileName = FileHelper.GetXPSFromDialog(false);//得到临时的文件存储

PrintWindow window
= new PrintWindow();

window.fixedDocFile
= xpsFileName;

window.Show();

}

6、 实现存储XPS文件的方法

XPS文件保存
//保存文件

private void otherBtn_Click(object sender, System.Windows.RoutedEventArgs e)

{

Uri printViewUri
= new Uri("/Workpiece;Component/PrintView.xaml",UriKind.Relative);

FixedPage printPage
= (FixedPage)Application.LoadComponent(printViewUri);
ComboBox combo = printPage.FindName("dataCombo") as ComboBox;

combo.SelectedIndex
= nameCombo.SelectedIndex;//数据同步
if (FileHelper.SaveXPS(printPage, true))

MessageBox.Show(
string.Format("文件保存成功"));

else

MessageBox.Show(
"取消文件保存");

}

 

至此,实现了固定文档的XPS打印和显示功能。示例

posted @ 2010-06-06 15:18  suyan010203  Views(12804)  Comments(25Edit  收藏