C# 如何使用带可滚动面板的PrintDocument?
如何使用带有可滚动面板的C# 如何使用带可滚动面板的PrintDocument?,c#,winforms,printing,C#,Winforms,Printing,如何使用带有可滚动面板的PrintDocument 以下是我的一些代码: MemoryImage = new Bitmap(pnl.Width, pnl.Height); Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height); pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width, pnl.Height)); Rectangle pagearea = e.Pag
PrintDocument
以下是我的一些代码:
MemoryImage = new Bitmap(pnl.Width, pnl.Height);
Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height);
pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width,
pnl.Height));
Rectangle pagearea = e.PageBounds;
e.Graphics.DrawImage(MemoryImage, (pagearea.Width / 2) -
(pannel.Width / 2), pannel.Location.Y);
这些方法集允许将文件的内容打印到位图 程序说明:
control.AutoScrollPosition=new Point(0,0);
(否则会引发异常:位图大小错误。您可能希望存储当前的滚动位置,然后将其还原)这将是位图的大小
边界
矩形相对于容器客户端区域)DrawNestedControls
,该方法将枚举并绘制所有嵌套的子容器/控件,保留内部剪辑边界RichEditPrinter
类包含打印RichTextBox/RichEdit控件内容所需的逻辑。该类使用正在打印控件的位图的设备上下文向RichTextBox发送消息。MSDN文档中提供了更多详细信息:
ScrollableControlToBitmap()
方法仅将ScrollableControl
类型作为参数:无法传递TextBox控件,即使它使用滚动条
► 将fullSize
参数设置为true
或false
以包含容器内的所有子控件或仅包含可见的子控件。如果设置为true
,则容器的ClientRectangle
将展开以包含并打印其所有子控件
► 将includeHidden
参数设置为true
或false
以包括或排除隐藏控件(如果有)
注意:此代码使用属性评估容器设备上下文的当前Dpi。此属性需要.Net Framework 4.7+。如果此版本不可用,您可以删除:
bitmap.SetResolution(canvas.DeviceDpi, canvas.DeviceDpi);
或使用其他方法导出值。请参阅。可能,请更新项目的框架版本:)
使用系统图;
使用系统、绘图、成像;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
公共类控制打印机
{
公共静态位图ScrollableControlToBitmap(ScrollableControlCanvas,bool fullSize,bool includeHidden)
{
canvas.AutoScrollPosition=新点(0,0);
如果(包括隐藏){
canvas.SuspendLayout();
foreach(canvas.Controls中的控件子级){
可见=真实;
}
canvas.ResumeLayout(true);
}
canvas.PerformLayout();
Size containerSize=canvas.DisplayRectangle.Size;
if(全尺寸){
containerSize.Width=Math.Max(containerSize.Width,canvas.ClientSize.Width);
containerSize.Height=Math.Max(containerSize.Height,canvas.ClientSize.Height);
}
否则{
containerSize=(画布是表单)?canvas.PreferredSize:canvas.ClientSize;
}
var bitmap=新位图(containerSize.Width、containerSize.Height、PixelFormat.Format32bppArgb);
SetResolution(canvas.DeviceDpi,canvas.DeviceDpi);
var graphics=graphics.FromImage(位图);
if(canvas.BackgroundImage!=null){
graphics.DrawImage(canvas.BackgroundImage,新矩形(Point.Empty,containerSize));
}
否则{
图形。清晰(画布。背景色);
}
var rtfPrinter=新的RichEditPrinter(图形);
试一试{
DrawNestedControls(画布、画布、新矩形(Point.Empty、containerSize)、位图、rtfPrinter);
返回位图;
}
最后{
rtfPrinter.Dispose();
graphics.Dispose();
}
}
私有静态void DrawNestedControls(控件outerContainer、控件父级、矩形父边界、位图位图、RichEditPrinter rtfPrinter)
{
对于(int i=parent.Controls.Count-1;i>=0;i--){
var ctl=parent.Controls[i];
如果(!ctl.Visible | | |(ctl.Width<1 | | ctl.Height<1))继续;
var clipBounds=Rectangle.Empty;
如果(parent==outerContainer){clipBounds=ctl.Bounds;}
否则{
如果((父项!=ctl)&&parent为ScrollableControl scrctl){
Size scrContainerSize=parentBounds.Size;
if(父级为ScrollableControl scrctl){
如果(scrctl.VerticalScroll.Visible)scrccontainersize.Width-=(SystemInformation.VerticalScrollBarWidth+1);
如果(scrctl.horizontalcroll.Visible)scrccontainersize.Height-=(SystemInformation.horizontalcrollbarheight+1);
}
剪贴簿=Rectangle.Intersect(新矩形(Point.Empty,scrContainerSize),ctl.Bounds);
}
如果(剪贴簿宽度<1 | |剪贴簿高度<1)继续;
var bounds=outerContainer.RectangleToClient(parent.RectangleToScreen(剪贴簿));
如果(ctl是RichTextBox rtb){
rtfPrinter.DrawRtf(rtb.Rtf,outerContainer.Bounds,Bounds,ctl.BackColor);
}
否则{
ctl.DrawToBitmap(位图,边界
// Prints the content of the current Form instance,
// include all child controls and also those that are not visible
var bitmap = ControlPrinter.ScrollableControlToBitmap(this, true, true);
// Prints the content of a ScrollableControl inside a Form
// include all child controls except those that are not visible
var bitmap = ControlPrinter.ScrollableControlToBitmap(this.panel1, true, false);
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ControlPrinter
{
public static Bitmap ScrollableControlToBitmap(ScrollableControl canvas, bool fullSize, bool includeHidden)
{
canvas.AutoScrollPosition = new Point(0, 0);
if (includeHidden) {
canvas.SuspendLayout();
foreach (Control child in canvas.Controls) {
child.Visible = true;
}
canvas.ResumeLayout(true);
}
canvas.PerformLayout();
Size containerSize = canvas.DisplayRectangle.Size;
if (fullSize) {
containerSize.Width = Math.Max(containerSize.Width, canvas.ClientSize.Width);
containerSize.Height = Math.Max(containerSize.Height, canvas.ClientSize.Height);
}
else {
containerSize = (canvas is Form) ? canvas.PreferredSize : canvas.ClientSize;
}
var bitmap = new Bitmap(containerSize.Width, containerSize.Height, PixelFormat.Format32bppArgb);
bitmap.SetResolution(canvas.DeviceDpi, canvas.DeviceDpi);
var graphics = Graphics.FromImage(bitmap);
if (canvas.BackgroundImage != null) {
graphics.DrawImage(canvas.BackgroundImage, new Rectangle(Point.Empty, containerSize));
}
else {
graphics.Clear(canvas.BackColor);
}
var rtfPrinter = new RichEditPrinter(graphics);
try {
DrawNestedControls(canvas, canvas, new Rectangle(Point.Empty, containerSize), bitmap, rtfPrinter);
return bitmap;
}
finally {
rtfPrinter.Dispose();
graphics.Dispose();
}
}
private static void DrawNestedControls(Control outerContainer, Control parent, Rectangle parentBounds, Bitmap bitmap, RichEditPrinter rtfPrinter)
{
for (int i = parent.Controls.Count - 1; i >= 0; i--) {
var ctl = parent.Controls[i];
if (!ctl.Visible || (ctl.Width < 1 || ctl.Height < 1)) continue;
var clipBounds = Rectangle.Empty;
if (parent == outerContainer) { clipBounds = ctl.Bounds; }
else {
if ((parent != ctl) && parent is ScrollableControl scrctl) {
Size scrContainerSize = parentBounds.Size;
if (parent is ScrollableControl scrctl) {
if (scrctl.VerticalScroll.Visible) scrContainerSize.Width -= (SystemInformation.VerticalScrollBarWidth + 1);
if (scrctl.HorizontalScroll.Visible) scrContainerSize.Height -= (SystemInformation.HorizontalScrollBarHeight + 1);
}
clipBounds = Rectangle.Intersect(new Rectangle(Point.Empty, scrContainerSize), ctl.Bounds);
}
if (clipBounds.Width < 1 || clipBounds.Height < 1) continue;
var bounds = outerContainer.RectangleToClient(parent.RectangleToScreen(clipBounds));
if (ctl is RichTextBox rtb) {
rtfPrinter.DrawRtf(rtb.Rtf, outerContainer.Bounds, bounds, ctl.BackColor);
}
else {
ctl.DrawToBitmap(bitmap, bounds);
}
if (ctl.HasChildren) {
DrawNestedControls(outerContainer, ctl, clipBounds, bitmap, rtfPrinter);
}
}
}
internal class RichEditPrinter : IDisposable
{
Graphics dc = null;
RTBPrinter rtb = null;
public RichEditPrinter(Graphics graphics)
{
this.dc = graphics;
this.rtb = new RTBPrinter() { ScrollBars = RichTextBoxScrollBars.None };
}
public void DrawRtf(string rtf, Rectangle canvas, Rectangle layoutArea, Color color)
{
rtb.Rtf = rtf;
rtb.Draw(dc, canvas, layoutArea, color);
rtb.Clear();
}
public void Dispose() => this.rtb.Dispose();
private class RTBPrinter : RichTextBox
{
public void Draw(Graphics g, Rectangle hdcArea, Rectangle layoutArea, Color color)
{
using (var brush = new SolidBrush(color)) {
g.FillRectangle(brush, layoutArea);
};
IntPtr hdc = g.GetHdc();
var canvasAreaTwips = new RECT().ToInches(hdcArea);
var layoutAreaTwips = new RECT().ToInches(layoutArea);
var formatRange = new FORMATRANGE() {
charRange = new CHARRANGE() { cpMax = -1, cpMin = 0 },
hdc = hdc,
hdcTarget = hdc,
rect = layoutAreaTwips,
rectPage = canvasAreaTwips
};
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(formatRange));
Marshal.StructureToPtr(formatRange, lParam, false);
SendMessage(this.Handle, EM_FORMATRANGE, (IntPtr)1, lParam);
Marshal.FreeCoTaskMem(lParam);
g.ReleaseHdc(hdc);
}
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
internal const int WM_USER = 0x0400;
// https://docs.microsoft.com/en-us/windows/win32/controls/em-formatrange
internal const int EM_FORMATRANGE = WM_USER + 57;
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
public RECT ToInches(Rectangle rectangle)
{
float inch = 14.92f;
return new RECT() {
Left = (int)(rectangle.Left * inch),
Top = (int)(rectangle.Top * inch),
Right = (int)(rectangle.Right * inch),
Bottom = (int)(rectangle.Bottom * inch)
};
}
}
// https://docs.microsoft.com/en-us/windows/win32/api/richedit/ns-richedit-formatrange?
[StructLayout(LayoutKind.Sequential)]
internal struct FORMATRANGE
{
public IntPtr hdcTarget; // A HDC for the target device to format for
public IntPtr hdc; // A HDC for the device to render to, if EM_FORMATRANGE is being used to send the output to a device
public RECT rect; // The area within the rcPage rectangle to render to. Units are measured in twips.
public RECT rectPage; // The entire area of a page on the rendering device. Units are measured in twips.
public CHARRANGE charRange; // The range of characters to format (see CHARRANGE)
}
[StructLayout(LayoutKind.Sequential)]
internal struct CHARRANGE
{
public int cpMin; // First character of range (0 for start of doc)
public int cpMax; // Last character of range (-1 for end of doc)
}
}
}
}