Udostępnij za pośrednictwem


Creating a great printing experience in Windows 8

When we reimagined Windows, we took the opportunity to rethink all the different aspects of printing – the user experience, the developer platform, and the broad device ecosystem - and how they work together to give a great overall experience. In this post, I give an overview of the user experience and the developer platform of printing in Windows 8. I also describe how you can tailor the user experience in a way that meets your app’s requirements.

Printing from Windows 8 apps

First, let’s look at how printing works in Windows 8 and then jump into how different types of apps might handle printing.

Printing in Windows 8 is contextual to the app supporting print. An app supports printing by registering for the print contract. You decide when your app supports printing and when it doesn’t. Not all apps have something to print. For example, if you are developing a game, you may decide that it won’t need to print. But if you are working on an airline app, being able to print a boarding pass is likely an important part of the check-in process. While it’s up to you to decide if and when to print from your app, you must consider your customer’s expectations. If your customer expects your app to print, we recommend you support printing from your app.

To print in Windows 8, you swipe from the right-side of the screen to see the charms. If an app supports printing, you will see all the printers installed on your computer when you tap on the Devices charm. When you select the printer to which you want to print, you’ll see the print window as shown here.

The print flow

The print flow

The print window shows a preview of what will be printed, and a few commonly used printer settings. When we were planning printing, we often heard that people wanted to see the print content before printing it. The print platform supports showing a preview of the print content in the print window, instead of you having to show preview in your own custom way within your app. The print platform also enables you to update the preview so that your user can see how the printed output will look as she picks different printer settings in the print window.

To streamline the default print experience, the print window shows only three printer settings by default: Copies, Orientation, and Color. Our research shows that these are the printer settings users most commonly change when they print. We show these settings so that a user can quickly access these common settings while configuring a print job. As the app developer, you can control which settings appear in the print window to provide the experience that best fits your app. You can also define the default value of each setting.

All Windows 8 apps use the same experience to print their content. So, when a user knows how to print from one app, she knows how to print from every app. This means users don’t have to look around to find the print functionality and they don’t need to learn a new way to print for each app.

The print contract

Your app uses the print contract to communicate with the print platform. Your app provides the print content, and Windows provides the print experience and the underlying infrastructure. When your app registers for the print contract, printing becomes available from the Devices charm, making it easy to access and use.

The print contract, like all WinRT APIs, is designed to work with the language and UI framework of your choice. So whether you are using HTML or XAML to lay out your content, you are covered. If you want to have even more control over how your app prints its content, you can even use D2D or XPS technologies.

When adding print support to your app, there are two core elements you need to consider:

  • Providing the content to print.
  • Determining the print settings that work best for your app.

Let’s take a look at each of them.

Providing the content to print

When designing your app, take some time to think through your print scenarios. What content do you think your user might want to take with her on the go? What content will she want to have a paper copy to use or view later? If your app captures photos, for example, would she appreciate being able to print a copy and frame it in her home? Thinking through your app’s scenarios will help you choose the content that needs to be printable from your app and help you design the best printing experience for your customers.

After you determine the content to print, take some time to think about where to get your print content from. One of the first things to realize is what flows well on the screen probably doesn’t flow as well on paper. You really need to think about the best way to format your content for each of these output types. This means that you will likely be printing something different from the main view of your app, which is the content on the screen. This applies to all types of apps, even simple apps like news readers. Content that flows well in columns on the screen might not transfer well to paper in the same layout. All of these considerations have an impact on how you build your app, and it is easier to consider them early in the design than to have to go back and make changes later.

You may also want to consider how printing fits into your app’s workflow. In many apps, printing is just a menu item. For example, the user may want to print the photo they are viewing. In this case the Devices charm is the right place to direct users for initiating printing. In other words, adding an in-app way to start printing is not the right way to go; just use the charm to print. On the other hand, there are times when printing is an important part of the app’s overall workflow. Consider a shopping app that prints receipts. The view on the screen may be confirming orders and thanking customers for their business and also have a Print receipt button that takes the user directly to the print experience. When the user taps on the Print receipt button, the print window launches with the preview of the receipt and allows them to print it directly based on what they see in the preview, as here:

Printing content different from the view on the screen

Printing content different from the view on the screen

Printing from HTML/JavaScript app

To get your printing support started, all you need to do is to add these lines of code to your app.

 // 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));
        });
    } 

These lines of code register your app for the print contract. When the user selects printing, the app just prints the content of the current document, which is what’s on the screen. You can now print from your app using the Devices charm. Your app gets the print experience with the default printer settings. The Windows 8 rendering engine handles pagination and preview updates for you.

In other scenarios you want to control what to print and how to lay out the print content. For example, you might want to print content that is currently not visible on the screen, such as content that is not the current DOM (e.g. printing a receipt we just looked at). The easiest way to do that is to specify the alternate content as part of the head element in your HTML as shown below.

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

When you do this, the call to getHtmlPrintDocumentSource(document) correctly uses the alternate content specified.

But what if you want to be really creative? The cool thing is that as long as you pass an HTML document to getHtmlPrintDocumentSource, you can print HTML from just about anywhere: created locally, downloaded off the web to an IFrame, or through any other way that you want to create a printed document.

You may also use complex layout controls such as the multi column layout, horizontal scrolling, and item grouping to build your on-screen app experience. These layouts don’t translate into a good print layout. It becomes essential to simplify the layout before sending it to the print platform for easy content pagination and preview rendering, which can be achieved using CSS media rules. Defining CSS rules specifically for print media helps to create a layout that is different from what the user sees in your app flow. Using CSS, you can hide content, resize elements, adjust margins, remove elements such as backgrounds and scrollbars, change fonts and colors, and a lot more. Let’s take a look at an example.

Consider a recipe app that lets you print recipes of your favorite dishes. This app is based on the Grid App template (File->New Project-><Language>->Windows Store->Grid App).

The recipe app

The recipe app

The first thing to do is to think about what really needs to be printed. Let’s assume that the person printing this is probably adding it to a recipe book, so they probably don’t care about the ratings and reviews at this point. So we print the title, image, description text, ingredients list and the directions. We won’t print the rest of the page content such as the ratings, reviews etc.

Here’s how your CSS file looks for showing content on the screen.

 .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;}

This is what you’d see in the print preview.

Printing without content formatting

Printing without content formatting

There are a few problems in this print preview. You see the back button next to the title and a scrollbar. Moreover, you can see the preview only for the first page. All of these result in an incorrect preview.

What’s happening here is that when the content for the app doesn’t fit in the screen, the overflow property adds a scrollbar so that you can to scroll to the hidden content. When sending the content for print, the same behavior is inherited, resulting in the clipped content. To allow a proper content layout to the printing surface, content needs to be formatted using the CSS rules.

To lay out print content, you create a CSS file (print.css) and add to the html file.

 <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%;}

This lays out your print preview and the printout to look as here.

Printing with content formatting

Printing with content formatting

Notice how the issues we’ve seen earlier are eliminated. You don’t see the back button next to the title anymore and you don’t see the scrollbar in the preview either. Going to the next page on preview shows the print content flowing well into that page. CSS can be a powerful tool to lay out the print content the way you want it to be.

For more info about printing from an app written in HTML and JavaScript, see Printing using JavaScript and HTML.

Printing from XAML apps

If you are writing your app in XAML, you are responsible for handling more of the printing work in your app. You need to define how the print content is laid out for your app and determine what pagination means. This way you can control the content of each page to determine exactly what content you want your app to print. As you might expect, you use XAML to define the layout of your app’s printed content just like you use XAML to define the layout of what goes on the screen.

XAML is a great descriptive language that gives you flexibility for displaying content on the screen, especially when your app needs to scale appropriately to fit different screen dimensions. But the same things that make XAML great for the screen don’t always make it fit the best on paper. For example, XAML layouts often scale fonts and pictures to fit the horizontal and vertical pixels on the screen. While this works great on the screen, people typically don’t want their printed output scaled. So you need to think about how you want your app’s printed content to look like on paper, and then make sure that the XAML you use accurately reflects your intentions.

Going back to the recipe app, let’s take a look at how you can do content layout in XAML. In XAML, the content that we are interested is located within the ItemDetailPage in the richTextColumns control. To get the info that we’re interested in printing (i.e. the title, image, description text, ingredients list and the directions), you define a recipe page as a list of linked containers.

 <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>

For the cases when the recipe content doesn’t fit on the print page, the app must create continuation pages that contain the overflowed content. In this case the continuation page simply contains a RichTextBlockOverlow element that is linked with the rich text block element from the previous page so that the remaining text flows to the new page.

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

In response to pagination, you can define your page projection algorithm like this:

 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);
}

For more info about printing from an app written in XAML, see Printing using C#/VB/C++ and XAML.

Determining the print settings that work best for your app

When you know what content your app needs to print and how you want the content to appear, take some time to think through the user experiences that you want your app to provide.

You may determine that the default experience with the three printer settings is not sufficient. You may want to show additional printer settings or add your own app-specific settings on the print window. If so, you can customize the print experience to suit your needs.

Let’s take a look at a few examples.

Adding printer settings

Going back to the recipe app again, we know that recipes can easily flow over multiple pages. So, you may want to provide the option to print on both sides of the paper. You can do this by adding the Duplex Printing setting to the print window.

So, now the print window looks like this:

Printing with additional printer settings

Printing with additional printer settings

Here’s how to add the Duplex Printing setting in JavaScript:

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

And here’s how to add the Duplex Printing setting in C#:

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

Duplex Printing is one of the many printer settings for you to choose from. But just because you ask for the duplex setting, it may not show up. If the printer doesn’t support duplex printing, Windows hides the setting from the user. For more info about adding printer settings, see How to change standard options in the print window for HTML/JavaScript and XAML.

Adding custom settings in XAML apps

There’s additional customization that you can do for XAML apps. For XAML apps, you can add your own custom settings to the print window. Let’s take a look at an example.

Consider a map app that lets you print directions. You may be thinking, “printing maps can be costly because ink is expensive. How can I let my users choose what they print?” You may want to show a custom setting such as the Directions type setting:

Printing with custom settings

Printing with custom settings

In this example, the app added the Directions type setting, which has three options – Map and text, Map only, Text only. This way users can choose how to print the map.

Here’s how to add a custom option such as this one:

 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;

For more info about this feature, see How to add custom settings to the print window.

Conclusion

The print contract gives you flexibility in deciding what to print from your app and how the output looks like,while enabling your users to always print the same way from every app in Windows 8. You can print using the default experience, or customize the print experience as much as your app needs, and you always have the flexibility to control the information that your app prints.

References

Link

Types

Print contract sample

Sample

Registering for print contract in HTML/JavaScript

Doc

Registering for print contract in XAML

Doc

Windows.Graphics.Printing namespace

Doc

Windows.Graphics.Printing.OptionDetails namespace

Doc

Sangeeta Ranjit  Senior Program Manager, Windows Devices and Networking

Contributions by: Darren Davis, Daniel Alex Volcinschi, Saumya Jain, Travis Eby

Comments

  • Anonymous
    September 26, 2012
    Why no page range support? How print just page 2-3 of 10?

  • Anonymous
    September 26, 2012
    Page ranges are in the Reader app so it is at the very least possible.

  • Anonymous
    September 26, 2012
    While the Reader App has page range support, not all Apps do. I'm still running the Release Preview, and the Mail and IE Apps DO NOT have page range support. I like consistency in my tools, not this hunt-the-thimble-and-discover-it-doesn't-even-exist approach...

  • Anonymous
    September 28, 2012
    you lost me at "javascript". Don't care, will not be using.

  • Anonymous
    October 02, 2012
    It's up to each app to decide whether or not page range makes sense for their printing experience. If apps decide to support page range they can do so by following the samples located here: code.msdn.microsoft.com/.../Print-Sample-c544cce6. Additional documentation is available here: msdn.microsoft.com/.../hh700387.aspx

  • Anonymous
    October 02, 2012
    I'm trying to ask a question about the fact that support for custom print templates has disappeared in the RTM build ... but my comments seem to be disappearing too!

  • Anonymous
    October 02, 2012
    Oh, there I am!  So, any response please, Sangeeta? This represents a fair amount of wasted development time for us. Since the optional template parameter is still there in getHtmlPrintDocumentSource is there anyway 'undocumented' way in which to enable the feature? Or is there any intention to enable it again in the future?

  • Anonymous
    October 07, 2012
    The comment has been removed

  • Anonymous
    October 07, 2012
    Does the user have to select the printer itself each time? Or can a default be set?