本文来自博客园 作者:罗小平
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,不然会出现发布时找不到数据文件的异常。相应的方法如下:
{
// 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打印和显示功能。示例
对的就做,做的就对