Click here to Skip to main content
14,931,113 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm hoping here is a simple answer that my lack of programming skills are hiding from me... I generate reports that I'd like to immediately display on a FORM for the Users review and acceptance. Once accepted this can be PRINTED. The "on-form" report is a subset of the full report that is included in the printed document (includes logo's, border graphics and more detail text, etc), so most of the generation of the document is the same, but not all. Rather than duplicate all the report code twice, once for the form and again for printing I hope there is a way to generate a common subroutine that both the form report and printing routines can call and that contains the conditional formatting lines.

For writing to the Form:

Module DisplayCode

  Sub WriteOnFrom()
    ' writes to form 

    With frmOutput
      .Visible = True
      .WindowState = FormWindowState.Normal
      .PrintPreviewControl.Visible = False
      Dim drawPen As Pen
      Dim drawBrush As SolidBrush
      Dim drawFont As Font
      Dim drawObject As Graphics = .CreateGraphics()  'Create object for the page
      
      Dim drawString As String = "Sample Text"

      drawObject.Clear(Color.White)   'clears the current screen

      drawFont = New Font("Arial", 16, FontStyle.Bold)
      drawBrush = New SolidBrush(Color.BlueViolet)
      drawObject.DrawString("text in a box", drawFont, drawBrush, 150, 50)
      drawFont = New Font("Calibri", 20, FontStyle.Italic)
      drawBrush = New SolidBrush(Color.Red)
      drawObject.DrawString("text above a line", drawFont, drawBrush, 150, 290)
      drawPen = New Pen(Color.Black, 3)
      drawObject.DrawRectangle(drawPen, 120, 50, 200, 25)
      drawPen = New Pen(Color.Purple, 5)
      drawObject.DrawLine(drawPen, 150, 320, 350, 320)

      drawFont.Dispose()
      drawBrush.Dispose()
      drawObject.Dispose()
    End With

  End Sub

End Module


For the PrintDocument:

Public Class PrintOutput
  Inherits System.Drawing.Printing.PrintDocument

Private Sub PrintOutput_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage
    'Prepares printed document

    Dim drawPen As Pen
    Dim drawBrush As SolidBrush
    Dim drawFont As Font
    Dim drawString As String = "Sample Text"

    'Can I move this to a subroutine outside the Class?
    drawFont = New Font("Arial", 16, FontStyle.Bold)
    drawBrush = New SolidBrush(Color.BlueViolet)
    e.Graphics.DrawString("text in a box", drawFont, drawBrush, 150, 50)
    drawFont = New Font("Calibri", 20, FontStyle.Italic)
    drawBrush = New SolidBrush(Color.Red)
    e.Graphics.DrawString("text above a line", drawFont, drawBrush, 150, 290)
    drawPen = New Pen(Color.Black, 3)
    e.Graphics.DrawRectangle(drawPen, 120, 50, 200, 25)
    drawPen = New Pen(Color.Purple, 5)
    e.Graphics.DrawLine(drawPen, 150, 320, 350, 320)

    e.HasMorePages = False

    drawFont.Dispose()
    drawBrush.Dispose()
    drawPen.Dispose()

  End Sub
End Class

Of course, this the same report for both for now, so just an example to demo the principles.

What I have tried:

Of course its possible to use the PrintPreview control on my frmOutput and develop this all as a PrintDocument object but I'd rather not. Firstly, it takes a while to generate this (on-form is instantaneous), secondly I'm not interested in the paper size format etc and just fit it onto a fixed size frmOutput and finally, I still don't know how to generate a subset of this report for the PrintPreview control relative to the full report for printing.

BTW for the form report it appears I still have to use .CreateGraphics() to write to the form itself, which is a conflict to simply using e.Graphics. for PrintDocument. I'm not especially committed to writing directly to the form, so any control placed on the form that accepts text & graphis will do.

Any great suggestions?
Posted
Updated 5-Dec-18 1:06am
Comments
Richard MacCutchan 5-Dec-18 4:55am
   
My only suggestion would be to stop making things so difficult for yourself, and use the Print and PrinPreview features that are built into .NET, for the specific purpose of making life easier.
IanSp 5-Dec-18 9:44am
   
Thank you for your reply. In the interests of keeping this question simple I didn't list all the reasons why I'd prefer to not use the PrintPreview control on my form, but I'm not excluding it for now. However, if I was to use it, how to I pass a conditional to the PrintPage procedure so I can print the full output to the printer and only parts of it (subset) to the PrintPreview control on form, I can't seem to find a way to pass a flag to the PrintPage subroutine. Your insight would be very helpful.

1 solution

It's pretty simple - in one piece of code you get your Graphics object from Form.CreateGraphics:
Dim drawObject As Graphics = .CreateGraphics()  'Create object for the page
In the other, you use the supplied context from the PrintDocument:
e.Graphics.DrawString("text in a box", drawFont, drawBrush, 150, 50)

If you write your "combined" method to accept a Graphics object as a parameter and draw to that, you can pass the context from your WriteOnFrom method, or your event handler and it will do the same thing to different outputs.

But ... your WriteOnFrom method shouldn't be creating the context anyway - you should be handling the Paint event and using the supplied Graphics context there. If you don't, then you have to manually find out that the form has been re-displayed and re-draw it yourself. Or when it goes behind another app, or gets minimized and restored you won't get to see your data any more.
   
Comments
IanSp 5-Dec-18 10:26am
   
Perfect, thank you! I just needed to know that this was doable and your reply gave me enough to work out the details myself. I created a common GenerateReport() subroutine passing the graphics object and a flag as paramaters:
 
Sub GenerateReport(graphicsTarget As Object, OnForm As Boolean)

    Dim drawPen As Pen
    Dim drawBrush As SolidBrush
    Dim drawFont As Font
    'Dim drawObject As Graphics = graphicsTarget.CreateGraphics()      ' Create the graphics object for the page
    Dim drawString As String = "Sample Text"

    graphicsTarget.Clear(Color.White)   'clears the current screen

    If OnForm Then
      drawFont = New Font("Arial", 16, FontStyle.Bold)
      drawBrush = New SolidBrush(Color.BlueViolet)
      graphicsTarget.DrawString("text in a box", drawFont, drawBrush, 150, 50)
      drawPen = New Pen(Color.Black, 3)
      graphicsTarget.DrawRectangle(drawPen, 120, 50, 200, 25)
    End If
    drawPen = New Pen(Color.Purple, 5)
    drawFont = New Font("Calibri", 20, FontStyle.Italic)
    drawBrush = New SolidBrush(Color.Red)
    graphicsTarget.DrawString("text above a line", drawFont, drawBrush, 150, 290)
    graphicsTarget.DrawLine(drawPen, 150, 320, 350, 320)

    drawFont.Dispose()
    drawBrush.Dispose()
    'drawObject.Dispose()

  End Sub
 

And then called from each as follows:
 Sub WriteOnFrom()
    ' writes to form 

    Dim OnForm As Boolean = True
    With frmOutput
      .Visible = True
      .WindowState = FormWindowState.Normal
      .PrintPreviewControl.Visible = False
      GenerateReport(frmOutput.CreateGraphics, OnForm)
    End With
  End Sub

  Private Sub PrintOutput_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles Me.PrintPage
    'Prepares printed document

    Dim OnForm As Boolean = False
    GenerateReport(e.Graphics, OnForm)
    'e.HasMorePages = False

  End Sub


I did notice in my development project the issue with the graphics being lost as you mention, but it does not occur in this example (not sure why). I solved it in my project by simply calling GenerateReport() every time a Paint event is triggered. Your comments make me think I can improve this. I shall try and figure this out myself but if you can point me in the best direction I would surely appreciate it.
OriginalGriff 5-Dec-18 10:36am
   
Don't forget to Dispose your drawPen as well, or you'll "Leak" handles for each Paint event.
I'd suggest that since the Pen, Font, and Brush are all effectively static, you declare them as class level Shared items (one set each for the form and non-form) so you don't have to create and dispose them at all in your event handler. Not only does that make your Paint event quicker - always a good idea - but it doesn't risk resources not being freed.
IanSp 5-Dec-18 11:06am
   
Your last point is now captured, thanks. I'm a long time VB6 & VBA programmer. I only program to make my engineering calculations more accessible to others, but VB6 is now becoming difficult to support on Win 10 so I'm now forcing myself to learn VB.Net. It was really tough at first but now I'm loving it... Your perspectives are invaluable. Thanks again!
OriginalGriff 5-Dec-18 11:10am
   
You're welcome!

You might have found it easier if you had switched from VB to C# - it's pretty much the same (but with { and }) but it doesn't let you do anything using outdated VB6 code, so you end up completely .NET quicker.
IanSp 5-Dec-18 11:48am
   
Hmmm, I've seen this suggestion many times before. However, VBA is still essential to my work with Excel, and I have a big library of VB6 code to convert. As programming is only a part time activity learning a new language seemed like 2 giant steps for me instead just 1 (although I still seem to have to touch virtually every line of my legacy VB6 code). I just hope MS continue to support it... just as I have invested the time to learn it. However, once I am able to get to full functionality in VB.Net I might consider your suggestion, converting VB.Net to C# might be a piece of cake compared to converting VB6 (to anything).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900