WPF中打印问题的探讨

最近在做一个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 &lt; 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 &lt; 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。代码如下: /// &lt;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 &lt;= 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里的控件,或进行别的处理。在这就不论述,有兴趣的可以自己去试试。

ref:http://blog.sina.com.cn/s/blog_624dc0120100ld6m.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值