首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 Store Apps 列印功能完整實作:分頁、預覽與打印任務管理

     編輯:關於.NET

前言

Windows 8 Store Apps 透過 Windows.Graphics.Printing 命名空間提供完整的列印支援。核心流程:註冊列印合約 → 準備列印內容(分頁佈局) → 回應系統預覽請求 → 發送最終內容至印表機。本文詳細解析 PrintDocument、PrintManager、分頁邏輯與自訂選項的完整實作。

列印架構核心類別

類別職責關鍵事件/方法
PrintManager管理列印合約,顯示印表機選擇 UIGetForCurrentView()、ShowPrintUIAsync()、PrintTaskRequested
PrintTask代表單次列印作業Options、Completed、SourceRequested
PrintDocument提供列印內容來源Paginate、GetPreviewPage、AddPages、SetPreviewPageCount、InvalidatePreview
PrintTaskOptionDetails管理列印預覽選項(份數、方向、紙張等)DisplayedOptions、CreateItemListOption、OptionChanged

列印內容準備:XAML 佈局與分頁邏輯

首頁模板

包含頁首(僅首頁顯示)、內容區(RichTextBlock + RichTextBlockOverflow 鏈式流動)、頁尾(僅末頁顯示):

<Grid x:Name="printableArea">
    <Grid.RowDefinitions>
        <RowDefinition Height="60"/>       <!-- 頁首 -->
        <RowDefinition Height="3*"/>       <!-- 內容區 1 -->
        <RowDefinition Height="3*"/>       <!-- 內容區 2 (含圖片) -->
        <RowDefinition Height="4*"/>       <!-- 內容區 3 -->
        <RowDefinition Height="60"/>       <!-- 頁尾 -->
    </Grid.RowDefinitions>
    
    <!-- 頁首:Logo + 標題 -->
    <StackPanel x:Name="header" Visibility="Collapsed" ...>...</StackPanel>
    
    <!-- 主內容:RichTextBlock 連結 Overflow 鏈 -->
    <RichTextBlock x:Name="textContent" OverflowContentTarget="{Binding ElementName=imageLeftText}" ...>...</RichTextBlock>
    <RichTextBlockOverflow x:Name="imageLeftText" OverflowContentTarget="{Binding ElementName=textOverflow}" Grid.Row="2" Grid.Column="0"/>
    <Image Source="ms-appx:///Assets/Logo.png" Grid.Row="2" Grid.Column="1"/>
    <RichTextBlockOverflow x:Name="textOverflow" Grid.Row="3" Grid.ColumnSpan="2"/>
    
    <!-- 頁尾:版權資訊 -->
    <StackPanel x:Name="footer" Visibility="Collapsed" ...>...</StackPanel>
</Grid>

溢出頁模板

僅包含 RichTextBlockOverflow 接收上一頁流動內容,以及條件性頁尾:

<Grid x:Name="printableArea">
    <RichTextBlockOverflow x:Name="textOverflow" />
    <StackPanel x:Name="footer" Visibility="Collapsed" ...>...</StackPanel>
</Grid>

核心實作:PrintDocument 事件處理

1. 註冊與釋放列印合約


protected override void OnNavigatedTo(NavigationEventArgs e) {
    RegisterForPrinting();
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
    UnregisterForPrinting();
}

private void RegisterForPrinting() {
    _printDocument = new PrintDocument();
    _printDocument.Paginate += OnPaginate;
    _printDocument.GetPreviewPage += OnGetPreviewPage;
    _printDocument.AddPages += OnAddPages;
    
    PrintManager.GetForCurrentView().PrintTaskRequested += OnPrintTaskRequested;
    PreparePrintContent(); // 建立首頁視覺樹
}

private void UnregisterForPrinting() {
    _printDocument.Paginate -= OnPaginate;
    _printDocument.GetPreviewPage -= OnGetPreviewPage;
    _printDocument.AddPages -= OnAddPages;
    PrintManager.GetForCurrentView().PrintTaskRequested -= OnPrintTaskRequested;
    _printingRoot.Children.Clear();
}

2. 分頁邏輯:Paginate 事件

系統請求預覽頁數與佈局時觸發,需建立所有預覽頁視覺樹:


private void OnPaginate(object sender, PaginateEventArgs e) {
    _previewPages.Clear();
    _printingRoot.Children.Clear();
    
    var printOptions = (PrintTaskOptions)e.PrintTaskOptions;
    var pageDescription = printOptions.GetPageDescription(0);
    
    // 建立首頁
    var lastOverflow = AddPreviewPageAndPrintPage(null, pageDescription);
    
    // 循環建立溢出頁,直到無溢出內容
    while (lastOverflow.HasOverflowContent && lastOverflow.Visibility == Visibility.Visible) {
        lastOverflow = AddPreviewPageAndPrintPage(lastOverflow, pageDescription);
    }
    
    _printDocument.SetPreviewPageCount(_previewPages.Count, PreviewPageCountType.Intermediate);
}

private RichTextBlockOverflow AddPreviewPageAndPrintPage(RichTextBlockOverflow lastOverflow, PrintPageDescription pageDesc) {
    FrameworkElement previewPage;
    if (lastOverflow == null) {
        previewPage = _firstPage; // 首頁模板
        ((StackPanel)previewPage.FindName("footer")).Visibility = Visibility.Collapsed; // 首頁無頁尾
    } else {
        previewPage = new ContinuationPage(lastOverflow); // 溢出頁模板
    }
    
    // 設定頁面尺寸與邊距
    previewPage.Width = pageDesc.PageSize.Width;
    previewPage.Height = pageDesc.PageSize.Height;
    var printableArea = (Grid)previewPage.FindName("printableArea");
    double marginWidth = Math.Max(pageDesc.PageSize.Width - pageDesc.ImageableRect.Width, pageDesc.PageSize.Width * 0.15);
    double marginHeight = Math.Max(pageDesc.PageSize.Height - pageDesc.ImageableRect.Height, pageDesc.PageSize.Height * 0.06);
    printableArea.Width = previewPage.Width - marginWidth;
    printableArea.Height = previewPage.Height - marginHeight;
    
    // 加入預覽集合與發送列印視覺樹
    _previewPages.Add(previewPage);
    _printingRoot.Children.Add(previewPage);
    _printingRoot.InvalidateMeasure();
    _printingRoot.UpdateLayout();
    
    var textOverflow = (RichTextBlockOverflow)previewPage.FindName("textOverflow");
    if (!textOverflow.HasOverflowContent && textOverflow.Visibility == Visibility.Visible) {
        ((StackPanel)previewPage.FindName("footer")).Visibility = Visibility.Visible; // 末頁顯示頁尾
    }
    return textOverflow;
}

3. 預覽頁請求:GetPreviewPage


private void OnGetPreviewPage(object sender, GetPreviewPageEventArgs e) {
    _printDocument.SetPreviewPage(e.PageNumber, _previewPages[e.PageNumber - 1]);
}

4. 發送列印內容:AddPages


private void OnAddPages(object sender, AddPagesEventArgs e) {
    foreach (var page in _previewPages) {
        _printDocument.AddPage(page);
    }
    _printDocument.AddPagesComplete();
}

PrintTask 請求處理:選項與自訂設定


private async void OnPrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e) {
    var printTask = e.Request.CreatePrintTask("列印標題", async sourceRequested => {
        // 設定預覽選項
        var optionDetails = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);
        var displayedOptions = optionDetails.DisplayedOptions;
        displayedOptions.Clear();
        displayedOptions.Add(StandardPrintTaskOptions.Copies);
        displayedOptions.Add(StandardPrintTaskOptions.Orientation);
        displayedOptions.Add(StandardPrintTaskOptions.MediaSize);
        displayedOptions.Add(StandardPrintTaskOptions.Collation);
        displayedOptions.Add(StandardPrintTaskOptions.Duplex);
        printTask.Options.MediaSize = PrintMediaSize.NorthAmericaLegal;
        
        // 自訂選項
        var customOption = optionDetails.CreateItemListOption("CustomOption", "自訂選項");
        customOption.AddItem("item1", "選項 1");
        customOption.AddItem("item2", "選項 2");
        customOption.AddItem("item3", "選項 3");
        displayedOptions.Add("CustomOption");
        optionDetails.OptionChanged += OnOptionChanged;
        
        // 提供列印來源
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
            sourceRequested.SetSource(_printDocument.DocumentSource);
        });
        
        printTask.Completed += (s, args) => {
            if (args.Completion == PrintTaskCompletion.Failed) {
                // 處理失敗
            }
        };
    });
}

private async void OnOptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args) {
    var optionId = args.OptionId as string;
    if (string.IsNullOrEmpty(optionId)) return;
    
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => {
        var itemId = sender.Options[optionId].Value as string;
        // 根據選項調整列印內容...
        _printDocument.InvalidatePreview(); // 重新觸發 Paginate
    });
}

關鍵技術細節總結

  1. 視覺樹分離: _previewPages 供預覽,_printingRoot 供實際列印,兩者同步佈局但獨立管理。
  2. 邊距計算: 取 PageSize - ImageableRect(印表機硬體邊距)與 PageSize * 比例(應用程式邊距)的較大值,確保內容不被切邊。
  3. RichTextBlockOverflow 鏈: 透過 OverflowContentTarget 串聯多個 Overflow 元素,實現自動分頁流動。
  4. 頁首頁尾條件顯示: 首頁顯示 Header、末頁顯示 Footer,中間頁兩者皆隱藏。
  5. InvalidatePreview(): 使用者變更選項(如紙張大小)時呼叫,觸發完整重新分頁。
  6. 非同步處理: 所有事件處理器中涉及 UI 操作必須 await Dispatcher.RunAsync。

常見問題與解決

問題原因解決
預覽頁數為 0Paginate 未呼叫 SetPreviewPageCount確保分頁邏輯正確添加頁面並呼叫 SetPreviewPageCount
內容被切邊邊距計算錯誤使用 Math.Max(硬體邊距, 應用邊距) 取較大值
頁首頁尾顯示錯誤Visibility 邏輯錯誤首頁 Header Visible、Footer Collapsed;末頁相反;中間頁皆 Collapsed
選項變更不生效未呼叫 InvalidatePreviewOptionChanged 中必須呼叫 _printDocument.InvalidatePreview()

掌握列印合約的完整流程與分頁佈局技巧,能為 Windows 8 Store Apps 提供專業級的文件列印體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved