PageSetupDialog 类的一个 BUG

我们在用 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 的内容如下:

-t:winexe
-r:System.Drawing.dll
-r:System.Windows.Forms.dll
PageSetupTester.cs
PreviewPrintDialog.cs
RuntimeFramework.cs

用于打印预览的 PreviewPrintDialog.cs 将在本文最后给出。而 RuntimeFramework.cs 请参见我在2009年12月13日写的“.NET Framework CLR 版本检测”一文。

让我们在 Windows Vista 操作系统的 .NET Framework 4 环境下编译和运行:

E:\work> C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc @mak.rsp
Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

E:\work> PageSetupTester

然后点击“打印预览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 环境下编译和运行:

E:\work\> C:\Windows\Microsoft.NET\Framework\v3.5\csc @mak.rsp
适用于 Microsoft(R) .NET Framework 3.5 版的 Microsoft(R) Visual C# 2008 编译器 3.5.30729.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

E:\work> PageSetupTester

重复上面的测试过程,得到的结果也是一样的。

现在让我们在 Ubuntu 10.10 操作系统的 mono 2.8.1 环境下编译和运行:

[email protected]:~/work$ /opt/mono-2.8.1/bin/dmcs @mak.rsp
[email protected]:~/work$ /opt/mono-2.8.1/bin/mono PageSetupTester.exe

点击主窗体的“打印预览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 环境下编译和运行:

[email protected]:~/work$ gmcs @mak.rsp
[email protected]:~/work$ ./PageSetupTester.exe

测试结果和 mono 2.8.1 环境的一样。

让我们在 Windows Vista 操作系统的 mono 2.8.1 环境下编译和运行:

E:\work> dmcs @mak.rsp
E:\work> mono PageSetupTester.exe

点击主窗体的“打印预览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原创文章,未经博主允许不得转载。

时间: 2024-12-14 23:49:34

PageSetupDialog 类的一个 BUG的相关文章

使用getDrawable时遇到的一个bug

做一个筛选菜单时候,用到了dongjunkun的DropDownMenu,github地址:https://github.com/dongjunkun/DropDownMenu 遇到几个问题: (1)最右面的上三角形.下三角形很难看,需要改成向上箭头向下箭头,而且靠近文件,在右边: (2)背景颜色需要改成白色: (3)下面的子菜单的文字在最左边,需要居中: (4)第一次进来Fragment的时候DropDownMenu的下拉选项没有选中任意一项 上面几个需求看起来很容易改,不就是改改布局什么的,

3 CActiveXUI的一个Bug

如果主窗口直接用变量生成,则关闭窗口时会产生崩溃 如果用new的方式生成,则不会崩溃,所以给出一个临时的快速解决方案,即主窗口都用new生成,_tWinMain改为下面这样: int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { CPaintManagerUI::SetInstance(hInstance); HRESULT Hr = ::CoI

由一个bug引出java包装类型

工作中遇到过一个bug,用两个POJO的 Integer 字段 做 == 判断,明明"数值"相等结果返回 false.检查代码,调试,看源码搞了好久,才知道是Java包装类理解不够惹的祸. 为了弄清楚其中的本质,先上一段代码: 1 int a = 5; 2 Integer b = 5; 3 Integer c = Integer.valueOf(5); 4 Integer d = Integer.valueOf(5); 5 Integer e = new Integer(5); 6 7

Tomcat一个BUG造成CLOSE_WAIT

之前应该提过,我们线上架构整体重新架设了,应用层面使用的是Spring Boot,前段日子因为一些第三方的原因,略有些匆忙的提前开始线上的内测了.然后运维发现了个问题,服务器的HTTPS端口有大量的CLOSE_WAIT: 我的第一反应是Spring boot有Bug,因为这个项目分为HTTP和HTTPS两种服务以JAR的形式启动的,而HTTP的没有问题,同时,老架构的服务在Tomcat中以HTTPS提供服务也没有问题,我当时认为这大致上可以判断为Socket层面应该是没有问题的,于是我开始分析S

CSS IE6、7下关于Position的一个bug问题分享

又好久没来了,小码哥甚是想念 想念我的人.由于近期工作中跟CSS打交道较多,所以偶尔会碰到有关它的一些问题,CSS很强大,尤其是后来的CSS3.鄙人正在学习中,如果就所遇到的问题,分析有偏差,望大家海涵哈!!下面就说说我刚刚遇到的一个问题,也许某些前辈大拿们会在心里BS我,不过对我来说,都是收获!嘎嘎,, 总所周知,我们都知道CSS中的定位position属性是一个相当重要和特殊的属性.它分别有一下几个属性值: position:relative; position:absolute; posi

NGUI中UILabel使用url标签的一个bug

在NGUI里,UILabel控件可以支持一些简单功能的标签,使文本显示更丰富及实现类似超链接的功能.但是在使用的时候发现了NGUI3.5.9版本里存在着一个bug.不过还好修复这个bug也很简单. 在UILabel中支持[url=link]text[/url]的方式来定义类超链接的文本.bug就出现在同一个UILabel里使用两个及以上这种标签时,最终显示的label内容就会全错掉.   text内容:[url=a]a[/url][url= 当再输入任一字符后,label的内容就全消失了. bu

[置顶]VC2013的一个bug

前段时间在尝试使用一个C++的GUI库nana.这个库最大的特点在于使用现代C++风格去编写GUI程序,而不需要使用大量的比较丑陋的代码(如MFC中的各种宏),或者其它的非C++元素.这是一个比较新的库,作者是个中国人,有兴趣的朋友可以去试一试,由于使用大量的C++11特性,所以需要VC2013或者GCC4.7以上的编译器.使用过程中无意间发现了VC2013的一个重载决议(overload resolution)上的一个bug,这边贴出来跟大家分享一下,或许可以帮助大家少走点弯路. 我写了以下简

(四)一个bug的生命周期

Bug的属性 Bug重现环境 这个应该是我们重现BUG的一个前提,如果没有这个前提,我们可能会无法重现问题,或者根本就无从下手. ? 操作系统 这个是一般软件运行的一大前提,基本上所以批的软件都依赖于操作系统之上的,对于一个软件来说,要想在某个操作系统上运行,必须要对这个操作系统支持,这就需要有针对性的设计与开发.对于不同的操作系统,其可能存在差异(如:win xp 与win 7)或本质的区别(如 win 7 与 CentOS linux),所以,操作系统环境是重现问题的一个重要前提. ? 浏览

VC2013的一个bug

前段时间在尝试使用一个C++的GUI库nana.这个库最大的特点在于使用现代C++风格去编写GUI程序,而不需要使用大量的比较丑陋的代码(如MFC中的各种宏),或者其它的非C++元素.这是一个比较新的库,作者是个中国人,有兴趣的朋友可以去试一试,由于使用大量的C++11特性,所以需要VC2013或者GCC4.7以上的编译器.使用过程中无意间发现了VC2013的一个重载决议(overload resolution)上的一个bug,这边贴出来跟大家分享一下,或许可以帮助大家少走点弯路. 我写了以下简