C# 列印功能

      在〈C# 列印功能〉中尚無留言

DocumentView, XpsDocuemnt, FlowDocument, Table, Paragraph, Section

基本概論

在 C# 中要啟用列印功能, 工程實在浩大, 方法也很多. 在此僅列出本人實作的方式.

DocumentViewer <- XpsDocument <- FlowDocument <- Blocks <- Paragraph/Table/Section

DocumentViewer

DocumentViewer是最外圍預覽列印的元件, 同時也具有列印工具列printer_1

上圖灰色背景的部份, 即為DocumentViewer的範圍, 具有最上方的工具列, 中間的資料顯示區, 及最下方的搜尋詢功能

此元件可直接使用 DocumentViewer docViewer = new DocumentViewer() 產生物件, 然後將此元件崁入到 Grid之中. 崁入到Grid的方法為 grid.Children.Add(docViewer)

XpsDocument

有了DocumentViewer後, 接下來就是把要顯示的資料崁入其中.  負責處理資料顯示的元件為XpsDocument, , 是由微軟自行開發處理列印的專用格式.

在啟用XPS時, 需於專案按右鍵/加入/參考, 將ReachFramework及System.Printing打勾

XpsDocument其實不是資料的存放處, 真正存放資料的元件是FlowDocument. 也就是說, 要先把資料都安排在FlowDocument之中, 然後再利用XpsDocumentWriter 轉換成XpsDocument.

製作XpsDocument 會花費相當長的時間, 所以需使用執行緒進行此工作, 如下程式碼為製作XpsDocument的方法

Dispatcher.BeginInvoke(new LoadXpsMethod(LoadXps), DispatcherPriority.ApplicationIdle);

上面程式碼中, 產生LoadXpsMethod物件, 並呼叫了LoadXps方法. 所以需於class之中宣告如下

public partial class WindowPatrolLog : Window
{
    private delegate void LoadXpsMethod();
}

至於LoadXps的方法如下

public void LoadXps()
{
    MemoryStream ms = new MemoryStream();
    Package package = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
    Uri DocumentUri = new Uri("pack://InMemoryDocument.xps");
    PackageStore.RemovePackage(DocumentUri);
    PackageStore.AddPackage(DocumentUri, package);
    XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Fast, DocumentUri.AbsoluteUri);
    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
    writer.Write(((IDocumentPaginatorSource)m_doc).DocumentPaginator);
    docViewer.Document = xpsDocument.GetFixedDocumentSequence();
    xpsDocument.Close();
}

上方程式碼只要照抄就可. 其中藍色的 m_doc, 即為 FlowDocument, 也就是要列印的資料存放處. 利用XpsDocumentWriter的Write方法, 將m_doc寫入XpsDocument之中, 然後再把DocumentViewer的Document屬性指向XpsDocuemnt.

FlowDocument

FlowDocument為資料的存放元件, 可以存放Paragraph, Table, 換頁符等元件, 每個元件都使用Blocks的屬性加入.

FlowDocument m_doc=new FlowDocument() 即可產生物件. m_doc.FontFamily = new FontFamily(“Century Gothic”) 可控制FlowDocument的字型.

若將表格置於FlowDocument中, 僅能佔滿一半的寬度, 此時需設定如下
m_doc.ColumnWidth = 999999;

Paragraph

Paragraph為段落的意思, 使用 new Paragraph()即可產生段落物件. FlowDocument.Blocks.Add()將此物件置於 FlowDocument之中

Paragraph 常用的屬性方法如下

FontSize = 10; //設定字型大小
TextAlignment = TextAlignment.Center; //設定段落對齊方式, 有靠左, 置中, 靠右三種
Inlines.Add("顯示的文字"); //設定要顯示的文字

Table

表格是列印時常用的元件之一. 製作表格的流程如下

設定表格屬性 -> 定義欄位 -> TableRowGroup -> TableRow -> TableCell

設定表格屬性

表格屬性包含了框線顏色, 粗細, 字型大小. 需特別注意的是, 每個儲存格的間距, 也是在Table的屬性中設定

Table table=new Table();
table.BorderBrush = Brushes.Black; //框線顏色
table.BorderThickness = new Thickness(0.5,0.5,0.5,0); 框線粗細
table.CellSpacing = 0; //每個儲存格的間距
table.FontSize = 10; //字型大小
table.Margin = new Thickness(0, 1, 0, 0); //表格的邊界

定義欄位

表格有多少個欄位, 需產生TableColumn 物件, 並設定每個欄位的寬度後, 再使用表格的Columns.Add() 將欄位加入

TableColumn[] cols = new TableColumn[3];
cols[0] = new TableColumn(); cols[0].Width = new GridLength(200);
cols[1] = new TableColumn(); cols[1].Width = new GridLength(20);
cols[2] = new TableColumn(); cols[2].Width = new GridLength(550);
for (int i = 0; i < cols.Length; i++)
{
    this.Columns.Add(cols[i]);
}

TableRowGroup

每一列的資料是存放在TableRow之中, 但TableRow並不能直接放置在Table裏, 而是要先置於TableRowGroup之中, 再把TableRowGroup放置在Table之中.

請注意, 一個表格可以加入多個TableRowGroup

TableRowGroup g = new TableRowGroup();
g.Rows.Add(row);

TableRow

TableRow 是定義每一列的物件, 然後將每一個TableCell物件加入到TableRow之中

TableRow row = new TableRow();
row.Cells.Add(cells[i]);

TableCell

TableCell是每個儲存格物件, 可以在每個儲存格中設定框線大小, 對齊方式, 及儲存格內的邊界. TableCell只能設定水平的對齊方式, 無法設定垂直對齊, 只能先計算出儲存格的高度, 再利用Padding屬性來達成垂直置中的需求.

TableRowGroup g = new TableRowGroup();
TableRow row = new TableRow();
cells[0] = new TableCell(new Paragraph(new Run("輕度")));
cells[1] = new TableCell(new Paragraph(new Run("中度")));
cells[2] = new TableCell(new Paragraph(new Run("重度")));
for (int i = 0; i < 3; i++)
{
    cells[i].BorderBrush = Brushes.Black;
    cells[i].BorderThickness = new Thickness(0.5);
    cells[i].TextAlignment = TextAlignment.Center;
    cells[i].Padding = new Thickness(0, 5, 0, 0);
    row.Cells.Add(cells[i]);
}
g.Rows.Add(row);
this.RowGroups.Add(g);

Section

Session最常用到的, 就是換頁符. 在FlowDocument中, 若要達到換頁的效果, 除了資料超出了FlowDocument的高度之外, 也可以於FlowDocument加入Section.

需注意的是, 每一個換頁符, 都必需是一個新的物件

Section section = new Section();//換頁符, 一定要是新的物件
section.BreakPageBefore = true;
m_doc.Blocks.Add(section);

補充 – 由xaml 取得物件

FlowDocument m_doc = (FlowDocument)Application.LoadComponent(new Uri("FlowDocumentPatrol.xaml", UriKind.RelativeOrAbsolute));

列印指定頁數, 橫向列印

列印時, 預設無法指定要列印的頁數, 此時必需撰寫自訂的DocumentViewer, 也可以由下指定列印的方向

class MahalDocumentViewer : DocumentViewer
    {
        protected override void OnPrintCommand()
        {
            PrintDialog printDialog = new PrintDialog();
            //printDialog.SelectedPagesEnabled = true;
            printDialog.UserPageRangeEnabled = true;
            printDialog.CurrentPageEnabled = true;
            //printDialog.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
            if (printDialog.ShowDialog() == true)
            {
                DocumentPaginator paginator;
                if (printDialog.PageRangeSelection == PageRangeSelection.UserPages)
                {
                    paginator = new PageRangeDocumentPaginator(this.Document.DocumentPaginator, printDialog.PageRange);
                }
                else if (printDialog.PageRangeSelection == PageRangeSelection.CurrentPage)
                {
                    int currentPage = this.MasterPageNumber;
                    PageRange pageRange = new PageRange();
                    pageRange.PageFrom = currentPage;
                    pageRange.PageTo = currentPage;
                    paginator = new PageRangeDocumentPaginator(this.Document.DocumentPaginator, pageRange);
                }
                else
                {
                    paginator = this.Document.DocumentPaginator;
                }
                printDialog.PrintDocument(paginator, "立向營造");
            }
        }
    }

print_page

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *