Question Printing the contents of a Rich Text Box

JamH

New member
Joined
Nov 9, 2024
Messages
3
Programming Experience
5-10
Although I have written a few applications and have gotten paid for a couple of them, programming is for the most part just a hobby. I am trying to make an application
that I wrote for my employer a little more functional. The trouble I am having is printing the text from a Rich Text Box in which the use can change the font. For example,
when making an address label I wanted to make it so the return address can be smaller than the to address. The text box works perfect and does what it is supposed to do
but when you print the label, it prints out all the same size. That is the most important part. As a side note, the user can place the text anywhere they want to using the enter
key and the spacebar, but for some reason using the tab key messes up the printout. Just to clarify, if you do not use the tab key, the label will print exactly as shown.
I hope someone can help with this and thank you in advance. I am using VB in Visual Studio 2022 and I hope this is the correct forum?
 
Here's a solution and usage for you. I tested it and its functional.

VB.NET:
Public Class RichTextBoxEx
    Inherits RichTextBox

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_RECT
        Public left As Int32
        Public top As Int32
        Public right As Int32
        Public bottom As Int32
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_CHARRANGE
        Public cpMin As Int32
        Public cpMax As Int32
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_FORMATRANGE
        Public hdc As IntPtr
        Public hdcTarget As IntPtr
        Public rc As STRUCT_RECT
        Public rcPage As STRUCT_RECT
        Public chrg As STRUCT_CHARRANGE
    End Structure

    <DllImport("user32.dll")>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, ByVal wParam As Int32, ByVal lParam As IntPtr) As Int32
    Private Const WM_USER As Int32 = &H400
    Private Const EM_FORMATRANGE As Int32 = WM_USER + 57
    Private Const EM_GETCHARFORMAT As Int32 = WM_USER + 58
    Private Const EM_SETCHARFORMAT As Int32 = WM_USER + 68

    Public Function FormatRange(ByVal measureOnly As Boolean, ByVal e As PrintPageEventArgs, ByVal charFrom As Integer, ByVal charTo As Integer) As Integer
        Dim cr As STRUCT_CHARRANGE = Nothing
        cr.cpMin = charFrom
        cr.cpMax = charTo
        Dim rc As STRUCT_RECT = Nothing
        rc.top = HundredthInchToTwips(e.MarginBounds.Top)
        rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom)
        rc.left = HundredthInchToTwips(e.MarginBounds.Left)
        rc.right = HundredthInchToTwips(e.MarginBounds.Right)
        Dim rcPage As STRUCT_RECT = Nothing
        rcPage.top = HundredthInchToTwips(e.PageBounds.Top)
        rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom)
        rcPage.left = HundredthInchToTwips(e.PageBounds.Left)
        rcPage.right = HundredthInchToTwips(e.PageBounds.Right)
        Dim hdc As IntPtr = Nothing
        hdc = e.Graphics.GetHdc()
        Dim fr As STRUCT_FORMATRANGE = Nothing
        fr.chrg = cr
        fr.hdc = hdc
        fr.hdcTarget = hdc
        fr.rc = rc
        fr.rcPage = rcPage
        Dim wParam As Int32 = Nothing

        If measureOnly Then
            wParam = 0
        Else
            wParam = 1
        End If

        Dim lParam As IntPtr = Nothing
        lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr))
        Marshal.StructureToPtr(fr, lParam, False)
        Dim res As Integer = 0
        res = SendMessage(Handle, EM_FORMATRANGE, wParam, lParam)
        Marshal.FreeCoTaskMem(lParam)
        e.Graphics.ReleaseHdc(hdc)
        Return res
    End Function

    Private Function HundredthInchToTwips(ByVal n As Integer) As Int32
        Return Convert.ToInt32(n * 14.4)
    End Function

    Public Sub FormatRangeDone()
        Dim lParam As IntPtr = New IntPtr(0)
        SendMessage(Handle, EM_FORMATRANGE, 0, lParam)
    End Sub
End Class
 
USAGE: FOR PRINTING
Class SurroundingClass
    Private Sub printDocument1_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
        firstCharOnPage = 0
    End Sub

    Private Sub printDocument1_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
        richTextBoxEx1.FormatRangeDone()
    End Sub

    Private Sub printDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
        firstCharOnPage = richTextBoxEx1.FormatRange(False, e, firstCharOnPage, richTextBoxEx1.TextLength)

        If firstCharOnPage < richTextBoxEx1.TextLength Then
            e.HasMorePages = True
        Else
            e.HasMorePages = False
        End If
    End Sub

    Private Sub printToolStripButton_Click(ByVal sender As Object, ByVal e As EventArgs)
        printDocument1.Print()
    End Sub
End Class
 
Last edited by a moderator:
Ok, thanks for your help with this. I think I need a little more. I created a new project with just a Rich Text Box which I just left the name at RichTextBox. I assume this is correct for the Public Class
RichTextBoxEx Inherits RichTextBox?
Also I used a button for the PrintDocument1.Print() portion. And included the PrintDocument in the project. I am guessing that I am not very clear on the Class. I put this code inside of the Public Class
Form1, is this not where it should go? I made the assumption also that the Private Shared Function SendMessage should have a End Function before the Public Function FormatRange? and not a nested function? Thanks again,
 
I'm going to assume Windows Forms and move this thread accordingly. As for the question check out this old but relevant solution from Microsoft:


Here's a solution and usage for you. I tested it and its functional.

VB.NET:
Public Class RichTextBoxEx
    Inherits RichTextBox

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_RECT
        Public left As Int32
        Public top As Int32
        Public right As Int32
        Public bottom As Int32
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_CHARRANGE
        Public cpMin As Int32
        Public cpMax As Int32
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Private Structure STRUCT_FORMATRANGE
        Public hdc As IntPtr
        Public hdcTarget As IntPtr
        Public rc As STRUCT_RECT
        Public rcPage As STRUCT_RECT
        Public chrg As STRUCT_CHARRANGE
    End Structure

    <DllImport("user32.dll")>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, ByVal wParam As Int32, ByVal lParam As IntPtr) As Int32
    Private Const WM_USER As Int32 = &H400
    Private Const EM_FORMATRANGE As Int32 = WM_USER + 57
    Private Const EM_GETCHARFORMAT As Int32 = WM_USER + 58
    Private Const EM_SETCHARFORMAT As Int32 = WM_USER + 68

    Public Function FormatRange(ByVal measureOnly As Boolean, ByVal e As PrintPageEventArgs, ByVal charFrom As Integer, ByVal charTo As Integer) As Integer
        Dim cr As STRUCT_CHARRANGE = Nothing
        cr.cpMin = charFrom
        cr.cpMax = charTo
        Dim rc As STRUCT_RECT = Nothing
        rc.top = HundredthInchToTwips(e.MarginBounds.Top)
        rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom)
        rc.left = HundredthInchToTwips(e.MarginBounds.Left)
        rc.right = HundredthInchToTwips(e.MarginBounds.Right)
        Dim rcPage As STRUCT_RECT = Nothing
        rcPage.top = HundredthInchToTwips(e.PageBounds.Top)
        rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom)
        rcPage.left = HundredthInchToTwips(e.PageBounds.Left)
        rcPage.right = HundredthInchToTwips(e.PageBounds.Right)
        Dim hdc As IntPtr = Nothing
        hdc = e.Graphics.GetHdc()
        Dim fr As STRUCT_FORMATRANGE = Nothing
        fr.chrg = cr
        fr.hdc = hdc
        fr.hdcTarget = hdc
        fr.rc = rc
        fr.rcPage = rcPage
        Dim wParam As Int32 = Nothing

        If measureOnly Then
            wParam = 0
        Else
            wParam = 1
        End If

        Dim lParam As IntPtr = Nothing
        lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr))
        Marshal.StructureToPtr(fr, lParam, False)
        Dim res As Integer = 0
        res = SendMessage(Handle, EM_FORMATRANGE, wParam, lParam)
        Marshal.FreeCoTaskMem(lParam)
        e.Graphics.ReleaseHdc(hdc)
        Return res
    End Function

    Private Function HundredthInchToTwips(ByVal n As Integer) As Int32
        Return Convert.ToInt32(n * 14.4)
    End Function

    Public Sub FormatRangeDone()
        Dim lParam As IntPtr = New IntPtr(0)
        SendMessage(Handle, EM_FORMATRANGE, 0, lParam)
    End Sub
End Class
 
USAGE: FOR PRINTING
Class SurroundingClass
    Private Sub printDocument1_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
        firstCharOnPage = 0
    End Sub

    Private Sub printDocument1_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
        richTextBoxEx1.FormatRangeDone()
    End Sub

    Private Sub printDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs)
        firstCharOnPage = richTextBoxEx1.FormatRange(False, e, firstCharOnPage, richTextBoxEx1.TextLength)

        If firstCharOnPage < richTextBoxEx1.TextLength Then
            e.HasMorePages = True
        Else
            e.HasMorePages = False
        End If
    End Sub

    Private Sub printToolStripButton_Click(ByVal sender As Object, ByVal e As EventArgs)
        printDocument1.Print()
    End Sub
End Class

Ok, in the private shared function SendMessage is there something missing? I get an error for all the private Constants. If I take out the
private function line those errors go away. Also when the function is called, there is nothing set for the first parameter "handle" I have been racking my brain and moving things around to see if something is just in the wrong place and no luck. I am guessing that something is missing, but I can not figure it out. I know I said this in my other response but, there is no End Function which is why I was wondering if something is missing. Any additional help you could give would be greatly appreciated. Thanks again for the help.
 
Back
Top