SelPrint property was not upgraded

In Visual Basic 6.0, the SelPrint method of a RichTextBox control could be used to print the contents of the control. It took a single argument, hDC, which specified the device context of the device.

In Visual Basic 2008, the SelPrint method no longer exists and device contexts are no longer used for printing. The RichTextBox control does not provide a method to print its content. However, you can extend the RichTextBox class to use the EM_FORMATRANGE message. You can then send the content of a RichTextBox to an output device, such as a printer. The two steps to accomplishing this process are:

  1. Create a RichTextBoxPrintCtrl class that inherits from RichTextBox.

  2. Use the RichTextBoxPrintCtrl class in your upgraded project.

To create a class that inherits from the RichTextBox class

  1. Use Visual Basic 2008 to create a new Class Library project named RichTextBoxPrintCtrl.

    By default, Class1.vb is created.

  2. Change the name of the Class1.vb file to RichTextBoxPrintCtrl.vb.

  3. In Solution Explorer, right-click References, and then click Add Reference.

  4. In the Add Reference dialog box, double-click System.Drawing.dll, and then double-click System.Windows.Forms.dll.

  5. To add the references, click OK.

  6. Delete the existing code in RichTextBoxPrintCtrl.vb.

  7. Copy the following code to RichTextBoxPrintCtrl.vb:

    Option Explicit On 
    
    Imports System
    Imports System.Windows.Forms
    Imports System.Drawing
    Imports System.Runtime.InteropServices
    Imports System.Drawing.Printing
    
    Namespace RichTextBoxPrintCtrl
        Public Class RichTextBoxPrintCtrl
            Inherits RichTextBox
    
            ' Convert the unit that is used by the .NET framework 
            ' (1/100 inch) and the unit that is used by Win32 API calls  
            ' (twips 1/1440 inch)
            Private Const AnInch As Double = 14.4
    
            Private WithEvents m_PrintDocument As Printing.PrintDocument
            Private intCharactersToPrint As Integer
            Private intCurrentPosition As Integer
    
            <StructLayout(LayoutKind.Sequential)> _
            Private Structure RECT
                Public Left As Integer
                Public Top As Integer
                Public Right As Integer
                Public Bottom As Integer
            End Structure
    
            <StructLayout(LayoutKind.Sequential)> _
            Private Structure CHARRANGE
                ' First character of range (0 for start of doc)
                Public cpMin As Integer
                ' Last character of range (-1 for end of doc)
                Public cpMax As Integer
            End Structure
    
            <StructLayout(LayoutKind.Sequential)> _
            Private Structure FORMATRANGE
                ' Actual DC to draw on
                Public hdc As IntPtr
                ' Target DC for determining text formatting
                Public hdcTarget As IntPtr
                ' Region of the DC to draw to (in twips)
                Public rc As Rect
                ' Region of the whole DC (page size) (in twips)
                Public rcPage As Rect
                ' Range of text to draw (see above declaration)
                Public chrg As CHARRANGE
            End Structure
    
            Private Const WM_USER As Integer = &H400
            Private Const EM_FORMATRANGE As Integer = WM_USER + 57
    
            Private Declare Function SendMessage Lib "USER32" Alias _
                "SendMessageA" (ByVal hWnd As IntPtr, ByVal msg As Integer, _
                ByVal wp As IntPtr, ByVal lp As IntPtr) As IntPtr
    
            Public Sub SelPrint()
    
                'print only the selected text if any is selected
                If Me.SelectionLength > 0 Then
                    intCharactersToPrint = Me.SelectionStart + Me.SelectionLength
                    intCurrentPosition = Me.SelectionStart
                Else
                    'otherwise print the entire document
                    intCharactersToPrint = Me.TextLength
                    intCurrentPosition = 0
                End If
    
                m_PrintDocument.Print()
    
            End Sub
            ' Render the contents of the RichTextBox for printing
            ' Return the last character printed + 1 (printing start from 
            ' this point for next page)
            Private Function Print(ByVal charFrom As Integer, _
    ByVal charTo As Integer, ByVal e As PrintPageEventArgs) As Integer
    
                ' Mark starting and ending character
                Dim cRange As CHARRANGE
                cRange.cpMin = charFrom
                cRange.cpMax = charTo
    
                ' Calculate the area to render and print
                Dim rectToPrint As RECT
                rectToPrint.Top = e.MarginBounds.Top * AnInch
                rectToPrint.Bottom = e.MarginBounds.Bottom * AnInch
                rectToPrint.Left = e.MarginBounds.Left * AnInch
                rectToPrint.Right = e.MarginBounds.Right * AnInch
    
                ' Calculate the size of the page
                Dim rectPage As RECT
                rectPage.Top = e.PageBounds.Top * AnInch
                rectPage.Bottom = e.PageBounds.Bottom * AnInch
                rectPage.Left = e.PageBounds.Left * AnInch
                rectPage.Right = e.PageBounds.Right * AnInch
    
                Dim hdc As IntPtr = e.Graphics.GetHdc()
    
                Dim fmtRange As FORMATRANGE
                ' Indicate character from to character to
                fmtRange.chrg = cRange
                ' Use the same DC for measuring and rendering
                fmtRange.hdc = hdc
                ' Point at printer hDC
                fmtRange.hdcTarget = hdc
                ' Indicate the area on page to print
                fmtRange.rc = rectToPrint
                ' Indicate whole size of page
                fmtRange.rcPage = rectPage
    
                Dim res As IntPtr = IntPtr.Zero
    
                Dim wparam As IntPtr = IntPtr.Zero
                wparam = New IntPtr(1)
    
                ' Move the pointer to the FORMATRANGE structure in 
                ' memory
                Dim lparam As IntPtr = IntPtr.Zero
                lparam = _
    Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange))
                Marshal.StructureToPtr(fmtRange, lparam, False)
    
                ' Send the rendered data for printing
                res = _
    SendMessage(Handle, EM_FORMATRANGE, wparam, lparam)
    
                ' Free the block of memory allocated
                Marshal.FreeCoTaskMem(lparam)
    
                ' Release the device context handle obtained by a 
                ' previous call
                e.Graphics.ReleaseHdc(hdc)
    
                'return the last + 1 character printed
                Return res.ToInt32
    
            End Function
            Public ReadOnly Property PrintDocument() As Printing.PrintDocument
                Get
                    If m_PrintDocument Is Nothing Then
                        m_PrintDocument = New Printing.PrintDocument
                    End If
    
                    Return m_PrintDocument
                End Get
            End Property
    
            Private Sub m_PrintDocument_PrintPage(ByVal sender As _
    Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
    Handles m_PrintDocument.PrintPage
                ' Print the content of the RichTextBox. 
                ' Store the last character printed.
    
                intCurrentPosition = Me.Print(intCurrentPosition, _
    intCharactersToPrint, e)
    
                ' Look for more pages by checking 
                If intCurrentPosition < intCharactersToPrint Then
                    e.HasMorePages = True
                Else
                    e.HasMorePages = False
                End If
    
            End Sub
        End Class
    End Namespace
    
  8. To create RichTextBoxPrintCtrl.dll, click Build Solution on the Build menu.

To use the RichTextBoxPrintCtrl class in your upgraded project

  1. Open your upgraded Windows Application project in Visual Basic 2008.

  2. From the Toolbox, drag a PrintDialog component to the form that contains the RichTextBox control.

  3. On the Tools menu, click Customize Toolbox.

  4. Click .NET Framework Components, click Browse, click to select RichTextBoxPrintCtrl.dll, and then click OK.

  5. From the Toolbox, drag a RichTextBoxPrintCtrl onto the form.

  6. Set the properties of the RichTextBoxPrintCtrl to match the properties of your original RichTextBox control.

  7. Delete the RichTextBox control, and then rename the RichTextBoxPrintCtrl control to the name of your original RichTextBox control.

  8. Double-click the form to open the code editor.

  9. Add the following code to the Button_Click event handler (or wherever you called SelPrint in the original code):

    ' NOTE: Replace "Button1" with the name of your actual Print button
    Private Sub Button1_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    
        'associate the correct printdocument to the printdialog
        PrintDialog1.Document = Me.RichTextBoxPrintCtrl1.PrintDocument
    
        If PrintDialog1.ShowDialog() = DialogResult.OK Then
            Me.RichTextBoxPrintCtrl1.SelPrint()
        End If
    End Sub 
    

See Also

Reference

RichTextBox Control Overview (Windows Forms)

EM_FORMATRANGE Message