Question [resolved] Printing text centralized to the width of the page

Use the Graphics.MeasureString method to find the width of the text, subtract that from the available page width (e.MarginBounds.Width). Divide the result by 2 and you got the X location to draw the string in order for it to be centered on page.
 
Hi,

I tried the next code and it is not printing in the center.
Can you tell me whats wrong ?

VB.NET:
    Private Sub PrintDocument2_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument2.PrintPage

        Dim MyText As String = "Hello World"
        Dim MyFont As New Font("miriam fixed", 11, FontStyle.Regular)
        Dim YLocation As Integer = 100

        'Dim TextLength As Integer = Me.CreateGraphics.MeasureString(MyText, MyFont).Width
        Dim TextLength As Integer = CreateGraphics.MeasureString(MyText, MyFont).Width
        Dim MarginBoundsWidth As Integer = e.MarginBounds.Width - TextLength
        Dim XLocation As Integer = MarginBoundsWidth / 2

        e.Graphics.DrawString(MyText, MyFont, Brushes.Black, XLocation, YLocation)

    End Sub
 
Why are you creating Graphics? You already have Graphics. e.Graphics.

You also should turn Option Strict to better learn data types. In this case you will see you are mixing Single and Integer. For better accuracy operate with Single here.

Apart from that it's a good try, now for the final fix ;) which I forgot to mention, the page has margins around it, top, bottom, left and right. The calculation is correct to place the text centered within the marginbounds rectangle, but drawing is relative to full page and you have to add the left margin to the "xlocation", adding e.MarginBounds.Left to it. If you swapped e.MarginBounds with e.PageBounds the calculation would be all correct, but since you always draw within given MarginBounds that is what you should always relate to. Here is a full sample:
VB.NET:
Dim text As String = "hello center"
Dim MyFont As New Font("miriam fixed", 11, FontStyle.Regular)
Dim textWidth As Single = e.Graphics.MeasureString(text, MyFont).Width
Dim availableWidth As Integer = e.MarginBounds.Width
Dim centeredX As Single = (availableWidth - textWidth) / 2
centeredX += e.MarginBounds.Left
e.Graphics.DrawString(text, MyFont, Brushes.Red, centeredX, e.MarginBounds.Y)
MyFont.Dispose()
Another thing you're forgetting is to Dispose objects. If a class you're creating an instance of has a Dispose method then use it when you're done. System.Drawing classes are particularly vulnerable to this because they often take much resources, are often called frequently like in a Paint event, and often contains unmanaged handles that have worse impact on operating system when not released correctly. Not releasing such resources can quickly lead to the app calling the quits with a out-of-memory error. In your code I already complained about you creating a new Graphics instance, had you needed this you would also needed to Dispose it. As you can see in the code sample the new Font object created also need to be disposed. The other variables are values and don't need cleanup. A Using block can be used instead of explicit call to Dispose, but can also make code less readable if there are many levels. (Using also includes a hidden Try-Catch that not be desired.)

Here is another way you can do it, with several more benefits added. What if the given text is wider than the available width? Firstly, the calculation would offset outside margins and most likely also outside page. Secondly, drawing text to a Point will not make it wrap, it will just keep on until end of page, while drawing to a rectangle will make it wrap. What we need to know here to define a rectangle is the height of the text if it should wrap given the limitation of the margins. This height is easily calculated with MeasureString, give it a width limit and it returns a size (width and height) for all lines the text would span. The DrawString method is also very co-operative, give it a Rectangle and it wraps the text, give it a basic StringFormat instruction and it center aligns it. The rectangle also has a location (Point), so here we don't need to calculate and offset a particular X in relation to margins. Compare this sample that doesn't differ much, is simpler, and solves all the mentioned problems:
VB.NET:
Dim text As String = "hello center hello center hello center hello center hello center hello center"
text &= " hello center hello center hello center hello center hello center hello center hello center"
Dim MyFont As New Font("miriam fixed", 11, FontStyle.Regular)
Dim textSize As SizeF = e.Graphics.MeasureString(text, MyFont, e.MarginBounds.Width)      
Dim format As New StringFormat
format.Alignment = StringAlignment.Center
Dim r As RectangleF = e.MarginBounds
r.Height = textSize.Height
e.Graphics.DrawString(text, MyFont, Brushes.Red, r, format)
MyFont.Dispose()
format.Dispose()
 
Back
Top