在 Windows 8 中打造卓越的打印体验

我们利用重新构思并构建 Windows 的这个机会重新考虑了打印功能的方方面面,包括用户体验、开发者平台以及种类繁多的设备环境,以及这些方面如何共同提供卓越的打印体验。在本篇博文中,我概要介绍 Windows 8 中打印的用户体验和开发者平台,还会介绍您可以如何定制自己的用户体验,以便符合您的应用程序的要求。

通过 Windows 8 应用程序打印

首先,我们来看一下 Windows 8 中的打印功能是如何工作的,然后再来讨论各种不同类型的应用程序如何处理打印作业。

Windows 8 中的打印功能与支持打印的应用程序上下文相关。通过注册“打印”合约,某个应用程序可以支持打印。您可以决定您的应用程序何时支持打印、何时不支持打印。并不是所有的应用程序都有需要打印的内容。例如,如果您正在开发某个游戏,则您可以决定它不需要进行打印。但是如果您正在开发某个航空应用程序,则能够打印登机证则是登机流程的一个重要组成部分。尽管是由您来决定是否可以通过您的应用程序进行打印以及何时进行打印,但是您务必需要考虑客户的期望。如果您的客户希望您的应用程序可以进行打印,则我们建议您令您的应用程序支持打印功能。

要在 Windows 8 中进行打印,您可以从屏幕的右侧轻扫屏幕,以便查看超级按钮。如果应用程序支持打印,则当您点击“设备”超级按钮时将可以看到电脑上安装的所有打印机。当选择您要用来打印的打印机时,将会看到打印窗口,如下所示。

打印流程

打印流程

打印窗口显示要打印内容的预览和一些常用打印机设置。当我们在规划打印功能时,经常听到人们说,希望能在打印前看到要打印的内容。打印平台支持在打印窗口中显示打印内容的预览,而不是您必须在您的应用程序中以自定义的方式显示预览。打印平台还允许您更新预览,这样您的用户在打印窗口中选择不同的打印机设置时均可以看到打印输出的外观。

为了简化默认打印体验,默认情况下,打印窗口仅显示三个打印机设置:副本数、打印方向和颜色。我们的调研显示,这些打印机设置是用户打印时最常更改的设置。我们显示这些设置,这样用户可以在配置打印作业时快速访问这些常用设置。作为应用程序开发者,您可以控制哪些设置显示在打印窗口中,以便提供最适合您的应用程序的用户体验。还可以定义每个设置的默认值。

所有 Windows 8 应用程序均使用相同的体验来打印内容。因此,当用户知道如何从一个应用程序打印时,也就知道如何从每个应用程序进行打印。这意味着用户不必到处找寻打印功能,也不需要为每个应用程序学习一种新的打印方法。

“打印”合约

您的应用程序使用“打印”合约与打印平台进行联系。您的应用程序提供打印内容,Windows 提供打印体验和基础结构。当您的应用程序注册“打印”合约时,打印功能开始在“设备”超级按钮中可用,令其非常易于访问和使用。

“打印”合约(诸如所有 WinRT API)设计为可以与您选择的语言和 UI 框架配合使用。因此,不论您是使用 HTML 还是 XAML 来布设内容,都可以利用该项功能。如果您希望更多控制您的应用程序打印内容的方式,则可以使用 D2D 或 XPS 技术。

当向您的应用程序添加打印支持时,有两个核心要素需要考虑:

  • 提供要打印的内容。
  • 确定最适合您的应用程序的打印设置。

让我们来分别看一下这两个要素。

提供要打印的内容

当设计您的应用程序时,您需要花一些时间来考虑相应的打印场景。您认为您的用户在具移动性时可能希望随身携带哪些内容?她希望拥有哪些内容的纸质副本以便以后使用或查看?例如,如果您的应用程序捕获照片,她是否希望能在家中打印照片以及装帧照片?想一想您的应用程序应用场景,可以帮助您选择那些可通过应用程序进行打印的内容,可以帮助您为您的客户设计最佳的打印体验。

在您决定要打印的内容后,请花一些时间来考虑应从哪里获得打印内容。第一件事就是要认识到在屏幕上能正常显示的内容可能在纸张上无法正常显示。您需要为每种输出类型考虑对内容进行格式设置的最佳方式。这意味着您要打印的内容可能与应用程序主视图中的内容(也就是屏幕上的内容)不太相同。这适用于所有类型的应用程序,即便是象新闻阅读器这样的简单应用程序也是如此。在屏幕上以列格式显示正常的内容,在纸张上的同样布局下可能不能正常显示。所有这些注意事项都对您如何构建自己的应用程序产生影响,在设计阶段就予以充分考虑,比在后续阶段重新返工进行更改,要容易得多。

您还可能希望考虑如何将打印功能契合到您的应用程序工作流。在许多应用程序中,打印功能只是一个菜单项。例如,用户可能希望在欣赏照片的同时打印照片。在这种情况下,“设备”超级按钮就是指导用户开始打印的最佳位置。也就是说,增加在应用程序内启动打印的方式不是一个好的办法;只需要使用超级按钮打印即可。另一方面,也存在一些情况,就是打印是某个应用程序整体工作流程的重要组成部分。比如用来打印收据的收银应用程序。屏幕上的视图可能正在确认订单,并感谢客户,还有一个“Print receipt”[打印收据] 按钮,可直接帮助用户进行打印。当用户点击“Print receipt”[打印收据] 按钮时,打印窗口会启动,显示收据的预览视图,并直接打印用户在预览窗口中看到的内容,如下所示:

打印内容与屏幕上的视图不同

打印内容与屏幕上的视图不同

通过 HTML/JavaScript 应用程序进行打印

要启动您的打印支持,您所需做的就是将下面这些代码行添加到您的应用程序内。

 // Register for print contract
var printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
printManager.onprinttaskrequested = onPrintTaskRequested;
    function onPrintTaskRequested(printEvent) {
        printEvent.request.createPrintTask("Print Sample", function (args) {
            args.setSource(MSApp.getHtmlPrintDocumentSource(document));
        });
    } 

这些代码行为您的应用程序注册“打印”合约。当用户选择打印时,应用程序只是打印当前文档的内容,也就是屏幕上显示的内容。您现在可以使用“设备”超级按钮通过您的应用程序进行打印。您的应用程序会获得默认打印机设置下的打印体验。Windows 8 呈现引擎会为您处理分页和预览更新。

在其他场景下,您希望控制要打印的内容以及如何布设打印内容。例如,您可能希望打印当前未在屏幕上显示的内容,如不属于当前 DOM 的内容(例如打印我们刚刚看到的收据)。最简单的办法就是指定备选内容作为 HTML 中头元素的一部分,如下所示。

 <! -- Add this to the head element of your html file -->
<link rel="alternate" href="https://go.microsoft.com/fwlink/?linkid=240076" media="print"/>  

这样,对 getHtmlPrintDocumentSource(document) 的调用正确地使用了指定的备选内容。

不过,如果您想更富创新性,该怎么做呢?只要您将 HTML 文档传递给 getHtmlPrintDocumentSource,您就可以打印位于任意位置处的 HTML,这是一件相当酷的事情:本地创建的文档、从 Web 下载到一个 IFrame 的文档,或者以其他任何您希望的方式创建的打印文档。

您还可以使用复杂的布局控件(如多列布局、水平滚动和项目分组)来构建您的屏幕应用程序体验。这些布局不会转换成一个良好的打印布局。在将文档发送到打印平台前简化布局就变得至关重要,因为这更便于将内容分页以及呈现预览,可以通过使用 CSS 媒体规则来实现上述目的。为打印媒体特别定义 CSS 规则可以帮助创建一个不同于用户在应用程序流程中看到的布局。利用 CSS,您可以隐藏内容、重新调整元素大小、调整边距、删除元素(如背景和滚动栏)、更改字体和颜色以及其他内容。让我们一起来看一个示例。

假设有一个菜谱应用程序,利用它您可以打印出自己喜欢的菜式说明。此应用程序是基于网格应用程序模板(文件->新建项目-><语言>->Windows 应用商店->网格应用程序。)

菜谱应用程序

菜谱应用程序

第一件要做的事,就是考虑哪些是真正需要打印的内容。我们设想一下,打印此菜谱的人可能正将该菜谱添加到自己的食谱表,所以他们可能并不在意这道菜的评分和评论。因此我们打印标题、图像、说明文字、成分列表和做法。我们不会打印页面内容的其他部分,例如,评分和评论。

下面是您的 CSS 文件如何查找在屏幕上显示的内容。

 .itemdetailpage .content {
-ms-grid-row: 1;
-ms-grid-row-span: 2;
display: block;
height: 100%;
overflow-x: auto;
position: relative;
    width: 100%;
z-index: 0;}

.itemdetailpage .content article {
/* Define a multi-column, horizontally scrolling article by default. */
column-fill: auto;
column-gap: 80px;
column-width: 480px;
height: calc(100% - 183px);
margin-left: 120px;
margin-top: 133px;
width: 480px;}

.itemdetailpage .content article header .item-title {
margin-bottom: 19px;}

.itemdetailpage .content article .item-image {
height: 240px;
width: 460px;}

.itemdetailpage .content article .item-rating {
height: 40px;
margin-top: 0px}

.itemdetailpage .content article .item-review {
height: 100px;
margin-top: 20px }

.itemdetailpage .content article .item-content p {
margin-top: 10px;
margin-bottom: 20px;
margin-right: 20px;}

下面就是您在打印预览中会看到的内容。

不带内容格式进行打印

不带内容格式进行打印

在该打印预览中有这样几个问题。您会在标题和滚动栏旁边看到后退按钮。而且,仅可以看到第一页的预览。所有这些都导致了预览不正确。

此处出现的情形就是,当应用程序的内容不适合屏幕时,溢出属性会添加一个滚动栏,这样您可以滚动到隐藏的内容。当发送内容以便进行打印时,会导致在剪辑内容中继承同样的行为。要令内容正确地布设在打印页面,需要使用 CSS 规则对内容进行格式化。

为了布设打印内容,您可以创建一个 CSS 文件 (print.css),并将其添加到 html 文件。

 <link rel="stylesheet" type="text/css" href="/css/print.css" media="print" />
 Print.css
/* Hide the back button */
.win-backbutton {
display: none;}

/* Remove the grid layout */
.fragment {
display: block;
height: auto;
width: 100%;
position: relative;
margin: 0px;
overflow: visible;}

/* Remove the header */
.fragment header[role=banner] {
display: none;}

/* Remove multicolumn layout */
/* Resize the content to take up available space*/
/* Remove scrolling behavior */
.itemdetailpage .content {
display: block;
height: 100%;
overflow: visible;
width: 100%;}

.itemdetailpage .content article {
height: 100%;
width: 100%;
margin: 0px;
overflow: visible;}

/* Adjust margins */
/* Increase font sizes */
.itemdetailpage .content article header .item-title {
margin-bottom: 29px;
font-size: 450%;}

.itemdetailpage .content article .item-image {
height: 480px;
width: 920px;
margin-top: 20px;
margin-bottom: 40px;}

/* Remove rating and review controls */
.itemdetailpage .content article .item-rating {
display: none;}
.itemdetailpage .content article .item-review {
display: none; }

.itemdetailpage .content article .item-content p {
height: auto;
width: 100%;}

这会将打印预览和打印输出布设为此处显示的样子。

带内容格式进行打印

带内容格式进行打印

请注意我们先前看到的问题是如何消失的。您在标题旁边不会再看到后退按钮,并且您也看不到先前预览中出现的滚动栏。在预览页上转到下一页,显示打印内容很好地流转到该页上。CSS 是一个强大的工具,可以令您根据自己的想法布设打印内容。

有关从以 HTML 和 JavaScript 编写的应用程序进行打印的更多信息,请参阅使用 JavaScript 和 HTML 进行打印。

从 XAML 应用程序打印

如果您正在以 XAML 编写应用程序,则您应负责在您的应用程序中处理更多打印作业。您需要定义如何为您的应用程序布设打印内容,并且决定分页意味着什么。这种方式下您可以控制每页的内容,并且精确地规定您希望您的应用程序要打印的内容。正如您希望的那样,您使用 XAML 定义您应用程序的打印内容的布局,就像使用 XAML 来定义要在屏幕中显示内容的布局一样。

XAML 是一种很好的描述性语言,令您可以在屏幕上灵活地显示内容,特别是当您的应用程序为了适合不同的屏幕尺寸而相应地伸缩时。不过,XAML 在屏幕上发挥的这一强大功能,在纸张上并不能总是发挥得特别充分。例如,XAML 布局通常会伸缩字体和图片以便适应屏幕上的水平像素和垂直像素。这个功能在屏幕上可以很好地发挥作用,但是人们一般不希望他们的打印输出也进行伸缩。所以您需要考虑您希望自己的应用程序打印内容在纸张上如何显示,然后确保您使用的 XAML 可以准确地反应您的意图。

现在再以菜谱应用程序为例,我们来看您可以如何使用 XAML 布设内容。在 XAML 中,我们感兴趣的内容位于 richTextColumns 控件中的 ItemDetailPage 中。为了获取我们有意要打印的内容(如标题、图片、说明文字、成分列表和做法说明),您可以将菜谱页定义为一个链接容器的列表。

 <Grid VerticalAlignment="Center" x:Name="grid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <Grid.RowDefinitions>
        <RowDefinition Height="3*"/>
        <RowDefinition Height="7*"/>
    </Grid.RowDefinitions>
    
    <RichTextBlock x:Name="txtContent" OverflowContentTarget="{Binding ElementName=link}" Grid.Row="0" Grid.Column="0">
        <Paragraph TextAlignment="Center">
            <Run x:Name="txtRecipe" Text="{Binding Title}" FontSize="30" Foreground="Black"></Run>
        </Paragraph>
        <Paragraph>
            <InlineUIContainer>
                <Image x:Name="imgRecipe" Stretch="Uniform" Source="{Binding Image}"/>
            </InlineUIContainer>
        </Paragraph>
        <Paragraph>
            <Run Text="Ingredients:" FontSize="30" Foreground="Black"/>
            <Run x:Name="txtIngredients" Text="{Binding RecipeIngredients}" FontSize="20" Foreground="Black"/>
        </Paragraph>
        <Paragraph>
            <Run Text="Details:" FontSize="30" Foreground="Black"/>
            <Run x:Name="txtDetails" Text="{Binding Content}" FontSize="20" Foreground="Black"/>
        </Paragraph>
    </RichTextBlock>
    
    <RichTextBlockOverflow x:Name="link" Grid.Row="1" Grid.Column="0"/>
</Grid>

对于菜谱内容在一个打印页面上放不下的情况,应用程序必须创建继续页,以便容纳这些放不下的内容。在此情况下,继续页仅包含一个 RichTextBlockOverlow 元素,该元素与上一页面中的格式文本块元素相链接,所以其他文字会流转到新的页面。

 <Grid x:Name="grid">
    <RichTextBlockOverflow x:Name="link"/>
</Grid>

为了应对分页,您可以将页面投影算法定义为如下所示:

 void printDocument_Paginate(object sender, PaginateEventArgs e)
{
    PrintDocument printDocument = (PrintDocument)sender;

    // get printer page description
    PrintPageDescription pageDesc = e.PrintTaskOptions.GetPageDescription(0);
    
    // clear previous generated print content
    pages.Clear();
    printingRoot.Children.Clear();

    RecipePage page = new RecipePage(pageDesc)
    {
        DataContext = flipView.SelectedItem
    };

    UIElement element = null;
    RichTextBlockOverflow rtbo = page.Link;

    do
    {
// check if this is the first page
        if (element != null)
        {
            var continuation = new ContinuationPage(rtbo, pageDesc);
            element = continuation;
            rtbo = continuation.Link;
        }
        else
        {
            element = page;
        }

        // add the page to the visual tree to forece content flow
        printingRoot.Children.Add(element);
        printingRoot.InvalidateMeasure();
        printingRoot.UpdateLayout();

        pages.Add(element);

    } while (rtbo.HasOverflowContent);

    // set the current number of pages
    printDocument.SetPreviewPageCount(pages.Count, PreviewPageCountType.Intermediate);
}

有关从以 XAML 编写的应用程序进行打印的更多信息,请参阅使用 C#/VB/C++ 和 XAML 进行打印。

确定最适合您的应用程序的打印设置

当您了解了您的应用程序需要打印的内容以及您希望内容如何显示后,请花一些时间来考虑您希望您的应用程序为用户提供什么样的用户体验。

您可能会认为有三种打印机设置的默认体验还远远不够。可能希望显示更多打印机设置或者在打印窗口中添加您自己的应用程序特定设置。如果这样,您可以自定义您的打印体验以便符合自己的需求。

让我们一起来看几个示例。

添加打印机设置

现在重新来看菜谱应用程序,我们知道菜谱通常很长,会跨多个页面。因此,您可能希望提供选项,用户可以在纸张的两面均能打印。您可以将“双面打印”设置添加到打印窗口来实现上述目的。

现在打印窗口会如下所示:

具有更多打印机设置的打印功能

具有更多打印机设置的打印功能

下面是如何在 JavaScript 中添加“双面打印”设置:

 printTask.options.displayedOptions.append
   (Windows.Graphics.Printing.StandardPrintTaskOptions.duplex);

下面是如何在 C# 中添加“双面打印”设置:

 printTask.options.displayedOptions.Add (Windows.Graphics.Printing.StandardPrintTaskOptions.Duplex);

“双面打印”是您可选的众多打印机设置之一。但是只是因为您在寻求双面设置,它可能不会予以显示。如果打印机不支持双面打印,则 Windows 会向用户隐藏该设置。有关添加打印机设置的更多信息,请参阅适用于 HTML/JavaScriptXAML 的如何在打印窗口中更改标准选项。

在 XAML 应用程序中添加自定义设置

您可以为 XAML 应用程序增加一些自定义项。对于 XAML 应用程序,您可以向打印窗口添加自己的自定义设置。让我们一起来看一个示例。

假设有一个地图应用程序,利用它您可以打印地图。您可能会想:“墨太贵,所以打印地图成本太高。我如何可以让我的用户选择要打印的内容”您可能希望显示一个自定义的设置,如“Directions type ”[Directions type] 设置:

带自定义设置的打印

带自定义设置的打印

在本示例中,该应用程序增加了“Directions type”[方向类型] 设置,具有三个选项:“Map and text”[地图和文字]、“Map only”[仅地图] 和“Text only”[仅文字]。通过这种方式用户可以选择如何打印地图。

下面是如何添加自定义选项,如下所示:

 PrintCustomItemListOptionDetails pageFormat = printDetailedOptions.CreateItemListOption 
   ("PageContent", "Pictures");
pageFormat.AddItem ("MapText", "Map and text");
pageFormat.AddItem ("MapOnly", "Map only");
pageFormat.AddItem ("TextOnly", "Text only");

// Add the custom option to the option list
displayedOptions.Add ("PageContent");

printDetailedOptions.OptionChanged += printDetailedOptions_OptionChanged;

有关此功能的更多信息,请参阅如何向打印窗口添加自定义设置

总结

“打印”合约令您可以灵活地决定要通过应用程序打印哪些内容,以及打印输出的外观,使得用户可以在 Windows 8 中的每个应用程序中始终以相同的方式打印。您可以使用默认体验打印,或根据应用程序的需要自定义打印体验,您可以始终灵活地控制您的应用程序打印的内容。

参考

链接

类型

“打印”合约样本

示例

以 HTML/JavaScript 注册“打印”合约

文档

以 XAML 注册“打印”合约

文档

Windows.Graphics.Printing 命名空间参考

文档

Windows.Graphics.Printing.OptionDetails 命名空间参考

文档

—Windows 设备和网络高级项目经理 Sangeeta Ranjit

投稿者:Darren Davis、Daniel Alex Volcinschi、Saumya Jain、 Travis Eby