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里的控件,或进行别的处理。在这就不论述,有兴趣的可以自己去试试。

转载于:https://www.cnblogs.com/weivyuan/archive/2013/01/08/2851411.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

语法分析树(先看例子再看定义)

语法分析树&#xff08;先看例子再看定义&#xff09; 先讲例子 书上讲问题&#xff0c;先讲定义&#xff0c;一顿学术操作&#xff0c;让人云里雾里&#xff0c;然后出例子。其实这样往往让人觉得看书的过程就是放弃的过程。 关于语法分析树&#xff0c;我先从上篇文章的例…

C语言正函数nosign,C语言标准库函数查询手册.docx

球锣宜软瘠旱塾3abort功能异常终止程序头文件StdIib.h语法VOid abort(void);abs功能整数的绝对值头文件StdIib.h语法int abs(int: j);返回值整数j的绝对值。如果不能表示J的绝对值&#xff0c;那么函数的行为是未定义的。absread功能读磁盘绝对扇区函数头文件dos ? h语法int a…

spring 实例化bean的几种方法

1.普通的通过构造函数初始化&#xff0c;没有指定构造函数参数的就是用默认的无参的构造方法 <bean id"exampleBean" class"examples.ExampleBean"/><bean name"anotherExample" class"examples.ExampleBeanTwo"/> 构造函…

middle函数C语言,C语言函数调用栈(三)

6 调用栈实例分析本节通过代码实例分析函数调用过程中栈帧的布局、形成和消亡。6.1 栈帧的布局示例代码如下&#xff1a;//StackReg.c#include //获取函数运行时寄存器%ebp和%esp的值#define FETCH_SREG(_ebp, _esp) do{\asm volatile( \"movl %%ebp, %0 \n" \"…

c语言多个形参,C中子函数最多有几个形参

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼C89 31个&#xff0c;C99 127个。ANSI C892.2.4.1 Translation limitsThe implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following lim…

什么叫ERP软件

1.ERP是什么&#xff1f; erp是个管理系统&#xff01;是一个成套的统称&#xff01;构成&#xff1a;进销存BOM生产MRP质检(IQC/FQC/PQC)车间日报/简单排产应收应付固定资产实际成本算法自动财务。就是通过软件管理&#xff0c;实现最小的库存和最大的利益&#xff01; 2.什么…

android 杀 服务器,android busybox httpd搭建简单web服务器

安卓手机利用busybox中的httpd命令可以创建一个简单的web服务器。我们可以在终端模拟器中输入一下命令查看httpd的使用帮助。busybox httpd --help1|u0_a84cancro:/ $ busybox httpd --helpBusyBox v1.20.2-MIUI (2012-08-20 20:59:32 CST) multi-call binary.Usage: httpd [-i…

sublimeclang里面使用相对路径

在sublime的project里面&#xff0c;可以添加sublimeclang的options,比如这样 {"folders":[{"path": "/C/EverBox/gitCode/clangxx"}],"settings": {"sublimeclang_options": ["-Wall","-IC:\\EverBox\\gitC…

短信恢复 android,苹果手机短信恢复:安卓+苹果手机短信恢复教程,必须收藏!...

原标题&#xff1a;苹果手机短信恢复&#xff1a;安卓苹果手机短信恢复教程&#xff0c;必须收藏&#xff01;手机短信怎么恢复&#xff1f;日常生活中&#xff0c;大家或多或少会不小心把删除一些不必要的短信&#xff0c;比如注册某软件的验证码的短信&#xff0c;面试通知地…

Shell应用:批量将文件编码由gbk转utf-8

Shell应用&#xff1a;批量将文件编码由gbk转utf-8,实例代码&#xff1a; [plain]#!/bin/bash // batch_change_GB2312_to_UTF-8 cd directory find ./ -type f -name "*.java" | while read line;do echo $line iconv -f GB2312 -t UTF-8 $line > ${lin…

c语言立体图像编程,C语言-编程实例-三视图的画法

#includeint dx[12]{0,60,60,0,0,60,60,0,60,0,60,0}; /*确定组合体X坐标*/int dy[12]{0,0,120,120,0,0,40,40,80,80,120,120};/*确定组合体Y坐标*/int dz[12]{0,0,0,0,80,80,80,80,40,40,40,40};/*确定组合体Z坐标*/int ld[24]{0,1,2,3,0,4,5,6,7,4,10,11,9,8,10,2,3,11,8,6,9…

【js拾遗】名称空间

function NameSpace(ns) {if (!ns) {return null;}var arr ns.split(.);//第一次调用的时候window.utry的值为{};//第二次调用的时候window.utry的值为window.utry;window[arr[0]] window[arr[0]] || {};//obj是window.utry的引用var obj window[arr[0]];//result也是window…

Linux编程简介——VI

VI是Linux/Unix下标配的一个纯字符界面的文本编辑器。由于不支持鼠标功能&#xff0c;也没有图形界面&#xff0c;相关的操作都要通过键盘指令来完成&#xff0c;需要记忆大量命令。因此很多人不大喜欢它&#xff0c;但同时由于键盘的方式往往比鼠标来得快&#xff0c;一旦熟练…

xamarin android 标签,安卓端Tabbedpage调整在底部位置和标签及取消Android API28 以下的点击特效—-xamarin.forms学习笔记(一)...

使用tabbedpage时将安卓端导航放在底部,官网也有说明方法.总结:xmlns"http://xamarin.com/schemas/2014/forms"xmlns:x"http://schemas.microsoft.com/winfx/2009/xaml"xmlns:local"clr-namespace:aya"x:Class"aya.MainPage"xmlns:and…

算法之递推及其应用(递推关系的建立及在信息学竞赛中的应用 安徽 高寒蕊)...

定义&#xff1a;给定一个数的序列H0,H1,…,Hn,…若存在整数n0&#xff0c;使当nn0时,可以用等号(或大于号、小于号)将Hn与其前面的某些项Hn(0i<n)联系起来&#xff0c;这样的式子就叫做递推关系。 递推关系中存在着三大基本问题&#xff1a;如何建立递推关系&#xff0c;已…

android广播第三方库,Android Support 库:LocalBroadcastManager

在介绍完 Android Support 库发展历程(http://blog.chengyunfeng.com/?p1047)后&#xff0c; 再分别介绍下 Android Support 库中有用但是被忽略的一些功能。了解这些功能&#xff0c;在需要的时候可以避免在引入其他类似的第三方库或者避免自己重复制造轮子&#xff0c;提高开…

2012年总结

2012年是一个特别的年头。 这一年&#xff0c;我的准研究生生活和研究生生活加起来已经一年了。最近在进行期末考试&#xff0c;自己也对自己这一年的经历进行一下总结&#xff0c;留在研一结束的时候对照一下。 自己在保研的时候不是很顺利&#xff08;主要是能力不足&#xf…