Share via


從您的應用程式列印

本主題描述如何從通用 Windows 應用程式列印。

如需更進階的功能,請參閱 自訂列印預覽 UI

重要 API

提示

 本主題中的大多數範例都基於通用 Windows 平台 (UWP) 列印範例,這是 GitHub 上通用 Windows 平台 (UWP) 應用程式範例儲存庫的一部分。

註冊列印

將列印新增至應用程式的第一步是註冊列印合約。 您的應用程式必須在您希望使用者能夠列印的每個螢幕上執行此操作。 只有向使用者顯示的螢幕才能註冊列印。 如果應用程式的一個畫面已註冊列印,退出時必須取消註冊列印。 如果它被另一個螢幕替換,則下一個螢幕在開啟時必須註冊新的列印合約。

提示

 如果您需要支援從應用程式中的多個頁面進行列印,您可以將此列印程式碼放入通用幫助程式類別中,並讓您的應用程式頁面重複使用它。 有關如何執行此操作的範例,請參閱 PrintHelperUWP 列印範例中的類別

首先,宣告 PrintManagerPrintDocumentPrintManager 類型位於 Windows.Graphics.Printing 命名空間中,以及支援其他 Windows 列印功能的類型。 PrintDocument 類型位於 Windows.UI.Xaml.Printing 命名空間中,以及其他支持準備 XAML 內容以進行列印的類型。 您可以將下列 usingImports 語句新增至頁面,讓您更輕鬆地撰寫列印程式碼。

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

PrintDocument 類別可用來處理應用程式與 PrintManager 之間的大部分互動,但它會公開自己的數個回呼。 在註冊期間,建立 PrintManagerPrintDocument 的執行個體,併為其列印事件註冊處理常式。

UWP 列印範例中,註冊是由 RegisterForPrinting 方法執行。

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 起始註冊。

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

在範例中,事件處理常式會在方法中 UnregisterForPrinting 取消註冊。

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

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

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

當使用者離開支援列印的頁面時,事件處理常式會在方法內 OnNavigatedFrom 取消註冊。

注意

如果您有多頁應用程式且未中斷列印連線,當使用者離開頁面並返回頁面時,就會擲回例外狀況。

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

建立列印按鈕

將列印按鈕新增至您想要顯示的應用程式畫面。 請確定它不會干擾您想要列印的內容。

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

接下來,將事件處理常式新增至應用程式的程式碼,以處理 Click 事件。 使用 ShowPrintUIAsync 方法來開始從您的應用程式列印。 ShowPrintUIAsync 是一種異步方法,可顯示適當的列印視窗。 建議您先呼叫 IsSupported 方法,以檢查應用程式是否正在支援列印的裝置上執行 (並處理它不是的情況)。 如果因為任何其他原因而無法執行列印,ShowPrintUIAsync 將會擲回例外狀況。 建議您攔截這些例外狀況,並讓使用者知道列印無法繼續。

在此範例中,按鈕按兩下的事件處理常式中會顯示列印視窗。 如果方法擲回例外狀況 (因為當時無法執行列印),ContentDialog 控制項會通知使用者情況。

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

格式化應用程式的內容

呼叫 ShowPrintUIAsync 時,會引發 PrintTaskRequested 事件。 此步驟中顯示的 PrintTaskRequested 事件處理常式會呼叫 PrintTaskRequest.CreatePrintTask 方法來建立 PrintTask,並傳遞列印頁面的標題和 PrintTaskSourceRequestedHandler 委派的名稱。 請注意,在此範例中,PrintTaskSourceRequestedHandler 會內嵌定義。 PrintTaskSourceRequestedHandler 提供列印格式的內容,稍後會加以描述。

在此範例中,也會定義完成處理常式來攔截錯誤。 處理完成事件是個好主意,因為您的應用程式可以讓使用者知道是否發生錯誤並提供可能的解決方案。 同樣地,您的應用程式可以使用完成事件來指出使用者在列印作業成功之後要採取的後續步驟。

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中。 這對應於 IPrintPreviewPageCollection 介面的 Paginate 方法。 此時會呼叫您在註冊期間建立的事件處理常式。

重要

 如果使用者變更列印設定,將會再次呼叫分頁事件處理常式,以允許您重排內容。 為了獲得最佳使用者體驗,建議您在重排內容之前檢查設定,並避免在不需要時重新初始化編頁內容。

Paginate 事件處理常式 (CreatePrintPreviewPagesUWP 列印範例中的方法) 中,建立要在列印預覽 UI 中顯示並傳送到印表機的頁面。 您用來準備應用程式內容以進行列印的程式碼專屬於您的應用程式和您列印的內容。 請參閱 UWP 列印範例原始程式碼,以了解如何格式化其內容以進行列印。

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 事件。 這對應於 IPrintPreviewPageCollection 介面的 Paginate 方法。 此時會呼叫您在註冊期間建立的事件處理常式。

GetPreviewPage 事件處理常式中 (GetPrintPreviewPageUWP 列印範例中的 方法),在列印文件上設定適當的頁面。

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

最後,一旦使用者按兩下列印按鈕,PrintManager 會呼叫 IDocumentPageSource 介面的MakeDocument方法,要求傳送至印表機的最終頁面集合。 在 XAML 中,這會引發 AddPages 事件。 此時會呼叫您在註冊期間建立的事件處理常式。

AddPages 事件處理常式中 (AddPrintPagesUWP 列印範例中的方法),將頁面集合中的頁面新增至要傳送至列印機的 PrintDocument 物件。 如果使用者指定要列印的特定頁面或頁面範圍,您可以使用此處的資訊只新增實際要傳送至列印機的頁面。

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

準備列印選項

接下來準備列印選項。 例如,本節將說明如何設定頁面範圍選項,以允許列印特定頁面。 如需更進階的選項,請參閱 自訂列印預覽 UI

此步驟會建立新的列印選項、定義選項支援的值清單,然後將選項新增至列印預覽 UI。 頁面範圍選項具有下列設定:

選項名稱 動作
全部列印 列印檔中的所有頁面。
列印選取範圍 只列印使用者選取的內容。
列印範圍 顯示編輯控制項,使用者可以在其中輸入要列印的頁面。

首先,修改 PrintTaskRequested 事件處理常式,以新增程式碼以取得 PrintTaskOptionDetails 物件。

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

清除列印預覽 UI 中顯示的選項清單,並新增使用者想要從應用程式列印時,您要顯示的選項。

注意

 這些選項會依照附加順序顯示在列印預覽 UI 中,第一個選項會顯示在視窗頂端。

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 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 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 方法會建立 Range 文本框。 這是使用者在選取列印範圍選項時輸入想要列印的特定頁面。

處理列印選項變更

OptionChanged 事件處理常式會執行兩件事。 首先,它會根據使用者選取的頁面範圍選項,顯示並隱藏頁面範圍的文字編輯欄位。 其次,它會測試在頁面範圍文本框中輸入的文字,以確定它代表檔的有效頁面範圍。

此範例示範如何在 UWP 列印範例中處理列印選項變更事件。

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

提示

 如需如何剖析使用者在範圍文本框中輸入的頁面範圍的詳細資訊,請參閱 UWP 列印範例 中的 GetPagesInRange 方法。

預覽選定的頁面

如何格式化應用程式的內容進行列印,取決於應用程式的性質及其內容。 中用於 UWP 列印範例的列印協助程式類別,用來格式化列印的內容。

列印頁面子集時,有數種方式可在列印預覽中顯示內容。 無論您選擇在列印預覽中顯示頁面範圍的方法為何,列印的輸出都必須只包含選取的頁面。

  • 在列印預覽中顯示是否指定頁面範圍的所有頁面,讓使用者知道實際列印哪些頁面。
  • 在列印預覽中只顯示使用者頁面範圍所選取的頁面,並在使用者變更頁面範圍時更新顯示。
  • 顯示列印預覽中的所有頁面,但以灰色顯示不在使用者所選取頁面範圍內的頁面。