一个可以打印任何控件的组件,包括 ListView、TreeView、DataGridView 和 Form






4.81/5 (66投票s)
终极打印组件。使用它可以打印 ListBox、ListView、TreeView、DataGridView、Form、TabPage,甚至您自己的 UserControl。

术语
在本文中,除非另有说明,以下词语被提及以指代特定含义。
- 控件:您希望打印的控件。
- 组件:本文正在讨论的 PrintControl组件。
注意
此组件需要 .NET Framework 2.0 或更高版本。
引言
嗯,我想标题很清楚。这完全是关于打印的。在我之前开发一些企业应用程序时,我在应用程序中使用了许多列表,并且需要全部打印它们,但它们在不同的窗体上,所以每个窗体上,我都要创建一个 PrintDocument 组件并添加一个 PrintPage 事件处理程序来手动绘制其内容,花费数小时计算每个表的总高度和宽度,然后将其拟合到页面大小。嗯,我实际上并没有这样做。相反,我想到了一个可以自动为我完成这项工作的组件,就这样。ControlPrint 组件可以完成这项工作。
此外,我还有一些用于输入姓名、年龄、性别、地址等的输入表单。我也想打印这些信息,但必须自定义另一个打印表单,通过在打印中绘制它!!ControlPrint 组件也会将它打印到纸上。请看本文顶部的图片。
我的组件继承自 PrintDocument 组件,并在 PrintPage 事件处理程序中完成了所有打印工作。所以要使用它,请按照以下步骤操作:
您可以像对待任何 PrintDocument 组件一样对待它,使用以下代码:
PrintControl m_print = new PrintControl();
m_print.SetControl(MyControl);
prinPreviewDialog1.Document = (PrintDocument)m_print;
背景
该组件的主要思想是将您希望打印的控件,无论是 ListView、ListBox、TreeView,还是其他任何控件,都绘制到打印机上,就像它在屏幕上绘制一样。有点像 WYSIWYG,或者“所见即所得”!所以,这里的主要步骤是:
- 获取控件。
- 计算显示控件内所有元素的最佳大小。纸上没有滚动条,对吧?
- 将控件绘制在页面上,就像在屏幕上一样。
当然,这并不那么简单,但我们会一步一步地介绍。
它是如何工作的?
ControlPrint 组件支持任何控件,包括 UserControl,它们都继承自 Control 类。它实际上调用控件的 GetPreferredSize() 方法来获取显示所有元素的控件的最佳大小。有些控件不支持此方法,并返回错误的预估大小。这就是为什么我在我的组件中添加了 CalculateSize() 和 ApplyBestSize() 方法来为这些控件添加更多特殊支持。目前,特殊支持的控件包括:
- ListView
- TreeView
- Form 以及包含其他控件的控件,例如 TabPage、GroupBox等……
其他控件应该可以使用普通的 GetPreferredSize() 方法正常工作,包括 DataGridView、ListBox 等。另外,Form 通常也能很好地使用 GetPreferredSize(),很少需要使用 CalculateSize() 方法。
该组件使用 Control.DrawToBitmap() 方法将其绘制到位图,然后进行打印。
您也可以使用 PrintControl.GetBitmap() 方法获取该位图,并按您喜欢的方式使用它,例如将其保存到文件,或在其他文本中打印等。
所以,在所有这些之后,打印步骤如下:
- 获取控件大小、停靠状态和父控件的旧值。
- 将停靠设置为 none,并将所有父控件的大小设置为大于所需大小,您很快就会明白原因。
- 更改控件的大小为首选大小,这将消除滚动条的需求。
- 将其绘制到位图。
- 恢复控件的状态,包括父控件的状态。
- 根据您的选择进行打印,或返回位图。
我必须移除停靠状态,因为它会阻止调整控件的大小。此外,父控件必须调整大小以适应控件的新尺寸。只有这样,才能成功将其绘制到位图。详情请参阅“有趣的点”。
重要提示
- 如果您自己制作控件,并希望使用 PrintControl组件打印它,请覆盖GetPreferredSize()方法,并返回最适合您的控件的尺寸,如果Control.GetPreferredSize()方法为您的控件返回错误值。
- 您可以通过调用我组件中适当的大小指定方法(见下文)以指定的大小打印控件。当控件大小计算错误时,或者出于您自己的原因偏好这样做时,这可能会有帮助。
- 打印 TreeView时,只会打印展开的节点,即可以通过滚动查看的节点。其他隐藏的节点将不会被打印。因此,如果您想确保打印TreeView中的所有节点,请使用TreeView.ExpandAll()方法。
- 当您更改控件的视觉样式时,它也会像在屏幕上一样被打印出来,很酷,不是吗?
- 视觉样式可能很美观,但它们会消耗更多的墨水和打印时间。所以要仔细考虑。
- 打印 Form 时,您可能需要移除背景颜色或图像,以使文本更清晰。
- 您可以将打印的位图拉伸以完全适合一页,方法是将 StretchControl属性设置为true。但要小心,这会破坏宽高比,并可能导致图像失真。
成员
我认为现在是时候谈谈组件的成员了。
构造函数
- ControlPrint()- 默认构造函数,初始化组件,不带实际值。 
- ControlPrint(Control print)- 使用选定的控件初始化组件。 
- ControlPrint(Control print, bool Str)- 使用选定的控件初始化组件,并指定是否拉伸。 
- ControlPrint(Control print, int Width, int Height)- 使用选定的控件初始化组件,并指定特定的宽度和高度。 
属性
- StretchControl- 设置为 - true以将控件拉伸以填充单个打印页面。
- PrintWidth- 要打印的控件的宽度。如果自动计算的宽度不适合控件的所有元素,您可以更改此值。 
- PrintHeight- 要打印的控件的高度。如果自动计算的高度不适合控件的所有元素,您可以更改此值。 
- ReapeatArea- 在打印多页时,要在页面之间重复打印的区域,以防止数据丢失。 
方法
- void SetControl(Control print)- 设置要打印的控件。 
- void SetControl(Control print, int Width, int Height)- 设置具有指定高度和宽度的控件。 
- Bitmap GetBitmap()- 将控件完整地绘制到位图并返回它。 
- Size CalculateSize()- 返回最适合控件的大小。 
- void ApplyBestSize()- 应用最适合控件的大小。 
有趣的点
这些是我在开发组件过程中认为有趣的点。
- 我首先必须调整控件的大小,否则它只会打印可见部分,并且还会打印滚动条。我认为这帮助不大。
- 尝试在控件停靠时调整它的大小是失败的,所以我必须先解除停靠。
- 同样,控件的父控件必须比它大。否则,控件只会绘制可见部分。我首先尝试将父控件设为 null 然后恢复它,但这对于包含控件的 Form 不起作用。文本字段的值丢失了,我不知道为什么!!有什么想法吗?所以我决定也调整父控件的大小,然后再恢复它们。
- 当控件的长度大于页面的长度时,将有多页需要打印。所以我将打印的长度存储在一个私有字段中,当有多页时,我将当前页要打印的区域放在另一个位图中,然后打印第二个。请参阅代码中的 PrintPage事件处理程序。
- 同样,在打印长表、列表或 DataGrid多页时,行很可能会被分割到两页上,使其数据不可读。所以作为一个简单的解决方案,我添加了一个RepeatArea属性,这是一个要在下一页重新打印的区域。所以如果一行的一半在页面末尾打印,它将在下一页完整地重新打印。RepeatArea是可修改的。如果您有更好的想法来解决这个问题,我很乐意将其添加到组件中。
- 如果您查看源代码,您会注意到我使用了一个递归方法来访问 TreeView的所有节点。您从foreach循环中调用该方法,并在方法内部使用另一个foreach循环来递归调用自身。我的方法名为EnumNodes。
- 我使用 GetType().AssemblyQualifiedName来标识控件的类型。此外,这还将识别继承自它的控件,因为它还将包含其基控件的名称。例如:if (m_ctrl.GetType().AssemblyQualifiedName.IndexOf("TreeView") >= 0) 如果控件是 TreeView控件或继承自它,则if语句将为true。
- 在尝试计算特殊控件(目前是 TreeView、ListView和Form)的大小时,我会枚举它们的元素,分别是Node、Item和Control,获取它们的边界,然后获取最大的Bounds.Right和Bounds.Bottom作为所需的宽度和高度,当然,在添加了边距之后。
结论
我希望我已经很好地解释了如何使用该组件,并且它将对您有所帮助。我想在文章中回顾代码,但我认为它有点大且不必要。我在代码中添加了注释,也许会有帮助。如果任何人有任何想法、建议或发现任何错误,我将热切等待您的帖子。
感谢您花时间阅读本文。


