Vb.net 如何使用滚动条打印容器的隐藏和可见内容

Vb.net 如何使用滚动条打印容器的隐藏和可见内容,vb.net,winforms,graphics,scrollbar,panel,Vb.net,Winforms,Graphics,Scrollbar,Panel,我有一个可滚动的面板:它的一些子控件是隐藏的,另一些是可见的 如何打印此面板上的所有内容,包括隐藏的子控件或在不滚动的情况下不可见的子控件 Private Sub PrintDocument1_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage Panel1.AutoSize = True Dim b As New Bitmap(Panel1.

我有一个可滚动的面板:它的一些子控件是隐藏的,另一些是可见的

如何打印此面板上的所有内容,包括隐藏的子控件或在不滚动的情况下不可见的子控件

Private Sub PrintDocument1_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
    Panel1.AutoSize = True
    Dim b As New Bitmap(Panel1.DisplayRectangle.Width, Panel1.DisplayRectangle.Height)
    Panel1.DrawToBitmap(b, Panel1.ClientRectangle)
    e.Graphics.DrawImage(b, New Point(40, 40))
    Panel1.AutoSize = False
End Sub

这些方法集允许将文件的内容打印到位图

程序说明:

  • 控件首先回滚到原点(
    [ScrollableControl]。AutoScrollPosition=新点(0,0)
    (否则会引发异常:位图大小错误。您可能希望存储当前滚动位置,然后将其还原)
  • 验证并存储由或属性返回的容器的实际大小(取决于方法参数设置的条件和打印的容器类型)。此属性考虑容器的完整范围。
    这将是位图的大小
  • 使用容器的背景色清除位图
  • 迭代ScrollableControl.Controls集合,并在其相对位置打印所有第一级子控件(子控件的
    边界
    矩形相对于容器ClientArea)
  • 如果一级控件有子控件,则调用递归方法
    DrawNestedControls
    ,该方法将枚举并绘制所有嵌套的子容器/控件,保留内部剪辑边界
  • 包括对RichTextBox控件的支持
    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.Drawing.Imaging
    导入System.Runtime.InteropServices
    导入System.Windows.Forms
    公共类控制打印机
    作为位图的公共共享函数ScrollableControlToBitmap(画布作为ScrollableControl,fullSize作为Boolean,includeHidden作为Boolean)
    canvas.AutoScrollPosition=新点(0,0)
    如果包括在内,那么
    canvas.SuspendLayout()
    对于画布中的每个子控件作为控件。控件
    child.Visible=True
    下一个
    canvas.ResumeLayout(True)
    如果结束
    canvas.PerformLayout()
    Dim containerSize As Size=canvas.DisplayRectangle.Size
    如果是全尺寸的话
    containerSize.Width=Math.Max(containerSize.Width,canvas.ClientSize.Width)
    containerSize.Height=Math.Max(containerSize.Height,canvas.ClientSize.Height)
    其他的
    containerSize=If((画布的类型是表单),canvas.PreferredSize,canvas.ClientSize)
    如果结束
    Dim bmp=新位图(containerSize.Width、containerSize.Height、PixelFormat.Format32bppArgb)
    bmp.SetResolution(canvas.DeviceDpi,canvas.DeviceDpi)
    Dim g=图形。从图像(bmp)
    g、 清除(画布。背景色)
    Dim rtfPrinter=新的RichEditPrinter(g)
    尝试
    DrawNestedControls(画布、画布、新矩形(Point.Empty、containerSize)、bmp、rtfPrinter)
    返回bmp
    最后
    rtfPrinter.Dispose()
    g、 处置
    结束尝试
    端函数
    私有共享子DrawNestedControls(outerContainer作为控件,parent作为控件,parentBounds作为矩形,bmp作为位图,rtfPrinter作为RichEditPrinter)
    对于i As Integer=parent.Controls.Count-1到0步骤-1
    Dim ctl=父控件(i)
    如果不是ctl.Visible或LSE(ctl.WITH<1或LSE ctl.Height<1),则继续
    Dim剪贴簿=矩形。空
    如果parent.Equals(outerContainer)则
    剪贴簿=ctl.Bounds
    其他的
    Dim scrContainerSize As Size=parentBounds.Size
    如果父项的类型为ScrollableControl,则
    Dim scrCtrl=DirectCast(父级,可滚动控件)
    与scrCtrl
    如果.VerticalScroll.Visible,则SCRCContainerSize.Width-=(SystemInformation.VerticalScrollBarWidth+1)
    如果.HorizontalScroll.Visible,则SCRCContainerSize.Height-=(SystemInformation.HorizontalScrollBarHeight+1)
    以
    如果结束
    剪贴簿=Rectangle.Intersect(新矩形(Point.Empty,scrContainerSize),ctl.Bounds)
    如果结束
    如果剪贴簿宽度小于1或剪贴簿高度小于1,则继续
    Dim bounds=outerContainer.RectangleToClient(父.RectangleToScreen(剪贴簿))
    如果ctl的类型为RichTextBox,则
    Dim rtb=DirectCast(ctl,RichTextBox)
    rtfPrinter。
    
    ' Prints the content of the current Form instance, 
    ' include all child controls and also those that are not visible
    Dim bitmap = ControlsPrinter.ScrollableControlToBitmap(Me, True, True)
    
    ' Prints the content of a ScrollableControl inside a Form
    ' include all child controls except those that are not visible
    Dim bitmap = ControlsPrinter.ScrollableControlToBitmap(Me.Panel1, True, False)
    
    Imports System.Drawing
    Imports System.Drawing.Imaging
    Imports System.Runtime.InteropServices
    Imports System.Windows.Forms
    
    Public Class ControlPrinter
        Public Shared Function ScrollableControlToBitmap(canvas As ScrollableControl, fullSize As Boolean, includeHidden As Boolean) As Bitmap
            canvas.AutoScrollPosition = New Point(0, 0)
            If includeHidden Then
                canvas.SuspendLayout()
                For Each child As Control In canvas.Controls
                    child.Visible = True
                Next
                canvas.ResumeLayout(True)
            End If
    
            canvas.PerformLayout()
            Dim containerSize As Size = canvas.DisplayRectangle.Size
            If fullSize Then
                containerSize.Width = Math.Max(containerSize.Width, canvas.ClientSize.Width)
                containerSize.Height = Math.Max(containerSize.Height, canvas.ClientSize.Height)
            Else
                containerSize = If((TypeOf canvas Is Form), canvas.PreferredSize, canvas.ClientSize)
            End If
    
            Dim bmp = New Bitmap(containerSize.Width, containerSize.Height, PixelFormat.Format32bppArgb)
            bmp.SetResolution(canvas.DeviceDpi, canvas.DeviceDpi)
    
            Dim g = Graphics.FromImage(bmp)
            g.Clear(canvas.BackColor)
            Dim rtfPrinter = New RichEditPrinter(g)
    
            Try
                DrawNestedControls(canvas, canvas, New Rectangle(Point.Empty, containerSize), bmp, rtfPrinter)
                Return bmp
            Finally
                rtfPrinter.Dispose()
                g.Dispose()
            End Try
        End Function
    
        Private Shared Sub DrawNestedControls(outerContainer As Control, parent As Control, parentBounds As Rectangle, bmp As Bitmap, rtfPrinter As RichEditPrinter)
            For i As Integer = parent.Controls.Count - 1 To 0 Step -1
                Dim ctl = parent.Controls(i)
                If Not ctl.Visible OrElse (ctl.Width < 1 OrElse ctl.Height < 1) Then Continue For
    
                Dim clipBounds = Rectangle.Empty
                If parent.Equals(outerContainer) Then
                    clipBounds = ctl.Bounds
                Else
                    Dim scrContainerSize As Size = parentBounds.Size
                    If TypeOf parent Is ScrollableControl Then
                        Dim scrCtrl = DirectCast(parent, ScrollableControl)
                        With scrCtrl
                            If .VerticalScroll.Visible Then scrContainerSize.Width -= (SystemInformation.VerticalScrollBarWidth + 1)
                            If .HorizontalScroll.Visible Then scrContainerSize.Height -= (SystemInformation.HorizontalScrollBarHeight + 1)
                        End With
                    End If
                    clipBounds = Rectangle.Intersect(New Rectangle(Point.Empty, scrContainerSize), ctl.Bounds)
                End If
                If clipBounds.Width < 1 OrElse clipBounds.Height < 1 Then Continue For
    
                Dim bounds = outerContainer.RectangleToClient(parent.RectangleToScreen(clipBounds))
    
                If TypeOf ctl Is RichTextBox Then
                    Dim rtb = DirectCast(ctl, RichTextBox)
                    rtfPrinter.DrawRtf(rtb.Rtf, outerContainer.Bounds, bounds, ctl.BackColor)
                Else
                    ctl.DrawToBitmap(bmp, bounds)
                End If
    
                If ctl.HasChildren Then
                    DrawNestedControls(outerContainer, ctl, clipBounds, bmp, rtfPrinter)
                End If
            Next
        End Sub
    
        Friend Class RichEditPrinter
            Implements IDisposable
            Private dc As Graphics = Nothing
            Private rtb As RTBPrinter = Nothing
    
            Public Sub New(graphics As Graphics)
                dc = graphics
                rtb = New RTBPrinter() With {
                    .ScrollBars = RichTextBoxScrollBars.None
                }
            End Sub
    
            Public Sub DrawRtf(rtf As String, canvas As Rectangle, layoutArea As Rectangle, color As Color)
                rtb.Rtf = rtf
                rtb.Draw(dc, canvas, layoutArea, color)
                rtb.Clear()
            End Sub
    
            Public Sub Dispose() Implements IDisposable.Dispose
                rtb.Dispose()
            End Sub
    
            Private Class RTBPrinter
                Inherits RichTextBox
                Public Sub Draw(g As Graphics, hdcArea As Rectangle, layoutArea As Rectangle, color As Color)
                    Using brush = New SolidBrush(color)
                        g.FillRectangle(brush, layoutArea)
                    End Using
    
                    Dim hdc As IntPtr = g.GetHdc()
                    Dim canvasAreaTwips = New RECT().ToInches(hdcArea)
                    Dim layoutAreaTwips = New RECT().ToInches(layoutArea)
    
                    Dim formatRange = New FORMATRANGE() With {
                        .charRange = New CHARRANGE() With {
                            .cpMax = -1,
                            .cpMin = 0
                        },
                        .hdc = hdc,
                        .hdcTarget = hdc,
                        .rect = layoutAreaTwips,
                        .rectPage = canvasAreaTwips
                    }
    
                    Dim lParam As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(formatRange))
                    Marshal.StructureToPtr(formatRange, lParam, False)
    
                    SendMessage(Me.Handle, EM_FORMATRANGE, CType(1, IntPtr), lParam)
                    Marshal.FreeCoTaskMem(lParam)
                    g.ReleaseHdc(hdc)
                End Sub
    
                <DllImport("User32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
                Friend Shared Function SendMessage(hWnd As IntPtr, uMsg As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
                End Function
    
                Friend Const WM_USER As Integer = &H400
                Friend Const EM_FORMATRANGE As Integer = WM_USER + 57
    
                <StructLayout(LayoutKind.Sequential)>
                Friend Structure RECT
                    Public Left As Integer
                    Public Top As Integer
                    Public Right As Integer
                    Public Bottom As Integer
    
                    Public Function ToRectangle() As Rectangle
                        Return Rectangle.FromLTRB(Left, Top, Right, Bottom)
                    End Function
    
                    Public Function ToInches(rectangle As Rectangle) As RECT
                        Dim inch As Single = 14.92F
                        Return New RECT() With {
                            .Left = CType(rectangle.Left * inch, Integer),
                            .Top = CType(rectangle.Top * inch, Integer),
                            .Right = CType(rectangle.Right * inch, Integer),
                            .Bottom = CType(rectangle.Bottom * inch, Integer)
                        }
                    End Function
                End Structure
    
                <StructLayout(LayoutKind.Sequential)>
                Friend Structure FORMATRANGE
                    Public hdcTarget As IntPtr      ' A HDC for the target device to format for
                    Public hdc As IntPtr            ' A HDC for the device to render to, if EM_FORMATRANGE is being used to send the output to a device
                    Public rect As RECT             ' The area within the rcPage rectangle to render to. Units are measured in twips.
                    Public rectPage As RECT         ' The entire area of a page on the rendering device. Units are measured in twips.
                    Public charRange As CHARRANGE   ' The range of characters to format (see CHARRANGE)
                End Structure
    
                <StructLayout(LayoutKind.Sequential)>
                Friend Structure CHARRANGE
                    Public cpMin As Integer   ' First character of range (0 for start of doc)
                    Public cpMax As Integer   ' Last character of range (-1 for end of doc)
                End Structure
            End Class
        End Class
    End Class