從您的應用程式列印Print from your app

重要 APIImportant APIs

了解如何從通用 Windows 應用程式列印文件。Learn how to print documents from a Universal Windows app. 本主題也示範如何列印特定頁面。This topic also shows how to print specific pages. 如需預覽列印 UI 的更進階變更,請參閱自訂預覽列印 UIFor more advanced changes to the print preview UI, see Customize the print preview UI.

提示

 本主題大部分的範例都是以 通用 Windows 平臺 (uwp) print 範例為基礎,這是 GitHub 上 通用 Windows 平臺 (UWP) 應用程式範例 存放庫的一部分。 Most of the examples in this topic are based on the Universal Windows Platform (UWP) print sample, which is part of the Universal Windows Platform (UWP) app samples repo on GitHub.

註冊列印Register for printing

新增列印至 app 的第一個步驟是註冊列印協定。The first step to add printing to your app is to register for the Print contract. 您的 App 必須在要讓您的使用者能夠進行列印的每個畫面上登錄列印協定。Your app must do this on every screen from which you want your user to be able to print. 只有對使用者顯示的畫面可以登錄列印。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.

提示

 如果您需要在 App 內一個以上的頁面中支援列印,可以將這個列印程式碼放置於常用協助程式類別中,並讓您的 App 頁面重複使用它。 If you need to support printing from more than one page in your app, you can put this print code in a common helper class and have your app pages reuse it. 如需如何執行這項操作的範例,請參閱 UWP 列印範例中的 PrintHelper 類別。For an example of how to do this, see the PrintHelper class in the UWP print sample.

首先,宣告 PrintManagerPrintDocumentFirst, declare the PrintManager and PrintDocument. PrintManager 類型位於 Windows.Graphics.Printing 命名空間以及支援其他 Windows 列印功能的類型中。The PrintManager type is in the Windows.Graphics.Printing namespace along with types to support other Windows printing functionality. PrintDocument 類型位於 Windows.UI.Xaml.Printing 命名空間以及其他支援準備列印 XAML 內容的類型中。The PrintDocument type is in the Windows.UI.Xaml.Printing namespace along with other types that support preparing XAML content for printing. 您可以藉由將下列 usingImports 陳述式新增到頁面中,更輕鬆地撰寫列印程式碼。You can make it easier to write your printing code by adding the following using or Imports statements to your page.

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

PrintDocument 類別是用來處理大部分的 app 與 PrintManager 之間的互動,但是它會公開自己的數個回呼。The PrintDocument class is used to handle much of the interaction between the app and the PrintManager, but it exposes several callbacks of its own. 在註冊期間,建立 PrintManagerPrintDocument 的執行個體,並註冊其列印事件的處理常式。During registration, create instances of PrintManager and PrintDocument and register handlers for their printing events.

UWP 列印範例 中,註冊是由 RegisterForPrinting 方法執行。In the UWP print sample, registration is performed by the RegisterForPrinting method.

public virtual void RegisterForPrinting()
{
   printDocument = new PrintDocument();
   printDocumentSource = printDocument.DocumentSource;
   printDocument.Paginate += CreatePrintPreviewPages;
   printDocument.GetPreviewPage += GetPrintPreviewPage;
   printDocument.AddPages += AddPrintPages;

   PrintManager printMan = PrintManager.GetForCurrentView();
   printMan.PrintTaskRequested += PrintTaskRequested;
}

當使用者前往支援列印的頁面時,它會初始化 OnNavigatedTo 方法內的註冊。When the user goes to a page that supports printing, it initiates the registration within the OnNavigatedTo method.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
   // Initialize common helper class and register for printing
   printHelper = new PrintHelper(this);
   printHelper.RegisterForPrinting();

   // Initialize print content for this scenario
   printHelper.PreparePrintContent(new PageToPrint());

   // Tell the user how to print
   MainPage.Current.NotifyUser("Print contract registered with customization, use the Print button to print.", NotifyType.StatusMessage);
}

在此範例中,會在方法中取消註冊事件處理常式 UnregisterForPrintingIn the sample, the event handlers are unregistered in the UnregisterForPrinting method.

public virtual void UnregisterForPrinting()
{
    if (printDocument == null)
    {
        return;
    }

    printDocument.Paginate -= CreatePrintPreviewPages;
    printDocument.GetPreviewPage -= GetPrintPreviewPage;
    printDocument.AddPages -= AddPrintPages;

    PrintManager printMan = PrintManager.GetForCurrentView();
    printMan.PrintTaskRequested -= PrintTaskRequested;
}

當使用者離開支援列印的頁面時,會在方法內取消註冊事件處理常式 OnNavigatedFromWhen the user leaves a page that supports printing, the event handlers are unregistered within the OnNavigatedFrom method.

注意

如果您有多頁應用程式,但未中斷列印,則當使用者離開頁面,然後返回該頁面時,將會擲回例外狀況。If you have a multiple-page app and don't disconnect printing, an exception is thrown when the user leaves the page and then returns to it.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
   if (printHelper != null)
   {
         printHelper.UnregisterForPrinting();
   }
}

建立列印按鈕Create a print button

將列印按鈕新增至 app 畫面中要放置按鈕的位置。Add a print button to your app's screen where you'd like it to appear. 確定按鈕不會干擾您要列印的內容。Make sure that it doesn't interfere with the content that you want to print.

<Button x:Name="InvokePrintingButton" Content="Print" Click="OnPrintButtonClick"/>

接下來,將事件處理常式新增至您的 app 的程式碼,以處理 Click 事件。Next, add an event handler to your app's code to handle the click event. 使用 ShowPrintUIAsync 方法,開始從 app 列印。Use the ShowPrintUIAsync method to start printing from your app. ShowPrintUIAsync 是非同步方法,會顯示適當的列印視窗。ShowPrintUIAsync is an asynchronous method that displays the appropriate printing window. 我們建議先呼叫 IsSupported 方法來檢查 App 是否是在支援列印的裝置上執行 (並處理不支援的情況)。We recommend calling the IsSupported method first in order to check that the app is being run on a device that supports printing (and handle the case in which it is not). 如果當下因任何其他原因而無法列印,ShowPrintUIAsync 會擲回例外狀況。If printing can't be performed at that time for any other reason, ShowPrintUIAsync will throw an exception. 我們建議攔截這些例外狀況,並在無法繼續進行列印時讓使用者知道。We recommend catching these exceptions and letting the user know when printing can't proceed.

在這個範例中,事件處理常式中會針對按鈕點擊顯示列印視窗。In this example, a print window is displayed in the event handler for a button click. 如果這個方法擲回例外狀況 (因為當時無法執行列印),ContentDialog 控制項會通知使用者該情況。If the method throws an exception (because printing can't be performed at that time), a ContentDialog control informs the user of the situation.

async private void OnPrintButtonClick(object sender, RoutedEventArgs e)
{
    if (Windows.Graphics.Printing.PrintManager.IsSupported())
    {
        try
        {
            // Show print UI
            await Windows.Graphics.Printing.PrintManager.ShowPrintUIAsync();

        }
        catch
        {
            // Printing cannot proceed at this time
            ContentDialog noPrintingDialog = new ContentDialog()
            {
                Title = "Printing error",
                Content = "\nSorry, printing can' t proceed at this time.", PrimaryButtonText = "OK"
            };
            await noPrintingDialog.ShowAsync();
        }
    }
    else
    {
        // Printing is not supported on this device
        ContentDialog noPrintingDialog = new ContentDialog()
        {
            Title = "Printing not supported",
            Content = "\nSorry, printing is not supported on this device.",PrimaryButtonText = "OK"
        };
        await noPrintingDialog.ShowAsync();
    }
}

將 app 的內容格式化Format your app's content

呼叫 ShowPrintUIAsync 時,會引發 PrintTaskRequested 事件。When ShowPrintUIAsync is called, the PrintTaskRequested event is raised. 此步驟顯示的 PrintTaskRequested 事件處理常式,會藉由呼叫 PrintTaskRequest.CreatePrintTask 方法及傳遞列印頁面的標題和 PrintTaskSourceRequestedHandler 委派的名稱來建立 PrintTaskThe 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. 請注意,這個範例中的 PrintTaskSourceRequestedHandler 是以內嵌的方式定義。Notice that in this example, the PrintTaskSourceRequestedHandler is defined inline. PrintTaskSourceRequestedHandler 會提供要列印的格式化內容,稍後會加以描述。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.

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 scenarioPage.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
               {
                     MainPage.Current.NotifyUser("Failed to print.", NotifyType.ErrorMessage);
               });
            }
         };

         sourceRequested.SetSource(printDocumentSource);
   });
}

建立列印工作之後,PrintManager 會藉由引發 Paginate 事件,要求在預覽列印 UI 中顯示列印頁面集合。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. 這會對應至 IPrintPreviewPageCollection 介面的 Paginate 方法。This corresponds with the Paginate method of the IPrintPreviewPageCollection interface. 您在註冊期間建立的事件處理常式會在此時呼叫。The event handler you created during registration will be called at this time.

重要

 如果使用者變更列印設定,會再次呼叫編頁事件處理常式,讓您自動重排內容。 If the user changes print settings, the paginate event handler will be called again to allow you to reflow the content. 若要獲得最佳的使用者經驗,建議您先檢查設定,再自動重排內容,並避免在不必要時重新初始化已編頁的內容。For the best user experience, we recommend checking the settings before you reflow the content and avoid reinitializing the paginated content when it's not necessary.

Paginate 事件處理常式中 (UWP 列印範例中的 CreatePrintPreviewPages 方法),建立要在預覽列印 UI 中顯示並傳送到印表機的頁面。In the Paginate event handler (the CreatePrintPreviewPages method in the UWP print sample), 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. 請參閱 UWP 列印範例原始程式碼,了解如何將要列印的內容格式化。Refer to the UWP print sample source code to see how it formats its content for printing.

protected virtual void CreatePrintPreviewPages(object sender, PaginateEventArgs e)
{
   // Clear the cache of preview pages
   printPreviewPages.Clear();

   // Clear the print canvas of preview pages
   PrintCanvas.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 (PreviewPagesCreated != null)
   {
         PreviewPagesCreated.Invoke(printPreviewPages, null);
   }

   PrintDocument printDoc = (PrintDocument)sender;

   // Report the number of preview pages created
   printDoc.SetPreviewPageCount(printPreviewPages.Count, PreviewPageCountType.Intermediate);
}

當特定頁面顯示在預覽列印視窗中時,PrintManager 會引發 GetPreviewPage 事件。When a particular page is to be shown in the print preview window, the PrintManager raises the GetPreviewPage event. 這會對應至 IPrintPreviewPageCollection 介面的 MakePage 方法。This corresponds with the MakePage method of the IPrintPreviewPageCollection interface. 您在註冊期間建立的事件處理常式會在此時呼叫。The event handler you created during registration will be called at this time.

GetPreviewPage 事件處理常式 (UWP 列印範例中的 GetPrintPreviewPage 方法) 中,在列印文件上設定適當的頁面。In the GetPreviewPage event handler (the GetPrintPreviewPage method in the UWP print sample), set the appropriate page on the print document.

protected virtual void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
{
   PrintDocument printDoc = (PrintDocument)sender;
   printDoc.SetPreviewPage(e.PageNumber, printPreviewPages[e.PageNumber - 1]);
}

最後,當使用者按一下 [列印] 按鈕時,PrintManager 會要求傳送至印表機的頁面最終集合,方法是呼叫 IDocumentPageSource 介面的 MakeDocument 方法。Finally, once the user clicks the print button, the PrintManager requests the final collection of pages to send to the printer by calling the MakeDocument method of the IDocumentPageSource interface. 在 XAML 中,這會引發 AddPages 事件。In XAML, this raises the AddPages event. 您在註冊期間建立的事件處理常式會在此時呼叫。The event handler you created during registration will be called at this time.

AddPages 事件處理常式 (UWP 列印範例中的 AddPrintPages 方法) 中,將來自頁面集合的頁面新增至要傳送到印表機的 PrintDocument 物件。In the AddPages event handler (the AddPrintPages method in the UWP print sample), 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.

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

準備列印選項Prepare print options

接下來準備列印選項。Next prepare print options. 本節將說明如何設定頁面範圍選項以允許列印特定頁面,做為範例。As an example, this section will describe how to set the page range option to allow printing of specific pages. 如需其他進階選項,請參閱自訂預覽列印 UIFor more advanced options, see Customize the print preview UI.

這個步驟會建立新的列印選項、定義選項支援的值清單,以及將選項新增至預覽列印 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. 頁面範圍選項包含下列設定: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.

首先,修改 PrintTaskRequested 事件處理常式以新增程式碼,以便取得 PrintTaskOptionDetails 物件。First, modify the PrintTaskRequested event handler to add the code to get a PrintTaskOptionDetails object.

PrintTaskOptionDetails printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);

清除預覽列印 UI 中顯示的選項清單,並新增當使用者想要從 app 列印時要顯示的選項。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.

注意

 這些選項會依照附加的順序出現在預覽列印 UI 中,第一個選項會在視窗的頂端顯示。 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.

IList<string> displayedOptions = printDetailedOptions.DisplayedOptions;

displayedOptions.Clear();
displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Copies);
displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Orientation);
displayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.ColorMode);

建立新的列印選項並初始化選項值的清單。Create the new print option and initialize the list of option values.

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

新增自訂列印選項並指派事件處理常式。Add your custom print option and assign the event handler. 自訂選項會最後附加,如此該選項就會出現在選項清單的底部。The custom option is appended last so that it appears at the bottom of the list of options. 但是您可以將它放到清單中的任意位置,自訂列印選項不一定要最後新增。But you can put it anywhere in the list, custom print options don't need to be added last.

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

// Create new edit option
PrintCustomTextOptionDetails pageRangeEdit = printDetailedOptions.CreateTextOption("PageRangeEdit", "Range");

// Register the handler for the option change event
printDetailedOptions.OptionChanged += printDetailedOptions_OptionChanged;

CreateTextOption 方法會建立 [範圍] 文字方塊。The CreateTextOption method 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.

處理列印選項變更Handle print option changes

OptionChanged 事件處理常式會執行兩個動作。The OptionChanged 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.

此範例說明如何在 UWP 列印範例中處理列印選項變更事件。This example shows how print option change events are handled in the UWP print sample.

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 scenarioPage.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
               {
                     ShowContent(null);
               });
               break;
            case "PrintSelection":
               await scenarioPage.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
               {
                     Scenario4PageRange page = (Scenario4PageRange)scenarioPage;
                     PageToPrint pageContent = (PageToPrint)page.PrintFrame.Content;
                     ShowContent(pageContent.TextContentBlock.SelectedText);
               });
               RemovePageRangeEdit(sender);
               break;
            default:
               await scenarioPage.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;
            }
         }
   }
}

提示

 如需 GetPagesInRange 如何剖析使用者在 [範圍] 文字方塊中輸入之頁面範圍的詳細資訊,請參閱 UWP 列印範例 中的方法。 See the GetPagesInRange method in the UWP print sample for details on how to parse the page range the user enters in the Range text box.

預覽已選取的頁面Preview selected pages

將要列印的 app 內容格式化的方式,取決於您的 app 及其內容的性質。How you format your app's content for printing depends on the nature of your app and its content. UWP 列印範例中使用的列印協助程式類別,用於格式化要列印的內容。A print helper class in used in the UWP print sample to format the content for printing.

列印頁面的子集時,有數種方式可在預覽列印中顯示內容。When printing a subset of 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.

  • 不論是否指定頁面範圍,在預覽列印中都顯示所有頁面,讓使用者自己知道實際要印出的是哪些頁面。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.
  • 在預覽列印中只顯示使用者的頁面範圍內所選取的頁面,並在每次使用者變更頁面範圍時更新顯示。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.
  • 在預覽列印中顯示所有頁面,但是以灰色呈現不在使用者所選頁面範圍內的頁面。Show all the pages in print preview, but grey out the pages that are not in page range selected by the user.