标签:
我们在用 C# 语言编写 WinForm 程序时,如果在程序中需要打印一些东东的话,经常需要先使用页面设置对话框进行一些设置。而 Microsoft .NET Framework Base Class Library 已经为我们考虑得很周到了,我们只需要使用 System.Windows.Forms 命名空间中的 PageSetupDialog 类就行了。但是这个类有个小小的 BUG,下面就是相应的测试程序 PageSetupTester.cs:
001: using System; 002: using System.Drawing; 003: using System.Windows.Forms; 004: using System.Globalization; 005: using System.Drawing.Printing; 006: 007: namespace Skyiv.Tester 008: { 009: sealed class PageSetupTester : Form 010: { 011: PageSetupDialog setupB; 012: 013: PageSetupTester() 014: { 015: Text = "页面设置测试"; 016: Width = 380; 017: 018: var lbl = new Label(); 019: lbl.Parent = this; 020: lbl.Text = string.Format( 021: " OS: {1}{0}CLR: {2} ( {3} ){0}{4}{5}{0}{0}{6}{0}{7}{0}{8}{0}{9}{0}{10}", 022: Environment.NewLine, 023: Environment.OSVersion, 024: Environment.Version, 025: RuntimeFramework.CurrentFramework, 026: "控制面板的区域选项的度量衡系统为", 027: (RegionInfo.CurrentRegion.IsMetric ? "公制。" : "英制。"), 028: "当度量衡系统为公制,且操作系统为Windows时,", 029: "显示在页面设置对话框中的当前页边距是以公制为单位的数值,", 030: "按下“确定”时,PageSetupDialog类内部却把屏幕上显示的", 031: "页边距数值按英制为单位对页面进行设定,", 032: "造成页边距不断减少的BUG。"); 033: lbl.Top = 10; 034: lbl.AutoSize = true; 035: 036: var docA = new PrintDocument(); 037: docA.PrintPage += PrintPage; 038: var setupA = new PageSetupDialog(); 039: setupA.Document = docA; 040: 041: var btnPageSetupA = new Button(); 042: btnPageSetupA.Parent = this; 043: btnPageSetupA.Text = "页面设置A(可能有BUG)"; 044: btnPageSetupA.Top = 145; 045: btnPageSetupA.Width = 150; 046: btnPageSetupA.Click += (sender, e) => setupA.ShowDialog(); 047: 048: var btnPreviewA = new Button(); 049: btnPreviewA.Parent = this; 050: btnPreviewA.Text = "打印预览A"; 051: btnPreviewA.Top = btnPageSetupA.Top; 052: btnPreviewA.Left = btnPageSetupA.Right + 5; 053: btnPreviewA.Width = 100; 054: btnPreviewA.Click += (sender, e) => PreviewPrint(docA); 055: 056: var docB = new PrintDocument(); 057: docB.PrintPage += PrintPage; 058: setupB = new PageSetupDialog(); 059: setupB.Document = docB; 060: 061: var btnSetupB = new Button(); 062: btnSetupB.Parent = this; 063: btnSetupB.Text = "页面设置B(修正)"; 064: btnSetupB.Top = btnPageSetupA.Top + 30; 065: btnSetupB.Width = 150; 066: btnSetupB.Click += PageSetupBClick; 067: 068: var btnPreviewB = new Button(); 069: btnPreviewB.Parent = this; 070: btnPreviewB.Text = "打印预览B"; 071: btnPreviewB.Top = btnSetupB.Top; 072: btnPreviewB.Left = btnSetupB.Right + 5; 073: btnPreviewB.Width = 100; 074: btnPreviewB.Click += (sender, e) => PreviewPrint(docB); 075: } 076: 077: void PrintPage(object o, PrintPageEventArgs e) 078: { 079: Graphics g = e.Graphics; 080: Rectangle r = e.MarginBounds; 081: g.DrawRectangle(new Pen(Color.Red), r); 082: g.DrawString(r.ToString(), new Font("宋体", 40), new SolidBrush(Color.Blue), r); 083: } 084: 085: void PreviewPrint(PrintDocument doc) 086: { 087: var ppc = new PreviewPrintController(); 088: doc.PrintController = ppc; 089: doc.Print(); 090: new PreviewPrintDialog(ppc.GetPreviewPageInfo()).ShowDialog(); 091: } 092: 093: void PageSetupBClick(object o, EventArgs e) 094: { 095: // 当前线程所使用的区域选项的度量衡系统为公制,且操作系统为Windows时, 096: // 显示在页面设置对话框中的当前页边距是以公制为单位的数值, 097: // 按下“确定”时,PageSetupDialog类内部却把屏幕上显示的 098: // 页边距数值按英制为单位对页面进行设定, 099: // 造成页边距不断减少的BUG。 100: // 以下代码就是为了纠正这个BUG的。 101: if (setupB.ShowDialog() == DialogResult.OK && RegionInfo.CurrentRegion.IsMetric 102: && Environment.OSVersion.Platform != PlatformID.Unix) 103: { 104: setupB.PageSettings.Margins = PrinterUnitConvert.Convert 105: (setupB.PageSettings.Margins, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); 106: } 107: } 108: 109: [STAThread] 110: static void Main() 111: { 112: Application.EnableVisualStyles(); 113: Application.Run(new PageSetupTester()); 114: } 115: } 116: }
本来嘛,在 C# 程序要得到一个页面设置对话框是非常简单的事,只要象上述程序中第 46 行那样给“页面设置”按钮的 Click 事件注册一个调用 System.Windows.Forms.PageSetupDialog 类的 ShowDialog 方法的Lambda 表达式就行了。
编译响应文件 mak.rsp 的内容如下:
用于打印预览的 PreviewPrintDialog.cs 将在本文最后给出。而 RuntimeFramework.cs 请参见我在2009年12月13日写的“.NET Framework CLR 版本检测”一文。
让我们在 Windows Vista 操作系统的 .NET Framework 4 环境下编译和运行:
然后点击“打印预览B”按钮,得到的结果如下图(图1)所示:
图1 |
再点击“页面设置B(修正)”按钮,得到的结果如下图(图2)所示:
图2 |
点击上图中的“确定”按钮后,再次点击主窗体中的“打印预览B”按钮,得到的结果依然如图1所示。这是页面设置对话框应该有的正确行为。实际上这个行为是经过 PageSetupTester.cs 中第 104 行到第 105 行的语句修正后的结果。
现在让我们点击主窗体中的“打印预览A”按钮,得到的结果也是如图1如示,这也是正常的结果。然后再点击主窗体中的“页面设置A(可能有BUG)”按钮,得到的得到如图2所示,这也是预期的结果。然后再点“确定”按钮。现在,让我们再次点击主窗体中的“打印预览A”按钮,结果如下图所示:
这就不正常了。
再次点击主窗体中的“页面设置A(可能有BUG)”按钮,得到的得到如下图所示:
从上图中可以看到,页边距由图2中的“10毫米”变为现在的“3.94毫米”了。
我们知道,在控制面板的区域选项的度量衡系统设为英制时,页边距单位是“1/10英寸”。我们还知道“1/10英寸≈2.54毫米”。而“10/2.54≈3.94”,这个公式能够说明为什么页边距由图2中的“10毫米”变为现在的“3.94毫米”了。可能是 BCL 的 PageSetupDialog 类做了一次不必要的从公制到英制的转换。而 PageSetupTester.cs 中的第 104 到第 105 行通过调用 PrinterUnitConvert 类的 Convert 方法对页边距做了一次从英制到公制的转换,也就是从 PrinterUnit.Display(0.01英寸) 到 PrinterUnit.TenthsOfAMillimeter(0.1毫米) 的转换,以修正这个BUG。
在上图中点击“确定”按钮后,继续点击主窗体中的“打印预览A”按钮,结果如下图所示:
可以看出,页边距进一步缩小了。
让我们在 Windows Vista 操作系统的 .NET Framework 3.5 环境下编译和运行:
重复上面的测试过程,得到的结果也是一样的。
现在让我们在 Ubuntu 10.10 操作系统的 mono 2.8.1 环境下编译和运行:
点击主窗体的“打印预览A”按钮,得到的结果如下图(图3)所示:
图3 |
可以看到,文字超出矩形框部分被截断了,而不是象 Windows 操作系统中那样折行显示,这应该是 mono 实现的 WinForm 的一个缺点。
再点击主窗体中的“页面设置A(可能有BUG)”按钮,得到的结果如下图(图4)所示:
图4 |
点击“OK”按钮后,再点击主窗体中的“打印预览A”按钮,得到的结果如下图(图5)所示:
图5 |
这可能是页边距微调。再反复点击主窗体中的“页面设置A(可能有BUG)”按钮和“打印预览A”按钮,得到的结果一直如图4和图5所示,不会象在 Windows 操作系统中一样有BUG。
如果点击主窗体中的“页面设置B(修正)”按钮和“打印预览B”按钮,结果和点击“A”系列按钮一样。这是可以预期的,因为在 PageSetupTester.cs 中的第 102 行就已经判断如果是 Unix 类的操作系统的话,就不进行修正,从而“A”和“B”两组按钮的行为是一样的。
让我们在 Ubuntu 10.10 操作系统的 mono 2.6.7 环境下编译和运行:
测试结果和 mono 2.8.1 环境的一样。
让我们在 Windows Vista 操作系统的 mono 2.8.1 环境下编译和运行:
点击主窗体的“打印预览A”按钮,得到的结果如下图(图6)所示:
图6 |
对照前面如图3所示的 Ubuntu 10.10 操作系统 mono 2.8.1 环境下的打印预览,发现这次文字超出矩形框部分不会被截断了,而是正确地进行了折行处理。看来 mono 对 WinForm 的实现跟操作系统还是有关系的。
再点击主窗体中的“页面设置A(可能有BUG)”按钮,得到的结果如下图(图7)所示:
图7 |
点击“OK”按钮后,再点击主窗体中的“打印预览A”按钮,得到的结果如下图(图8)所示:
图8 |
这可能是页边距微调。再反复点击主窗体中的“页面设置A(可能有BUG)”按钮和“打印预览A”按钮,得到的结果一直如图7和图8所示,不会象在 Windows 操作系统的 .NET Framework 4 环境中一样有BUG,虽然这也是在 Windows 操作系统中运行,但这次是 mono 2.8.1 环境。
我们这次点击主窗体中的“打印预览B”按钮,结果如图6所示,这是正常的。再点击主窗体中“页面设置B(修正)”按钮,结果如图7所示,然后点击“OK”按钮,再次点击主窗体中的“打印预览B”按钮,结果如下图所示:
咦,页边距变大了。再次点击主窗体中的“页面设置B(修正)”按钮,结果如下图所示:
可以看出,页边距由图7中的“25毫米”变为现在的“63毫米”。这是 PageSetupTester.cs 程序中第 104 行到第 105 行的从英制到公制的转换造成的:“25 * 2.54 = 63.5”。程序中第 102 行认为不是 Unix 类的操作系统就需要进行修正,而实际上在 Windows 操作系统的 mono 环境中 PageSetupDialog 类没有这个 BUG,不需要修正。在 PageSetupDialog 类没有 BUG 的情况下去修正反而会出问题的。
最后,就是提供打印预览功能的 PreviewPrintDialog.cs 了:
01: using System.Drawing; 02: using System.Drawing.Printing; 03: using System.Windows.Forms; 04: 05: namespace Skyiv 06: { 07: sealed class PreviewPrintDialog : Form 08: { 09: Label lbl; 10: PictureBox pbx; 11: Button btnPrevPage; 12: Button btnNextPage; 13: PreviewPageInfo[] ppi; 14: 15: public PreviewPrintDialog(PreviewPageInfo[] ppi) 16: { 17: this.ppi = ppi; 18: 19: Text = "打印预览"; 20: StartPosition = FormStartPosition.CenterScreen; 21: Width = 400; 22: Height = 610; 23: ShowInTaskbar = false; 24: 25: var pnlMain = new Panel(); 26: pnlMain.Parent = this; 27: pnlMain.Dock = DockStyle.Fill; 28: pnlMain.AutoScroll = true; 29: 30: var pnlTop = new Panel(); 31: pnlTop.Parent = this; 32: pnlTop.Dock = DockStyle.Top; 33: pnlTop.Height = 30; 34: 35: var page = 0; 36: 37: lbl = new Label(); 38: lbl.Parent = pnlTop; 39: lbl.Text = string.Format("第{0}页 共{1}页", page + 1, ppi.Length); 40: lbl.Left = 5; 41: lbl.Top = 8; 42: lbl.Width = 95; 43: 44: btnPrevPage = new Button(); 45: btnPrevPage.Parent = pnlTop; 46: btnPrevPage.Text = "上页(&V)"; 47: btnPrevPage.Left = 100; 48: btnPrevPage.Top = 3; 49: btnPrevPage.Width = 80; 50: btnPrevPage.Enabled = (page > 0); 51: btnPrevPage.Click += delegate { if (page > 0) --page; PageHandler(page); }; 52: 53: btnNextPage = new Button(); 54: btnNextPage.Parent = pnlTop; 55: btnNextPage.Text = "下页(&N)"; 56: btnNextPage.Left = 200; 57: btnNextPage.Top = 3; 58: btnNextPage.Width = 80; 59: btnNextPage.Enabled = (page < ppi.Length - 1); 60: btnNextPage.Click += delegate { if (page < ppi.Length - 1) ++page; PageHandler(page); }; 61: 62: var btnClose = new Button(); 63: btnClose.Parent = pnlTop; 64: btnClose.Text = "关闭(&C)"; 65: btnClose.Left = 300; 66: btnClose.Top = 3; 67: btnClose.Width = 80; 68: btnClose.DialogResult = DialogResult.OK; 69: 70: var size = ppi[page].Image.Size; 71: 72: var pnlImage = new Panel(); 73: pnlImage.Parent = pnlMain; 74: pnlImage.Width = pnlMain.Width - 10; 75: pnlImage.Height = pnlImage.Width * size.Height / size.Width; 76: pnlImage.BackColor = Color.White; 77: 78: pbx = new PictureBox(); 79: pbx.Parent = pnlImage; 80: pbx.ClientSize = pnlImage.ClientSize; 81: pbx.Image = ppi[page].Image; 82: pbx.BorderStyle = BorderStyle.FixedSingle; 83: pbx.SizeMode = PictureBoxSizeMode.StretchImage; 84: } 85: 86: void PageHandler(int page) 87: { 88: btnPrevPage.Enabled = (page > 0); 89: btnNextPage.Enabled = (page < ppi.Length - 1); 90: lbl.Text = string.Format("第{0}页 共{1}页", page + 1, ppi.Length); 91: pbx.Image = ppi[page].Image; 92: } 93: } 94: }
版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/u013948187/article/details/47163931