Dela via


Quickstart: Printing from your app (XAML)

This Quickstart shows the simplest way to add print functionality to your Windows Store app using C++, C#, or Visual Basic.

Watch this brief video for an overview of the process.

At a high level, to print from your Windows Store app using C++, C#, or Visual Basic, your app must:

  • Register for the Print contract in each display of the app from which you want users to be able to print.

    Registering for the Print contract means obtaining a PrintManager object, creating a PrintTask object, and handling the printing events.

  • Provide formatted content to print.

    The default print experience shown in this Quickstart is for Windows to print your app's screen display. For more info about formatting your app's content for printing, see the Summary and next steps.

Prerequisites

  • You must be familiar with C# or Visual Basic, XAML, Windows events, and event handling.
  • You must have Microsoft Visual Studio installed.
  • You must have a printer installed.
  • You must have an app to which you want to add printing. If you don't have your own app, you can download the Print Sample sample app and use that app.

Instructions

1. Open the app's source code for editing

This procedure describes how to open the Print Sample sample app. If you are using your own app, open it in Visual Studio and skip to the next step.

Important  The code examples shown in this Quickstart refer to the Print Sample sample app. You might need to add more code from the sample app to use these examples in your own app.

 

  1. Open the Windows Store app Print sample and download the C# example to your computer.
  2. In Visual Studio, click File > Open Project and go to the folder that contains the solution file of the sample app that you downloaded in the previous step.
  3. Select the PrintSample solution file and click Open.

2. Build and test the app

  1. Click Build > Build Solution to build the app you are working on. Make sure that there are no error messages in the Output pane at the bottom of the screen.
  2. Click Debug > Start Without Debugging.
  3. Verify that, after a few seconds, the screen displays the PrintSample app.
  4. If the app runs without error, return to Visual Studio and click Debug > Stop Debugging.

3. Register for Windows printing

At this point, you should have an app that displays a screen with some content.

The first step to add printing to your app is to register for the Print contract. Your app must do this on every screen from which you want your customer to be able to print.

Tip  If you need to support printing from more than one page in your app, you can put this print code in a common base class and have your app pages derive from it. For an example of how to do this, see the BasePrintPage class in the Print Sample sample app.

 

Note  Only the screen that is displayed to the user can be registered for printing. If one screen of your app has registered for printing, it must unregister for printing when it exits. If it is replaced by another screen, the next screen must register for a new Print contract when it opens.

 

First, declare the PrintManager and PrintDocument. The PrintManager type is in the Windows.Graphics.Printing namespace along with types to support other Windows printing functionality. The PrintDocument type is in the Windows.UI.Xaml.Printing namespace along with other types that support preparing XAML content for printing. You can make it easier to write your printing code by adding the following using or Imports statements to your page.

using namespace Windows::Graphics::Printing;
using Windows.Graphics.Printing;
using Windows.UI.Xaml.Printing;
Imports Windows.Graphics.Printing
Imports Windows.UI.Xaml.Printing

Create the registration method to initialize the properties your app will require for printing.

In the Print Sample sample app, this method is in the base class that is used by the different displays of the app.

void BasePrintPage::RegisterForPrinting()
{
    // Create the PrintDocument.
    m_printDocument = ref new PrintDocument();

    // Save the DocumentSource.
    m_printDocumentSource = m_printDocument->DocumentSource;

    // Add an event handler which creates preview pages.
    m_printDocument->Paginate += ref new Windows::UI::Xaml::Printing::PaginateEventHandler(this, &BasePrintPage::CreatePrintPreviewPages);

    // Add an event handler which provides a specified preview page.
    m_printDocument->GetPreviewPage += ref new Windows::UI::Xaml::Printing::GetPreviewPageEventHandler(this, &BasePrintPage::GetPrintPreviewPage);

    // Add an event handler which provides all final print pages.
    m_printDocument->AddPages += ref new Windows::UI::Xaml::Printing::AddPagesEventHandler(this, &BasePrintPage::AddPrintPages);

    // Create a PrintManager and add a handler for printing initialization.
    PrintManager^ printMan = PrintManager::GetForCurrentView();

    m_printTaskRequestedEventToken = printMan->PrintTaskRequested += ref new TypedEventHandler<PrintManager^, PrintTaskRequestedEventArgs^>(this, &BasePrintPage::PrintTaskRequested);        

    // Initialize print content for this scenario
    PreparePrintContent();
}
protected virtual void RegisterForPrinting()
{
    // Create the PrintDocument.
    printDocument = new PrintDocument();

    // Save the DocumentSource.
    printDocumentSource = printDocument.DocumentSource;

    // Add an event handler which creates preview pages.
    printDocument.Paginate += CreatePrintPreviewPages;

    // Add an event handler which provides a specified preview page.
    printDocument.GetPreviewPage += GetPrintPreviewPage;

    // Add an event handler which provides all final print pages.
    printDocument.AddPages += AddPrintPages;

    // Create a PrintManager and add a handler for printing initialization.
    PrintManager printMan = PrintManager.GetForCurrentView();
    printMan.PrintTaskRequested += PrintTaskRequested;

    // Initialize print content for this scenario
    PreparePrintContent();
}
Protected Overridable Sub RegisterForPrinting()
    ' Create the PrintDocument.
    printDocument = New PrintDocument

    ' Save the DocumentSource.
    printDocumentSource = printDocument.DocumentSource

    ' Add an event handler which creates preview pages.
    AddHandler printDocument.Paginate, AddressOf CreatePrintPreviewPages

    ' Add an event handler which provides a specified preview page.
    AddHandler printDocument.GetPreviewPage, AddressOf GetPrintPreviewPage

    ' Add an event handler which provides all final print pages.
    AddHandler printDocument.AddPages, AddressOf AddPrintPages

    ' Create a PrintManager and add a handler for printing initialization.
    Dim printMan As PrintManager = PrintManager.GetForCurrentView
    AddHandler printMan.PrintTaskRequested, AddressOf PrintTaskRequested

    ' Initialize print content for this scenario
    PreparePrintContent()
End Sub

When the user goes to the page, register for printing by creating instances of PrintManager and PrintDocument and registering handlers for their printing events.

void ScenarioInput2::OnNavigatedTo(NavigationEventArgs^ e)
{
    BasePrintPage::OnNavigatedTo(e);

    RegisterForPrinting();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Get a pointer to our main page
    rootPage = e.Parameter as MainPage;

    // init printing 
    RegisterForPrinting();
}
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
    ' Get a pointer to our main page
    rootPage = TryCast(e.Parameter, MainPage)

    ' init printing 
    RegisterForPrinting()
End Sub

When the user leaves the page, disconnect the printing event handlers. If you have a multiple-page app and don't disconnect printing, an exception is thrown when the user leaves the page and then comes back to it.

void ScenarioInput2::OnNavigatedFrom(NavigationEventArgs^ e)
{
    UnregisterForPrinting() ;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    UnregisterForPrinting();
}
Protected Overrides Sub OnNavigatedFrom(e As NavigationEventArgs)
    UnregisterForPrinting()
End Sub

4. Format your app's content for printing

Windows printing raises events when the user wants to print and when it wants the app to provide the formatted content to print.

When a user selects a printer on the Devices charm, the PrintTaskRequested event is raised. The PrintTaskRequested event handler shown in this step creates a PrintTask by calling the PrintTaskRequest.CreatePrintTask method and passes the title for the print page and the name of a PrintTaskSourceRequestedHandler delegate. Notice that in this example, the PrintTaskSourceRequestedHandler is defined inline. The PrintTaskSourceRequestedHandler provides the formatted content for printing and is described later.

In this example, a completion handler is also defined to catch errors. It's a good idea to handle completion events because then your app can let the user know if an error occurred and provide possible solutions. Likewise, your app could use the completion event to indicate subsequent steps for the user to take after the print job is successful.

void BasePrintPage::PrintTaskRequested(PrintManager^ sender, PrintTaskRequestedEventArgs^ e)
{
    auto printTaskRef = std::make_shared<PrintTask^>(nullptr);
    *printTaskRef = e->Request->CreatePrintTask("C++ Printing SDK Sample", ref new Windows::Graphics::Printing::PrintTaskSourceRequestedHandler(
                               [=](PrintTaskSourceRequestedArgs^ args)
                               {
                                   PrintTask^ printTask = *printTaskRef;

                                   // Print Task event handler is invoked when the print job is completed.
                                   printTask->Completed += ref new TypedEventHandler<PrintTask^, PrintTaskCompletedEventArgs^>(
                                   [=](PrintTask^ sender, PrintTaskCompletedEventArgs^ e)
                                   {
                                        // Notify the user when the print operation fails.
                                        if (e->Completion == Windows::Graphics::Printing::PrintTaskCompletion::Failed)
                                        {
                                            auto callback = ref new Windows::UI::Core::DispatchedHandler(
                                            [=]()
                                            {
                                                m_rootPage->NotifyUser(ref new String(L"Failed to print."), NotifyType::ErrorMessage);
                                            });

                                            Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, callback);
                                        }
                                   });

                                   args->SetSource(m_printDocumentSource);
                               }));
}
protected virtual void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
    PrintTask printTask = null;
    printTask = e.Request.CreatePrintTask("C# Printing SDK Sample", sourceRequested =>
        {
            // Print Task event handler is invoked when the print job is completed.
            printTask.Completed += async (s, args) =>
            {
                // Notify the user when the print operation fails.
                if (args.Completion == PrintTaskCompletion.Failed)
                {
                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        rootPage.NotifyUser("Failed to print.", NotifyType.ErrorMessage);
                    });
                }
            };

            sourceRequested.SetSource(printDocumentSource);
        });
} 
Protected Overridable Sub PrintTaskRequested(sender As PrintManager, e As PrintTaskRequestedEventArgs)
    Dim printTask As PrintTask = Nothing
    printTask = e.Request.CreatePrintTask("VB Printing SDK Sample", Sub(sourceRequested)

                                                                     ''' <summary>
                                                                     ''' Option change event handler
                                                                     ''' </summary>
                                                                     ''' <param name="s">The print task option details for which an option changed.</param>
                                                                     ''' <param name="args">The event arguments containing the id of the changed option.</param>
                                                                     AddHandler printTask.Completed,
                                                                         Async Sub(s, args)
                                                                             '' Notify the user when the print operation fails.
                                                                             If args.Completion = PrintTaskCompletion.Failed Then
                                                                                 Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                                                                 Sub()
                                                                                     rootPage.NotifyUser("Failed to print.", NotifyType.ErrorMessage)
                                                                                 End Sub)
                                                                             End If
                                                                         End Sub

                                                                     sourceRequested.SetSource(printDocumentSource)
                                                                 End Sub)
End Sub

After the print task is created, the PrintManager requests a collection of print pages to show in the print preview UI by raising the Paginate event. In the Paginate event handler, you create the pages to show in the print preview UI and to send to the printer. The code you use to prepare your app's content for printing is specific to your app and the content you print.

This example of a Paginate event handler is taken from the Print sample sample app. Refer to the sample app's source code to see how it formats its content for printing.

void BasePrintPage::CreatePrintPreviewPages(Object^ sender, PaginateEventArgs^ e)
{
    // Clear the cache of preview pages 
    m_printPreviewPages.clear();

    // Clear the printing root of preview pages
    PrintingRoot->Children->Clear();

    // This variable keeps track of the last RichTextBlockOverflow element that was added to a page which will be printed
    RichTextBlockOverflow^ lastRTBOOnPage;

    // Get the PrintTaskOptions
    PrintTaskOptions^ printingOptions = safe_cast<PrintTaskOptions^>(e->PrintTaskOptions);

    // Get the page description to deterimine how big the page is
    PrintPageDescription pageDescription = printingOptions->GetPageDescription(0);

    // We know there is at least one page to be printed. passing null as the first parameter to
    // AddOnePrintPreviewPage tells the function to add the first page.
    lastRTBOOnPage = AddOnePrintPreviewPage(nullptr, pageDescription);

    // We know there are more pages to be added as long as the last RichTextBoxOverflow added to a print preview
    // page has extra content
    while (lastRTBOOnPage->HasOverflowContent && lastRTBOOnPage->Visibility == Windows::UI::Xaml::Visibility::Visible)
    {
        lastRTBOOnPage = AddOnePrintPreviewPage(lastRTBOOnPage, pageDescription);
    }

    OnContentCreated();

    PrintDocument^ printDocument = safe_cast<PrintDocument^>(sender);

    // Report the number of preview pages created
    printDocument->SetPreviewPageCount(static_cast<int>(m_printPreviewPages.size()), PreviewPageCountType::Intermediate);
}
protected virtual void CreatePrintPreviewPages(object sender, PaginateEventArgs e)
{
    // Clear the cache of preview pages 
    printPreviewPages.Clear();
    
    // Clear the printing root of preview pages
    PrintingRoot.Children.Clear();

    // This variable keeps track of the last RichTextBlockOverflow element that was added to a page which will be printed
    RichTextBlockOverflow lastRTBOOnPage;

    // Get the PrintTaskOptions
    PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);

    // Get the page description to deterimine how big the page is
    PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

    // We know there is at least one page to be printed. passing null as the first parameter to
    // AddOnePrintPreviewPage tells the function to add the first page.
    lastRTBOOnPage = AddOnePrintPreviewPage(null, pageDescription);

    // We know there are more pages to be added as long as the last RichTextBoxOverflow added to a print preview
    // page has extra content
    while (lastRTBOOnPage.HasOverflowContent && lastRTBOOnPage.Visibility == Windows.UI.Xaml.Visibility.Visible)
    {
        lastRTBOOnPage = AddOnePrintPreviewPage(lastRTBOOnPage, pageDescription);
    }

    if (pagesCreated != null)
    {
        pagesCreated.Invoke(printPreviewPages, null);
    }

    PrintDocument printDoc = (PrintDocument)sender;

    // Report the number of preview pages created
    printDoc.SetPreviewPageCount(printPreviewPages.Count, PreviewPageCountType.Intermediate);
}
Protected Overridable Sub CreatePrintPreviewPages(sender As Object, e As PaginateEventArgs)
    ' Clear the cache of preview pages 
    printPreviewPages.Clear()

    ' Clear the printing root of preview pages
    PrintingRootCanvas.Children.Clear()

    ' This variable keeps track of the last RichTextBlockOverflow element that was added to a page which will be printed
    Dim lastRTBOOnPage As RichTextBlockOverflow

    ' Get the PrintTaskOptions
    Dim printingOptions As PrintTaskOptions = DirectCast(e.PrintTaskOptions, PrintTaskOptions)

    ' Get the page description to deterimine how big the page is
    Dim pageDescription As PrintPageDescription = printingOptions.GetPageDescription(0)

    ' We know there is at least one page to be printed. passing null as the first parameter to
    ' AddOnePrintPreviewPage tells the function to add the first page.
    lastRTBOOnPage = AddOnePrintPreviewPage(Nothing, pageDescription)

    ' For this app: we know there are more pages to be added as long as the last RichTextBlockOverflow 
    ' added to a print preview page has extra content and is visible
    While lastRTBOOnPage.HasOverflowContent And lastRTBOOnPage.Visibility = Xaml.Visibility.Visible
        lastRTBOOnPage = AddOnePrintPreviewPage(lastRTBOOnPage, pageDescription)
    End While

    RaiseEvent pagesCreated(printPreviewPages, Nothing)

    Dim printDoc As PrintDocument = sender

    ' Report the number of preview pages created
    printDoc.SetPreviewPageCount(printPreviewPages.Count, PreviewPageCountType.Intermediate)
End Sub

The preceding example shows a very simple print scenario. Only one page is created, and the content is scaled to fill all of the available area. Other topics in this section show examples of more complex printing scenarios.

In the AddPages event handler, you add pages from the page collection to the PrintDocument object to be sent to the printer. If a user specifies particular pages or a range of pages to print, you use that information here to add only the pages that will actually be sent to the printer.

This example of an AddPages event handler is taken from the Print sample sample app.

void BasePrintPage::AddPrintPages(Object^ sender, AddPagesEventArgs^ e)
{
    PrintDocument^ printDocument = safe_cast<PrintDocument^>(sender);

    // Loop over all of the preview pages and add each one to  add each page to be printied
    for (size_t i = 0; i < m_printPreviewPages.size(); i++)
    {
        printDocument->AddPage(m_printPreviewPages[i]);
    }

    // Indicate that all of the print pages have been provided
    printDocument->AddPagesComplete();
}
protected virtual void AddPrintPages(object sender, AddPagesEventArgs e)
{
    // Loop over all of the preview pages and add each one to  add each page to be printied
    for (int i = 0; i < printPreviewPages.Count; i++)
    {
        // We should have all pages ready at this point...
        printDocument.AddPage(printPreviewPages[i]);
    }

    PrintDocument printDoc = (PrintDocument)sender;
    
    // Indicate that all of the print pages have been provided
    printDoc.AddPagesComplete();
}
Protected Overridable Sub AddPrintPages(sender As Object, e As AddPagesEventArgs)
    Dim printDoc As PrintDocument = sender

    ' Loop over all of the preview pages and add each one to  add each page to be printied
    For i = 0 To printPreviewPages.Count - 1
        printDoc.AddPage(printPreviewPages(i))
    Next

    ' Indicate that all of the print pages have been provided
    printDoc.AddPagesComplete()
End Sub

Summary and next steps

In this Quickstart, you added Windows printing to your app, without modifying how users interact with your app.

In the next tutorial, How to print using an in-app print button, you will invoke print functionality from a Windows Store app using C++, C#, or Visual Basic with a print button in the app. From there, you can explore some more advanced printing functions.

Your app can give the user even more print options, as How to change standard options in the print preview UI and How to add custom options to the print preview UI demonstrate.

And for more printing scenarios that are available in Windows Store apps, see the Windows Store app Print sample sample app.

Roadmap for Windows Runtime apps using C# or Visual Basic