长期饭票

大家好,请喊我序员!
QQ:15838986
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2011-01-13 11:19  Baode  阅读(7795)  评论(0编辑  收藏  举报

本文来自博客园 作者:罗小平

 

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{……}

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

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;

}

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

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

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。

<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事件,以装载文档

//传递一个公共的数据类
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文件的方法

//保存文件

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打印和显示功能。示例