zoukankan      html  css  js  c++  java
  • WPF中打印问题的探讨[转]

    转自:http://blog.sina.com.cn/s/blog_624dc0120100ld6m.html

        最近在做一个WPF方面的项目,在打印功能实现上费了很大劲。因为我原来是在做Winform方面的项目,接受WPF时感觉还很相似,可仔细往里做下去却发现两者外看相似,实则差异很大,打印就是其中很大的差距。Winform有对应的对话框围绕打印核心类PrintDocument进行操作,而WPF里则是在PrintDialog里进行扩展。WPF简单的打印很简单,几行代码就够,可要实现较大功能时就需要很多的扩展,如分页打印及对打印允许客户有较大的设置自由度时,就很麻烦了。我以我这次的功能实现与大家进行探讨。

        常见的打印方式有以下三中:

        第一种:对单一控件内容的打印。

        private void billtitle_btn_PrintClick(object sender, RoutedEventArgs e)
            {

                PrintDialog printDialog = new PrintDialog();
                if (printDialog.ShowDialog() == true)
                {
                    printDialog.PrintVisual(Mainwindow, "123");
                }
            }

         其中Mainwindow是控件名,也可以是ListView等控件,只要把名称传入即可。很简单,不过不实用,因为这种方法没用自由度,是按系统默认进行打印且只能打印在一页上,数据多了就不行。

         第二种:根据PrintDialog进行功能扩展,就可以对打印功能在一定程度上扩展。  

    /// <summary>
    /// 打印类
    /// </summary>
    public class PrintService
    {
        public PrintService()
       {
          //创建一个PrintDialog的实例
          PrintDialog dlg = new PrintDialog();

          //创建一个PrintDocument的实例
          PrintDocument docToPrint = new PrintDocument();

          //将事件处理函数添加到PrintDocument的PrintPage事件中
          docToPrint.PrintPage += new System.Drawing.Printing.PrintPageEventHandler                 (docToPrint_PrintPage);

          //把PrintDialog的Document属性设为上面配置好的PrintDocument的实例
          dlg.Document = docToPrint;

          //根据用户的选择,开始打印
          if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
         {
            docToPrint.Print();//开始打印
          }
       }

       //设置打印机开始打印的事件处理函数
       private void docToPrint_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
      {
            e.Graphics.DrawString("Hello, world!", new System.Drawing.Font("Arial", 16,  System.Drawing.FontStyle.Regular), System.Drawing.Brushes.Black, 100, 100);
        }
    }

        在这就可以看出Winform与WPF打印的不同了,在WPF在PrintDocument是一个打印方法。WPF有两种打印途径,一种是第一种方法中的PrintVisual,另一种就是PrintDocument。

        第三种:具体步骤如下:

      1.PrintDialog

      This sample illustrates how to create an instance of a simple PrintDialog and then display it. The sample uses both Extensible Application Markup Language (XAML) and procedural code.

       这个示例演示了如何进行一个最简单的打印工作,为此需要引入两个dll:ReachFramework.dll和System.Printing。

      InvokePrint方法只是显示了一个PrintDialog打印框,并未进行打印工作:

      PrintDialog pDialog = new PrintDialog();
       pDialog.PageRangeSelection = PageRangeSelection.AllPages;
       pDialog.UserPageRangeEnabled = true;
       pDialog.ShowDialog();

      有 PrintableAreaHeight和PrintableAreaWidth两个属性,分别用来表示可打印区域的高和宽。

      而对 PrintDialog的设置,可以保存在PrintTicket中,下次再打开PrintDialog,就不必重复进行设置了。

      PrintDialog pDialog = new PrintDialog();
       PrintTicket pt = pDialog.PrintTicket;  

      同样,选择使用哪一台打印机的设置,存放在PrintQueue中,下次再打开PrintDialog,也不用再次设置了。

      PrintDialog pDialog = new PrintDialog();
        PrintQueue pq = pDialog.PrintQueue;   

      如果要把特定的内容打印输出,则需要调用PrintDialog的PrintVisual方法:

      if ((bool)pDialog.ShowDialog().GetValueOrDefault())
       {
            DrawingVisual vis = new DrawingVisual();
            DrawingContext dc = vis.RenderOpen();
            dc.DrawLine(new Pen(), new Point(0, 0), new Point(0, 1));
            dc.Close();
            pDialog.PrintVisual(vis, "Hello, world!");
         }

      我们能打印的,都是Visual类型的对象,其中UIElement派生于 Visual,从而我们可以打印所有Panel、控件和其它元素,最一般的方法是使用派生于Visual的DrawingVisual类,利用它的 RenderOpen方法生成DrawingContext对象,为其绘制图形,最后使用PrintDialog的PrintVisual方法,输出图形 和文字。

      注意到,pDialog.ShowDialog()返回的是可空类型?bool,为此需要使用 GetValueOrDefault将其转为bool值,对于null值也会转为false。

       2.EnumerateSubsetOfPrintQueues

       EnumerateSubsetOfPrintQueues shows how to use the EnumeratedPrintQueueTypes enumeration to get a subset of available print queues.

      这个程序演示了如何得到本地和共享的所有打印机列表。为此,需要 使用到EnumeratedPrintQueueTypes枚举中的Local和Shared两个值,组合成一个数组,

       EnumeratedPrintQueueTypes[] enumerationFlags =

                          {EnumeratedPrintQueueTypes.Local,EnumeratedPrintQueueTypes.Shared};

      作为参数传递到查询方法GetPrintQueues中:

      LocalPrintServer printServer = new LocalPrintServer();
        PrintQueueCollection printQueuesOnLocalServer = printServer.GetPrintQueues(enumerationFlags);

      接着就可 以对PrintQueueCollection进行遍历了,获取每一个的PrintQueue名称和所在位置:

      foreach (PrintQueue printer in printQueuesOnLocalServer)
       {
             Console.WriteLine(""tThe shared printer " + printer.Name + " is located at " + printer.Location + ""n");
       }

       下面就看我的方法了,先创建一个打印用的类:DataPaginator,它继承了Document虚基类,并进行扩展,从而为我们的功能实现做铺垫。

      public class DataPaginator : DocumentPaginator
        {
            #region  属性及字段
            private DataTable dataTable;
            private Typeface typeFace;
            private double fontSize;
            private double margin;
            private int rowsPerPage;
            private int pageCount;
            private Size pageSize;

            public override Size PageSize
            {
                get
                {
                    return pageSize;
                }
                set
                {
                    pageSize = value;
                    PaginateData();
                }
            }
            public override bool IsPageCountValid
            {
                get { return true; }
            }
            public override int PageCount
            {
                get { return pageCount; }
            }
            public override IDocumentPaginatorSource Source
            {
                get { return null; }
            }
            #endregion

            #region  构造函数相关方法
            //构造函数
            public DataPaginator(DataTable dt, Typeface typeface, int fontsize, double margin, Size pagesize)
            {
                this.dataTable = dt;
                this.typeFace = typeface;
                this.fontSize = fontsize;
                this.margin = margin;
                this.pageSize = pagesize;
                PaginateData();
            }
            /// <summary>
            /// 计算页数pageCount
            /// </summary>
            private void PaginateData()
            {
                //字符大小度量标准
                FormattedText ft = GetFormattedText("A");  //取"A"的大小计算行高等;
                //计算行数
                rowsPerPage = (int)((pageSize.Height - margin * 2) / ft.Height);
                //预留标题行
                rowsPerPage = rowsPerPage - 1;
                pageCount = (int)Math.Ceiling((double)dataTable.Rows.Count / rowsPerPage);
            }
            /// <summary>
            /// 格式化字符
            /// </summary>
            private FormattedText GetFormattedText(string text)
            {
                return GetFormattedText(text, typeFace);
            }
            /// <summary>
            /// 按指定样式格式化字符
            /// </summary>
            private FormattedText GetFormattedText(string text, Typeface typeFace)
            {
                return new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeFace, fontSize, Brushes.Black);
            }
            /// <summary>
            /// 获取对应页面数据并进行相应的打印设置
            /// </summary>
            public override DocumentPage GetPage(int pageNumber)
            {
                //设置列宽
                FormattedText ft = GetFormattedText("A");
                List<double> columns = new List<double>();
                int rowCount = dataTable.Rows.Count;
                int colCount = dataTable.Columns.Count;
                double columnWith = margin;
                columns.Add(columnWith);
                for (int i = 1; i < colCount; i++)
                {
                    columnWith += ft.Width * 15;
                    columns.Add(columnWith);
                }
                //获取页面对应行数
                int minRow = pageNumber * rowsPerPage;
                int maxRow = minRow + rowsPerPage;
                //绘制打印内容
                DrawingVisual visual = new DrawingVisual();
                Point point = new Point(margin, margin);
                using (DrawingContext dc = visual.RenderOpen())
                {
                    Typeface columnHeaderTypeface = new Typeface(typeFace.FontFamily, FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);
                    //获取表头
                    for (int i = 0; i < colCount; i++)
                    {
                        point.X = columns[i];
                        ft = GetFormattedText(dataTable.Columns[i].Caption, columnHeaderTypeface);
                        dc.DrawText(ft, point);
                    }
                    dc.DrawLine(new Pen(Brushes.Black,3), new Point(margin, margin + ft.Height), new Point(pageSize.Width - margin, margin + ft.Height));
                    point.Y += ft.Height;
                    //获取表数据
                    for (int i = minRow; i < maxRow; i++)
                    {
                        if (i > (rowCount - 1)) break;
                        for (int j = 0; j < colCount; j++)
                        {
                            point.X = columns[j];
                            string colName = dataTable.Columns[j].ColumnName;
                            ft = GetFormattedText(dataTable.Rows[i][colName].ToString());
                            dc.DrawText(ft, point);
                        }
                        point.Y += ft.Height;
                    }
                }
                return new DocumentPage(visual);
            }
            #endregion
        }

        又构造函数可知,我们需传入一个DataTable,这样可打印的内容、样式等就宽泛多了。DataTable可直接获取,也可自己根据需要构建,我在项目时是根据显示的数据构建一个DataTable。代码如下:

        /// <summary>
            /// 获取要打印的数据
            /// </summary>
            private DataTable GetDataTable()
            {
                DataTable table = new DataTable("Data Table");           
                // Declare variables for DataColumn and DataRow objects.
                DataColumn column;
                DataRow row;

                // Create new DataColumn, set DataType,
                // ColumnName and add to DataTable.   
                column = new DataColumn();
                column.DataType =Type.GetType("System.Int32");
                column.ColumnName = "id";
                column.Caption = "编号";
                column.ReadOnly = true;
                column.Unique = true;
                // Add the Column to the DataColumnCollection.
                table.Columns.Add(column);

                // Create second column.
                column = new DataColumn();
                column.DataType = Type.GetType("System.String");
                column.ColumnName = "Name";
                column.AutoIncrement = false;
                column.Caption = "姓名";
                column.ReadOnly = false;
                column.Unique = false;
                // Add the column to the table.
                table.Columns.Add(column);

                //Create third column
                column = new DataColumn();
                column.DataType = Type.GetType("System.String");
                column.ColumnName = "Age";
                column.AutoIncrement = false;
                column.Caption = "年龄";
                column.ReadOnly = false;
                column.Unique = false;
                // Add the column to the table.
                table.Columns.Add(column);


                //Create forth column
                column = new DataColumn();
                column.DataType = Type.GetType("System.String");
                column.ColumnName = "Pay";
                column.AutoIncrement = false;
                column.Caption = "工资";
                column.ReadOnly = false;
                column.Unique = false;
                // Add the column to the table.
                table.Columns.Add(column);
                // Make the ID column the primary key column.
                DataColumn[] PrimaryKeyColumns = new DataColumn[1];
                PrimaryKeyColumns[0] = table.Columns["id"];
                table.PrimaryKey = PrimaryKeyColumns;

                // Instantiate the DataSet variable.
                //dataSet = new DataSet();
                // Add the new DataTable to the DataSet.
                //dataSet.Tables.Add(table);

                // Create three new DataRow objects and add
                // them to the DataTable
                for (int i = 0; i <= 60; i++)
                {
                    row = table.NewRow();              
                    row["id"] = i+1;
                    row["Name"] = "zhangsan " + (i+1).ToString();
                    row["Age"] = 20 + i;
                    row["Pay"] = 50 * (i + 1);
                    table.Rows.Add(row);
                }
                return table;
            }

          由此就可以开始打印了:

        private void BT_MultiPrint_Click(object sender, RoutedEventArgs e)
            {
                PrintDialog printDialog = new PrintDialog();
                if (printDialog.ShowDialog() == true)
                {
                    DataTable dt = GetDataTable();
                    try
                    {
                        DataPaginator dp = new DataPaginator(dt, new Typeface("SimSun"), 16, 96 * 0.75, new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight));
                        printDialog.PrintDocument(dp, "Test Page");             
                    }
                    catch
                    {
                        MessageBox.Show("无法打印!");
                    }               
                }                                                                                                                                        
            }

         试用后效果还不错。Winform里的打印功能比这强大的多,可以将其使用的控件扩展为WPF里的控件,或进行别的处理。在这就不论述,有兴趣的可以自己去试试。

  • 相关阅读:
    课堂作业04 2017.10.27
    课程作业 03 动手动脑 2017.10.20
    课程作业 03 2017.10.20
    HDU 3974 Assign the task
    POJ 2155 Matrix
    POJ 2481 Cows
    HDU 3038 How Many Answers Are Wrong
    CS Academy Array Removal
    POJ_1330 Nearest Common Ancestors LCA
    CF Round 427 D. Palindromic characteristics
  • 原文地址:https://www.cnblogs.com/weivyuan/p/2851411.html
Copyright © 2011-2022 走看看