4

I am having a terrible time trying to allow the user to set the margins for a report. Right now I have it hard coded but I would prefer to set them at run time so they can be changed. I tried this:

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    Me.OriginAtMargins = True
    Me.DefaultPageSettings.Margins = New Margins(25, 25, 25, 25)

End Sub

To print a 1/4" margin which is well beyond the printer's hard margins. Nothing prints. According to Margins the arguments are in hundreds of an inch. If I change the code to:

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    Me.OriginAtMargins = True
    Me.DefaultPageSettings.Margins = New Margins(20, 20, 23, 23)

End Sub

I do get good output: Good output

But when I change the code to:

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    Me.OriginAtMargins = True
    Me.DefaultPageSettings.Margins = New Margins(21, 20, 23, 23)

End Sub

This is what I get, which makes no sense. It looks like it shifts over 1 inch: Bad output

And I am not the only one having problems with margins, there is no response here and I even posted to microsoft, with no response.

Since report writers can produce the majority of documents I will presume not many people use the PrintDocument object. So I am seeing what anyone here has to say before I report a bug to Microsoft, which will take a considerable time for a response to come from then.

EDIT: I made a simpler version and got similar but still strange results. I had to leave a lot of the code in the class, class definitions, properties, ect. that the caller requires. I just removed (almost) all the code from the events.

Here is the event code:

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    Me.OriginAtMargins = True
    Me.DefaultPageSettings.Margins = New Margins(19, 20, 23, 23)
    mintLevel = 0
    'Reset the loop trackers (used when we need to break out to print a page but remember our place) and others.
    mintRowLoopStart = 0
    mintTableLoopStart = 0
    'Flags
    ResetHeaderFooterFlags()
    'Page counters
    mintCurrentPage = 0
    mintTotalPages = 0

End Sub

Private Sub rotHCReport_QueryPageSettings(ByVal sender As Object, _
     ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles Me.QueryPageSettings

    e.PageSettings.Landscape = mePageOrientation = Orientation.Landscape
    mintCurrentPage += 1

End Sub

Private Sub rotHCReport_PrintPage(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage


    Dim DR As DataRow
    Dim PrintFont As Font


    e.Graphics.PageUnit = GraphicsUnit.Inch
    PrintFont = New Font("Arial", 8, FontStyle.Regular)

    For intRow = 0 To 10

        DR = mDS.Tables(0).Rows(intRow)

        For intColumn = 0 To 5

            e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), intColumn + 1, CSng(intRow + 1))

        Next
    Next

End Sub

Private Sub rotHCReport_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.EndPrint

    mbSetUpRequired = True

End Sub

Which produces (looks less than 1/8 of an inch): Less

And changing:

Me.DefaultPageSettings.Margins = New Margins(20, 20, 23, 23)

Produces (looks more than 1/4 inch): enter image description here

EDIT2: I tried TnTinMn's technique and I had a difficult time understanding the TranslateTransform method. From my testing it almost sounds like it "adds" to whatever margin you have. Worse, if I use 0 I get a larger margin than what I have set, OriginAtMargins makes no difference at this point. What I am trying to do is create a consistent margin across printers.

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    'Me.OriginAtMargins = True
    Me.DefaultPageSettings.Margins = New Margins(25, 25, 25, 25)
    'Subreport level
    mintLevel = 0
    'Reset the loop trackers (used when we need to break out to print a page but remember our place) and others.
    mintRowLoopStart = 0
    mintTableLoopStart = 0
    'Flags
    ResetHeaderFooterFlags()
    'Page counters
    mintCurrentPage = 0
    mintTotalPages = 0

End Sub

Private Sub rotHCReport_QueryPageSettings(ByVal sender As Object, _
     ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles Me.QueryPageSettings

    e.PageSettings.Landscape = mePageOrientation = Orientation.Landscape
    mintCurrentPage += 1

End Sub


Private Sub rotHCReport_PrintPage(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage


    Dim DR As DataRow
    Dim PrintFont As Font


    e.Graphics.PageUnit = GraphicsUnit.Inch
    e.Graphics.ResetTransform()
    e.Graphics.TranslateTransform(e.MarginBounds.X / 100.0F, e.MarginBounds.Y / 100.0F)
    PrintFont = New Font("Arial", 8, FontStyle.Regular)

    For intRow = 0 To 10

        DR = mDS.Tables(0).Rows(intRow)

        For intColumn = 0 To 5

            e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), CSng(intColumn + 1), CSng(intRow + 1))

        Next
    Next

End Sub

Private Sub rotHCReport_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.EndPrint

    mbSetUpRequired = True

End Sub

Which produces: enter image description here

I can change the TranslateTransform call to this:

e.Graphics.TranslateTransform(-e.MarginBounds.X / 100.0F, -e.MarginBounds.Y / 100.0F)

Which produces this, better, but I already have a hack job working. I am trying to figure out how to do this right.

enter image description here

It seems a great mystery on how to use the PrintDocument properly.

TnTinMn is using the PrintController while I am using the PrintDocument. I tried incorporating his proposed change and it did not work at all. Any idea how to get these two object to work together?

Public Class NewController
    Inherits PrintController

    Public Overrides Function OnStartPage(ByVal document As System.Drawing.Printing.PrintDocument, ByVal e As System.Drawing.Printing.PrintPageEventArgs) As System.Drawing.Graphics

        Dim g As System.Drawing.Graphics


        g = MyBase.OnStartPage(document, e)
        g.PageUnit = GraphicsUnit.Inch
        g.ResetTransform()
        g.TranslateTransform((e.MarginBounds.X - e.PageSettings.HardMarginX) / 100.0F, (e.MarginBounds.Y - e.PageSettings.HardMarginY) / 100.0F)

        'This does not work either
        'e.Graphics.PageUnit = GraphicsUnit.Inch
        'e.Graphics.ResetTransform()
        'e.Graphics.TranslateTransform((e.MarginBounds.X - e.PageSettings.HardMarginX) / 100.0F, (e.MarginBounds.Y - e.PageSettings.HardMarginY) / 100.0F)

        'Return e.Graphics
        Return g

    End Function
End Class

And I changed the other class.

Private mPC As NewController


Public Sub New()

    MyBase.New()
    mPC = New NewController
    Me.PrintController = mPC

End Sub

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    Me.DefaultPageSettings.Margins = New Margins(25, 25, 25, 25)
    'Subreport level
    mintLevel = 0
    'Reset the loop trackers (used when we need to break out to print a page but remember our place) and others.
    mintRowLoopStart = 0
    mintTableLoopStart = 0
    'Flags
    ResetHeaderFooterFlags()
    'Page counters
    mintCurrentPage = 0
    mintTotalPages = 0

End Sub

'Raised before each and every page
Private Sub rotHCReport_QueryPageSettings(ByVal sender As Object, _
     ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles Me.QueryPageSettings

    e.PageSettings.Landscape = mePageOrientation = Orientation.Landscape
    mintCurrentPage += 1

End Sub


Private Sub rotHCReport_PrintPage(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage


    Dim DR As DataRow
    Dim PrintFont As Font


    e.Graphics.PageUnit = GraphicsUnit.Inch
    PrintFont = New Font("Arial", 8, FontStyle.Regular)

    For intRow = 0 To 10

        DR = mDS.Tables(0).Rows(intRow)

        For intColumn = 0 To 5

            e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), CSng(intColumn + 1), CSng(intRow + 1))
            'millimetersd
            'e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), CSng((intColumn + 1) * 25.4), CSng((intRow + 1) * 25.4))

        Next
    Next

End Sub

Private Sub rotHCReport_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.EndPrint

    mbSetUpRequired = True

End Sub

Same results.

EDIT FINAL: TnTinMn's solution worked perfectly. I think I made an error somewhere copying the code between here, my test dll and production dll. In any event, I went over everything again, albeit a little more carefully and it worked. Here is the code that worked in test (and the same technique worked in production)

Private Sub rotHCReport_BeginPrint(ByVal sender As Object, _
      ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.BeginPrint

    'Me.OriginAtMargins = True 'Don't need this anymore
    Me.DefaultPageSettings.Margins = New Margins(25, 25, 25, 25) 'These are the margins actually used
    'Subreport level
    mintLevel = 0
    'Reset the loop trackers (used when we need to break out to print a page but remember our place) and others.
    mintRowLoopStart = 0
    mintTableLoopStart = 0
    'Flags
    ResetHeaderFooterFlags()
    'Page counters
    mintCurrentPage = 0
    mintTotalPages = 0

End Sub

'Raised before each and every page
Private Sub rotHCReport_QueryPageSettings(ByVal sender As Object, _
     ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles Me.QueryPageSettings

    e.PageSettings.Landscape = mePageOrientation = Orientation.Landscape
    mintCurrentPage += 1

End Sub

Private Sub rotHCReport_PrintPage(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage

    Dim DR As DataRow
    Dim PrintFont As Font


    e.Graphics.PageUnit = GraphicsUnit.Inch
    e.Graphics.ResetTransform()
    e.Graphics.TranslateTransform((e.MarginBounds.X - e.PageSettings.HardMarginX) / 100.0F, (e.MarginBounds.Y - e.PageSettings.HardMarginY) / 100.0F)
    PrintFont = New Font("Arial", 8, FontStyle.Regular)

    For intRow = 0 To 10

        DR = mDS.Tables(0).Rows(intRow)

        For intColumn = 0 To 5

            e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), CSng(0.25 + intColumn), CSng(0.25 + intRow))

        Next
    Next

End Sub

Private Sub rotHCReport_EndPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles Me.EndPrint

    mbSetUpRequired = True

End Sub

I set 1/4" margins and I got 1/4" margins! Here's my 7th screenshot, lucky #7.

enter image description here

John
  • 65
  • 1
  • 8
  • I can't reproduce your "wide margin" issue with your settings. Maybe it's your print code. Try posting a simple PrintPage example that reproduces the problem. – LarsTech Sep 08 '17 at 14:57
  • Is that the output you are seeing on a hard-printed page or what you see in the Print Preview screen? – TnTinMn Sep 08 '17 at 15:19
  • @TnTinMn, screenshot from preview – John Sep 08 '17 at 17:06
  • @TnTinMn - And printed output is identical as well. Gonna test a smaller version for Lars. – John Sep 08 '17 at 17:37
  • @LarsTech - I simplified it and it did not seem to help. I will see if a new measurement other than inches causes the same issue – John Sep 08 '17 at 18:11
  • I used millimeters and received sensible results. I think Inches does not work. – John Sep 08 '17 at 18:38
  • My guess is OriginAtMargins is messing things up. The default is false. – LarsTech Sep 08 '17 at 19:46
  • @LarsTech - OriginAtMargins is required otherwise the PrintDocument class will use the origin at the printer's hard margins and this will produce a report that looks different on different printers, an undesirable effect. – John Sep 11 '17 at 15:21
  • See [How to find the actual printable area? (PrintDocument)](https://stackoverflow.com/q/8761633/719186) – LarsTech Sep 11 '17 at 15:55
  • @LarsTech - Yes, that is what I used to build my code, i.e. that is why I have OriginAtMargins set to true. – John Sep 11 '17 at 17:15

1 Answers1

4

In your PrintPage handler, you have the statement e.Graphics.PageUnit = GraphicsUnit.Inch and are thus causing the problem. The received e.Graphics.PageUnit is set to Display and the Graphics has a coordinate transform applied to it that reflects your OriginAtMargins = True setting. The transforms' OffsetX and OffsetY values were computed using the Display PageUnit setting (in 1/100 inch). When you change the PageUnit to Inch those offsets are now interpreted as Inches and whatever you are drawing on the Graphics is drawn out of bounds. This is why you get an empty page.

You should be able to eliminate the OriginAtMargins = True statement and modify the PrintPage handler code like this:

e.Graphics.PageUnit = GraphicsUnit.Inch
e.Graphics.ResetTransform() ' clear any previous transforms
e.Graphics.TranslateTransform((e.MarginBounds.X - e.PageSettings.HardMarginX) / 100.0F, (e.MarginBounds.X - e.PageSettings.HardMarginY) / 100.0F)

Edit: I corrected the TranslateTransform arguments to account for the printer's hard margin values that I originally neglected. This is based on the transform performed by the StandardPrintController.OnStartPage method when then PrintDocument.OriginAtMargins property is true.

TnTinMn
  • 11,522
  • 3
  • 18
  • 39
  • Tried this and I get huge 1" margins. This will cause the report to be cut off. The whole point of changing PageUnit from Display and using OriginAtMargins is to ensure the report looks the same across printers. I added a negative sign in the TranslateTransform method and the margin shrunk. But I already have a hacked method that uses "magic numbers" to work. I'm sure I can mess around with this approach to find the magic number that makes it work but I am hoping to find a clean solution. Any ideas? – John Sep 11 '17 at 15:19
  • @John, The original code neglected to account for the hard margins. The code has been updated. There should be no need for _magic numbers_. The code assumes that you are configuring the Margins using the 1/100th inch units that are specified in the standard documentation. – TnTinMn Sep 12 '17 at 03:19
  • Tried your change and it did not make a difference. I am looking at what else may be in the code that may be affecting it. – John Sep 12 '17 at 18:42
  • Thanks for providing the link, now I see why its not working. The code excerpt you provided is overriding the PrintController. I am overriding the PrintDocument. Any idea how to combine these two approaches? – John Sep 12 '17 at 19:13
  • @John, Yes I tested the code. I suggest that you look at your call to the `DrawString` method: `e.Graphics.DrawString(DR(intColumn).ToString, PrintFont, New SolidBrush(Color.Black), CSng(intColumn + 1), CSng(intRow + 1)`. The first column is placed 1-inch from the margin. – TnTinMn Sep 12 '17 at 19:13
  • My mistake. You solution works well. It worked in the test code and then when I copied it over to the production code it worked flawlessly. I didn't even meed to mess with the PrintController. Thanks you! – John Sep 14 '17 at 14:29