一个可以打印任何控件的组件,包括 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
作为所需的宽度和高度,当然,在添加了边距之后。
结论
我希望我已经很好地解释了如何使用该组件,并且它将对您有所帮助。我想在文章中回顾代码,但我认为它有点大且不必要。我在代码中添加了注释,也许会有帮助。如果任何人有任何想法、建议或发现任何错误,我将热切等待您的帖子。
感谢您花时间阅读本文。