How to print specific pages (XAML)

Learn how to add a page range option to your Windows Store app.

The page range option has these settings:

Option name Action

Print all

Print all pages in the document.

Print Selection

Print only the content the user selected.

Print Range

Display an edit control into which the user can enter the pages to print.

What you need to know

Technologies

Prerequisites

We assume that:

  • You are familiar with C#, C++, or Visual Basic, XAML, events and event handling.
  • You have an installed instance of Microsoft Visual Studio.
  • You have a printer installed.
  • You have a Windows Store app using C++, C#, or Visual Basic to which you want to add the page range option. If you don't have your own app, you can download the Print Sample sample app and use that app.
  • Your app supports basic Windows printing. If you don't have such an app, review Quickstart: Printing from your app to learn how to add basic Windows printing support to your app.

Instructions

Step 1: Open your app in Visual Studio

The procedure described here refers to the PrintSample app from the Print Sample sample app. If you are adding a print button to your own app, open your app in Visual Studio instead of the Print Sample sample app.

  1. Open the Print Sample sample app and download the C# example.
  2. In Visual Studio, click File > Open Project and navigate 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.

Step 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 Print JS Sample app.
  4. If the app runs without error, return to Visual Studio, and then click Debug > Stop Debugging.

Step 3: Add the new option to the print preview UI

This step creates a new print option, defines a list of values that the option supports, and then adds the option to the print preview UI.

  1. Modify the PrintTaskRequested event handler to add the code to get a PrintTaskOptionDetails object.

    using namespace Windows::Graphics::Printing;
    using namespace Windows::Graphics::Printing::OptionDetails;
    
    PrintTaskOptionDetails printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);
    
    Dim printDetailedOptions As PrintTaskOptionDetails = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options)
    
  2. Create the new print option and initialize the list of option values.

    // Create a new list option
    
    PrintCustomItemListOptionDetails^ pageFormat = printDetailedOptions->CreateItemListOption(L"PageRange", L"Page Range");
    pageFormat->AddItem(L"PrintAll", L"Print all");
    pageFormat->AddItem(L"PrintSelection", L"Print Selection");
    pageFormat->AddItem(L"PrintRange", L"Print Range");
    
    // Create a new list option
    
    PrintCustomItemListOptionDetails pageFormat = printDetailedOptions.CreateItemListOption("PageRange", "Page Range");
    pageFormat.AddItem("PrintAll", "Print all");
    pageFormat.AddItem("PrintSelection", "Print Selection");
    pageFormat.AddItem("PrintRange", "Print Range");
    
    ' Create a new list option
    
    Dim pageFormat As PrintCustomItemListOptionDetails = printDetailedOptions.CreateItemListOption("PageRange", "Page Range")
    pageFormat.AddItem("PrintAll", "Print all")
    pageFormat.AddItem("PrintSelection", "Print Selection")
    pageFormat.AddItem("PrintRange", "Print Range")
    
  3. Clear the list of options that are shown in the print preview UI and add the options that you want to display when the user wants to print from the app.

    Note The options appear in the print preview UI in the same order they are appended, with the first option shown at the top of the window.

    // Choose the printer options to be shown.
    // The order in which the options are appended determines the order in which they appear in the UI
    displayedOptions->Clear();
    
    displayedOptions->Append(StandardPrintTaskOptions::Copies);
    displayedOptions->Append(StandardPrintTaskOptions::Orientation);
    displayedOptions->Append(StandardPrintTaskOptions::ColorMode);
    
    // Choose the printer options to be shown.
    // The order in which the options are appended determines the order in which they appear in the UI
    displayedOptions.Clear();
    
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Copies);
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Orientation);
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.ColorMode);
    
    ' Choose the printer options to be shown.
    ' The order in which the options are appended determines the order in which they appear in the UI
    displayedOptions.Clear()
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Copies)
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Orientation)
    displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.ColorMode)
    
  4. Add your custom print option and assign the event handler.

    In this example, the custom option is appended last so that it appears at the bottom of the list of options. But you, put it anywhere in the list. Custom print options don't need to be added last.

    Also notice the command that creates the Range text box. This is where the user enters the specific pages they want to print when they select the Print Range option.

    // Add the custom option to the option list
    displayedOptions->Append(L"PageRange");
    
    // Create new edit option
    PrintCustomTextOptionDetails^ pageRangeEdit = printDetailedOptions->CreateTextOption(L"PageRangeEdit", L"Range");
    
    // Add the custom option to the option list
    displayedOptions.Add("PageRange");
    
    // Create new edit option
    PrintCustomTextOptionDetails pageRangeEdit = printDetailedOptions.CreateTextOption("PageRangeEdit", "Range");
    
    ' Add the custom option to the option list
    displayedOptions.Add("PageRange")
    
    ' Create new edit option
    Dim pageRangeEdit As PrintCustomTextOptionDetails = printDetailedOptions.CreateTextOption("PageRangeEdit", "Range")
    

Step 4: Create change event handler for option changes

The change event handler does two things. First, it shows and hides the text edit field for the page range depending on the page range option that the user selected. Second, it tests the text entered into the page range text box to make sure that it represents a valid page range for the document.

  1. Create the function that handles option-change events. This example is from scenario5 of the Print Sample sample app.

    void ScenarioInput5::printDetailedOptions_OptionChanged(PrintTaskOptionDetails^ sender, PrintTaskOptionChangedEventArgs^ args)
    {
        if (args->OptionId == nullptr)
            return;
    
        String^ optionId = args->OptionId->ToString();
    
        // Handle change in Page Range Option
    
        if (optionId == L"PageRange")
        {
            IPrintOptionDetails^ pageRange = sender->Options->Lookup(optionId);
            String^ pageRangeValue = pageRange->Value->ToString();
    
            m_selectionMode = false;
    
            if(pageRangeValue == L"PrintRange")
            {
                // Add PageRangeEdit custom option to the option list
                sender->DisplayedOptions->Append(L"PageRangeEdit");
                m_pageRangeEditVisible = true;
    
                auto callback = ref new Windows::UI::Core::DispatchedHandler(
                [=]()
                {
                    // Restore first page to its default layout
                    // Undo any changes made by a text selection
                    ShowContent(nullptr);
                });
    
                Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, callback);
            }
            else if(pageRangeValue == L"PrintSelection")
            {
                Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
                                     ref new Windows::UI::Core::DispatchedHandler([this]()
                                                                {
                                                                    ScenarioOutput5^ outputScenario = safe_cast<ScenarioOutput5^>(RootPage->OutputFrame->Content);
    
                                                                    ShowContent(outputScenario->SelectedText);
                                                                }));
                RemovePageRangeEdit(sender);
            }
            else
            {
                auto callback = ref new Windows::UI::Core::DispatchedHandler(
                [=]()
                {
                    // Restore first page to its default layout
                    // Undo any changes made by a text selection
                    ShowContent(nullptr);
                });
    
                Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, callback);
                RemovePageRangeEdit(sender);
            }
    
            RefreshPreview();
    
        }
        else if (optionId == L"PageRangeEdit")
        {
            IPrintOptionDetails^ pageRange = sender->Options->Lookup(optionId);
    
            // Expected range format (p1,p2...)*, (p3-p9)* ...
            std::wregex rangePattern(L"^\\s*\\d+\\s*(\\-\\s*\\d+\\s*)?(\\,\\s*\\d+\\s*(\\-\\s*\\d+\\s*)?)*$");
            std::wstring pageRangeValue(pageRange->Value->ToString()->Data());
    
            if(!std::regex_match(pageRangeValue.begin(), pageRangeValue.end(), rangePattern))
            {
                pageRange->ErrorText = L"Invalid Page Range (eg: 1-3, 5)";
            }
            else
            {
                pageRange->ErrorText = L"";
                try
                {
                    GetPagesInRange(pageRange->Value->ToString());
                }
                catch(InvalidPageException* rangeException)
                {
                    pageRange->ErrorText = ref new String(rangeException->get_DisplayMessage().data());
    
                    delete rangeException;
                }
    
                RefreshPreview();
            }
        }
    }
    
    async void printDetailedOptions_OptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
    {
        if (args.OptionId == null)
            return;
    
        string optionId = args.OptionId.ToString();
    
        // Handle change in Page Range Option
    
        if (optionId == "PageRange")
        {
            IPrintOptionDetails pageRange = sender.Options[optionId];
            string pageRangeValue = pageRange.Value.ToString();
    
            selectionMode = false;
    
            switch (pageRangeValue)
            {
                case "PrintRange":
                    // Add PageRangeEdit custom option to the option list
                    sender.DisplayedOptions.Add("PageRangeEdit");
                    pageRangeEditVisible = true;
                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                              () =>
                                              {
                                                  ShowContent(null);
                                              });
                    break;
                case "PrintSelection":
                    {
                        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                          () =>
                                          {
                                              ScenarioOutput5 outputContent = (ScenarioOutput5)rootPage.OutputFrame.Content;
                                              ShowContent(outputContent.SelectedText);
                                          });
                        RemovePageRangeEdit(sender);
                    }
                    break;
                default:
                    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                              () =>
                                              {
                                                  ShowContent(null);
                                              });
                    RemovePageRangeEdit(sender);
                    break;
            }
    
            Refresh();
    
        }
        else if (optionId == "PageRangeEdit")
        {
            IPrintOptionDetails pageRange = sender.Options[optionId];
            // Expected range format (p1,p2...)*, (p3-p9)* ...
            if (!Regex.IsMatch(pageRange.Value.ToString(), @"^\s*\d+\s*(\-\s*\d+\s*)?(\,\s*\d+\s*(\-\s*\d+\s*)?)*$"))
            {
                pageRange.ErrorText = "Invalid Page Range (eg: 1-3, 5)";
            }
            else
            {
                pageRange.ErrorText = string.Empty;
                try
                {
                    GetPagesInRange(pageRange.Value.ToString());
                    Refresh();
                }
                catch (InvalidPageException ipex)
                {
                    pageRange.ErrorText = ipex.Message;
                }
            }
        }
    }
    
    Private Sub printDetailedOptions_OptionChanged(sender As PrintTaskOptionDetails, args As PrintTaskOptionChangedEventArgs)
    
        If args.OptionId Is Nothing Then
            Exit Sub
        End If
    
        Dim optionId As String = args.OptionId.ToString
    
        ' Handle change in Page Range Option
    
        If optionId = "PageRange" Then
            Dim pageRange As IPrintOptionDetails = sender.Options(optionId)
            Dim pageRangeValue As String = pageRange.Value.ToString
    
            selectionMode = False
    
            Select Case pageRangeValue
                Case "PrintRange"
                    ' Add PageRangeEdit custom option to the option list
                    sender.DisplayedOptions.Add("PageRangeEdit")
                    pageRangeEditVisible = True
                    Dim ignored = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                                      Sub()
                                                          ' Restore original page layout
                                                          ShowContent(Nothing)
                                                      End Sub)
                    Exit Select
                Case "PrintSelection"
                    Dim ignored = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                                      Sub()
                                                          Dim outputContent As ScenarioOutput5 = DirectCast(rootPage.OutputFrame.Content, ScenarioOutput5)
                                                          ShowContent(outputContent.SelectedText)
                                                      End Sub)
                    RemovePageRangeEdit(sender)
                    Exit Select
                Case Else
                    Dim ignored = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                                      Sub()
                                                          ' Restore original page layout
                                                          ShowContent(Nothing)
                                                      End Sub)
                    RemovePageRangeEdit(sender)
                    Exit Select
            End Select
    
            Refresh()
        ElseIf optionId = "PageRangeEdit" Then
            Dim pageRange As IPrintOptionDetails = sender.Options(optionId)
            ' Expected range format (p1,p2...)*, (p3-p9)* ...
            If Not Regex.IsMatch(pageRange.Value.ToString, "^\s*\d+\s*(\-\s*\d+\s*)?(\,\s*\d+\s*(\-\s*\d+\s*)?)*$") Then
                pageRange.ErrorText = "Invalid Page Range (eg: 1-3, 5)"
            Else
                pageRange.ErrorText = String.Empty
                Try
                    GetPagesInRange(pageRange.Value.ToString)
                    Refresh()
                Catch ipex As InvalidPageException
                    pageRange.ErrorText = ipex.Message
                End Try
            End If
        End If
    End Sub
    
  2. Assign the change event handler in the PrintTaskRequested event handler.

    // Register the handler for the option change event
    printDetailedOptions->OptionChanged += ref new TypedEventHandler<PrintTaskOptionDetails^, PrintTaskOptionChangedEventArgs^>(this, &ScenarioInput5::printDetailedOptions_OptionChanged);
    
    // Register the handler for the option change event
    printDetailedOptions.OptionChanged += printDetailedOptions_OptionChanged;
    
    ' Register the handler for the option change event
    AddHandler printDetailedOptions.OptionChanged, AddressOf printDetailedOptions_OptionChanged
    
  3. See GetPagesInRange in the Print Sample sample app for the details on how to parse the page range the user enters in the Range text box.

Step 5: Print the pages

How you format your app's content for printing depends on the nature of your app and its content. The Print Sample sample app uses the BasePrintPage class to format its content for printing.

When printing a subset of the pages, there are several ways to show the content in the print preview. Regardless of the method you chose to show the page range in the print preview, the printed output must contain only the selected pages.

  1. Show all the pages in the print preview whether a page range is specified or not, leaving the user to know which pages will actually be printed.
  2. Show only the pages selected by the user's page range in the print preview, updating the display whenever the user changes the page range.
  3. Show all the pages in print preview, but grey out the pages that are not in page range selected by the user.

The ScenarioInput5_pagesCreated method in the Print Sample sample app implements option 2.