FormPrint 作为简单的类
一篇关于如何打印(或另存为位图文件)WinForms 或任何其他控件的文章。
引言
“私有类”FormPrint
使您能够将任何窗体或其他控件硬编码打印到打印机或图像文件。为此,该类使用公共静态方法Print()
或Save()
。这对于开发人员或用户的文档记录和调试目的可能很有用。
我在讨论区找到的其他解决方案太庞大和复杂,无法简单使用。因此,我决定创建自己的解决方案。
使用代码
代码是为 NET 2.0 编写的。适应 NET 1.1 的方法将在本文后面介绍。
在源代码中插入对该类的引用
using JThomas.Tools;
如果您将此类放入名为Tools
的单独程序集中,您当然需要在您的应用程序中引用Tools.Dll。
在源代码中插入,例如,在按钮或ToolStripMenuItem
的Click
事件中,使用指定的窗体或控件调用该类的方法。
要打印的版本
以下是一些打印示例
/// print the actual form on the default printer with default settings FormPrint.Print(this); /// print another form that may not call a null reference, of course FormPrint.Print(WinForm2); /// print any control on the default printer with default settings, e.g.: FormPrint.Print(panel1); /// print using special settings: 8 bpp pixelformat, another printer, /// your own margins instead of the default ones, landscape orientation Margins aMargins = new System.Drawing.Printing.Margins(80,50,40,40); FormPrint.Print(this, ColorDepthReduction.Colored8bpp, "PDF Printer", aMargins, false);
要保存的版本
以下是一些保存示例
/// Save: this version sets filename and uses png format
FormPrint.Save(this, "E:\\Temp\\MainForm.Png");
/// Save: this version sets ImageFormat and filename with standard folder
FormPrint.Save(listBox1, System.Drawing.Imaging
.ImageFormat.Bmp, "Listbox.Bmp");
大多数参数都可以省略。
所有版本的参数
Control Ctrl
表示要打印或保存的窗体或任何其他控件。此参数是强制性的。
ImageFormat fImage
指定位图的保存方式;如果省略,则使用ImageFormat.Png。打印始终使用 png 格式。
打印参数
ColorDepthReduction f8bitPixel
说明如何打印位图:标准是None
,即 PixelFormat.Canonical 作为 32 位颜色;Colored8bpp
将位图转换为 PixelFormat.Format8bppIndexed,而Grayscaled8bpp
转换为 8bpp 灰度。请参阅后面的“打印机问题”。
string sPrinterName
说明要使用标准打印机以外的其他打印机。如果省略,则使用标准打印机。
Margins aMargins
设置特殊的页边距。如果省略,则使用当前打印机的默认边距。
bool bPortrait
设置方向为纵向(true)或横向(false)。如果省略,则方向设置为纵向。
保存参数
string sFileName
包含完整的文件名。如果它只是一个没有路径的文件名,则位图保存在当前文件夹中。如果省略文件名,则会创建一个标准文件名,格式为:用户文件夹‘MyPictures’,控件名称,以及imageformat
的扩展名。
工作原理
FormPrint
类为所需的窗体或控件创建一个bitmap
对象。将其发送到标准打印机或以适当的格式保存到文件。您可以调用实际的窗体或其他应用程序中的窗体,也可以调用任何其他控件(例如,GroupBox
、Panel
等)。所有这些都组合在一个简单的类FormPrint
中。
FormPrint
类包含以下私有变量
// The FormPrint instance cls is used to execute printing resp. saving.
static FormPrint cls;
// The bitmap bmp contains the copy of the control's graphics.
Bitmap bmp;
// deciding whether to print or save
bool Printing;
// additional: variables related to each of the parameters
// mentioned above
您可以使用重载的静态方法FormPrint.Print()
和FormPrint.Save()
并指定所需参数。如果省略某个参数,将使用默认值。
所有变体都调用一个私有静态方法FormPrint.Start()
来执行工作:首先,它检查所有必需的参数是否已设置。然后,创建FormPrint
类的一个私有实例cls
,并将所有参数传递给它。最后,私有方法StartPrinting()
和StartSaving()
执行底层工作。
// create the private formprint class cls
cls = new FormPrint(Ctrl, bPrint, fImage, f8bitPixel,
sPrinterName, aMargins, bPortrait, sFileName);
// execute the required printing resp. saving method
try {
if (bPrint) {
cls.StartPrinting();
} else {
cls.StartSaving();
}
} finally {
// free resources
cls.bmp.Dispose();
cls.bmp = null;
// in some situations, the next command is useful
Ctrl.Refresh();
}
构造函数创建一个Bitmap
对象bmp
,该对象的大小和图形与控件相同,并将控件的屏幕内容复制到Bitmap
对象bmp
中。
private FormPrint(Control Ctrl, bool bPrint,
// formatings, convertings
ImageFormat fImage, ColorDepthReduction f8bitPixel
// additional parameters for printing
string sPrinterName, Margins aMargins, bool bPortrait,
// additional parameter for saving
string sFileName)
{
// call the bitmap in the propriate way - NET 1.1 or NET 2.0
ctrl = Ctrl;
bmp = GetBitmap(Ctrl);
/* set all other parameters ... these instructions are removed here */
}
在 NET 2.0 下,可以使用一条指令复制控件的图像。
// NET 2.0 - Copy the control's graphics directly to the bitmap
private Bitmap GetBitmap(Control Ctrl) {
// prepare the bitmap
Graphics grCtrl = Ctrl.CreateGraphics();
Bitmap Result = new Bitmap(Ctrl.Width, Ctrl.Height,
grCtrl);
// copy the control's graphics – not available
with NET 1.1
Ctrl.DrawToBitmap( Result, new Rectangle(0, 0,
Ctrl.Width, Ctrl.Height));
// that's all work for the bmp using NET 2.0
// instructions
return Result;
}
在 NET 1.1 下,必须使用复杂的 Windows GDI 解决方法。因此,这项工作由一个单独的方法GetBitmap()
完成。如果您想了解更多信息,请查看源代码。
如果您想打印,bmp
会被发送到PrintDocument
。如果需要,会使用一些指令(但在以下代码片段中未显示):应用打印机名称或页面设置,将大位图压缩到纸张大小,将 32bpp 位图转换为 8bpp 位图以避免打印机内存问题。
private void StartPrinting() {
// create a document; the event handler PrintPage
// will connect it with the bitmap
using(PrintDocument doc = new PrintDocument()){
doc.PrintPage += new PrintPageEventHandler(
doc_PrintPage);
// setting printer name and page settings,
// if required
// if margins are set, then check if bitmap has to
// be compressed
if (PrinterMargins != null) {
// check if bmp size is larger than PaperSize
if ( (bmp.Width > bmpX) || (bmp.Height >
bmpY) )
GetThumbnail((int)bmpX, (int)bmpY);
}
// convert to 8-bit-pixel or grayscale to avoid
// printer problems
if (format8bitPixel != ColorDepthReduction.None) {
ConvertBitmapTo8Bpp(15);
// now print
doc.DocumentName = SaveFileName;
doc.Print();
}
}
如果您想保存,则直接通过
bmp.Save(SaveFileName, formatImage);
此外,代码包含一些try-catch
块,用于将bmp
连接到PrintDocument
的PrintPageEventHandler
,doc
,释放资源,以及用于压缩位图的实用工具GetThumbnail()
和ConvertBitmapTo8Bpp().
由于转换可能需要几秒钟,因此包含一个带有简单标签的ProgressForm
。仅当打印的位图像素超过 40,000 个(例如 200x200)时才显示此内容。
附加使用提示
如果您通过MenuItem.Click
事件调用该方法,则应使用ShortCut
;否则,打开的菜单也会被复制。请注意:始终打印或保存实际屏幕内容;如果窗体部分隐藏,则打印前景屏幕。不匹配的控件不总是能正确打印,例如工具栏上的普通按钮。如果设置了Control.AutoScroll
,则必须注意 SDK 文档。
适应 NET 1.1
两条指令与 NET 1.1 不兼容;其中一条需要复杂的解决方法。要进行适应,请设置适当的#define
指令
/// with NET 2.0 use as follows:
#define NET_V20
#undef NET_V11
/// with NET 1.1 use as follows:
#define NET_V11
#undef NET_V20
打印机问题
在这两个版本中,全屏窗体都可能导致打印机问题,例如“内存不足”或“数据过多”。如果出现这种情况,请尝试使用 8 位像素格式,通过ColorDepthReduction f8bitPixel
参数。如果问题仍然存在,您应该打印一个较小的控件,例如面板或组框,而不是整个窗体。在这种情况下,我建议您将窗体与一些控制要打印的设置组合起来。
演示窗体
要创建演示应用程序,请使用FormPrintDemoForm.cs
作为主窗体,FormPrint.cs
作为附加类。该窗体包含一个列表框,用于选择FormPrint.Print()
和FormPrint.Save()
的任何变体,以及一个带有 256 色标签的面板。您需要在buttonStart_Click()
方法中更改常量值,并在构造函数中更改richTextBox1.LoadFile()
调用。
可能的扩展
我选择了比最初计划更多的参数。我省略了以下可能性
- 页面设置对话框
- 如果文件保存成功,则显示消息框
感谢帮助
最初,我采用了 MSDN 库中的方法 如何:打印 Windows 窗体。
Tim van Wassenhove 告诉我使用DrawToBitmap()
。
“Programmierhans”在 NET 1.1 中演示了如何复制控件的图形,并在他 2005 年 8 月 2 日 19:49 的帖子中提到了GetThumbnail()
方法。
Robert Schmitz "robertico" 开发了将位图快速转换为 8bpp 像素格式和灰度图的方法ConvertBitmapTo8Bpp()
。(使用 GetPixel/SetPixel 的 MSDN 解决方案需要不可接受的时间。)
我的目标是将所有这些结合成一个带有可变参数的简单类。
历史
2006/12/17 - 第一个版本。
12/19/2006
1. 添加:清除资源(位图、图形)
2. 添加:纵向或横向打印12/24/2006
1. 更改:一种更短的将控件图形复制到位图中的方法
2. 更改注释:目前没有适应 NET 1.1。12/25/2006
1. 添加:使用 NET 1.1 将控件图形复制到位图中
2. 更改:变量和方法重新分组,但没有其他功能
3. 更改注释:适应 NET 1.1
4. 添加注释:已知问题,感谢帮助01/13/2007
1. NET 1.1 更改:窗体可包含标题和菜单栏进行打印
2. 更改:公共方法 Execute() 分割为 Print() 和 Save()
3. 更改:Print() 允许多个参数:PrinterName、Margins、Orientation
4. 更改:打印可以根据需要压缩到纸张大小(带边距)
5. 更改:大多数参数都可以省略
6. 更改:通过转换为 PixelFormat.Format8bppIndexed 和灰度位图来管理打印机内存问题,而不是彩色 PixelFormat.Canonical
7. 更改:Execute() 在打印大型控件(超过 40000 像素,例如 200x200)时使用进度标签
8. 更改:私有方法 Execute() 分割为 StartPrinting() 和 StartSaving()01/22/2007
1. 更改:通过 #define 适应 NET 1.1
2. 更改:PixelFormat.Format8bppIndexed 和灰度图替换为 ColorDepthReduction 枚举03/22/2007
NET 2.0 改进了 RichTextBox 或 WebBrowser 的显示(这些控件无法使用 DrawToBitmap)