?
Solved

Print problem

Posted on 2005-05-10
12
Medium Priority
?
233 Views
Last Modified: 2010-04-23
I want to print a page that has some text on it and a couple of datagrids.

For the moment I created some fonts, brushes and pens in the PrintDocument.PrintPage event handler.

I also been able to put the text on the paper also in this event handler.

My questions are as follows:

What's the best wat to show those datagrids (or tables) ?

What do I have to do when the page is full ? How can I start printing on the next page ?

Do I have to use DefaultPageSettings.Margins or can I go out of them ? Because these look a little too big. The space to put the text on is a bit too small.

Thanks!!!

Urgent help needed. :o)
0
Comment
Question by:Jerry_0001
  • 6
  • 4
  • 2
12 Comments
 
LVL 38

Accepted Solution

by:
PaulHews earned 700 total points
ID: 13968108
Here is an example that prints a dataset bound to a datagrid:
http://thescarms.com/dotNet/PrintDataSet.asp

I found it useful when getting started, and you can probably use it as a starting point.

>What do I have to do when the page is full ? How can I start printing on the next page ?
In your printpage event, if you have more lines to print than you can fit on the page, you set
ev.HasMorePages = True

If you set it to false, the printpage event will not fire again...

>Do I have to use DefaultPageSettings.Margins or can I go out of them ? Because these look a little too big. The space to put the text on is a bit too small.

You can change the margins using the PageSettings object of the print document.  Set it up before you call document.print.
0
 
LVL 27

Assisted Solution

by:planocz
planocz earned 700 total points
ID: 13968296
sample ....

'FORM 1

Imports System.Data.OleDb

Public Class frmPrintDataGrid
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents dg As System.Windows.Forms.DataGrid
    Friend WithEvents btnPrint As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.dg = New System.Windows.Forms.DataGrid
        Me.btnPrint = New System.Windows.Forms.Button
        CType(Me.dg, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()
        '
        'dg
        '
        Me.dg.DataMember = ""
        Me.dg.HeaderForeColor = System.Drawing.SystemColors.ControlText
        Me.dg.Location = New System.Drawing.Point(0, 0)
        Me.dg.Name = "dg"
        Me.dg.Size = New System.Drawing.Size(292, 196)
        Me.dg.TabIndex = 0
        '
        'btnPrint
        '
        Me.btnPrint.Location = New System.Drawing.Point(8, 220)
        Me.btnPrint.Name = "btnPrint"
        Me.btnPrint.TabIndex = 1
        Me.btnPrint.Text = "Print"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.btnPrint)
        Me.Controls.Add(Me.dg)
        Me.Name = "Form1"
        Me.Text = "Form1"
        CType(Me.dg, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)

    End Sub

#End Region
    Private dge As New DataGridEx

    Dim cn As OleDbConnection
    Dim sConn As String = _
     "Provider=Microsoft.Jet.OLEDB.4.0;" & _
     "Data Source=C:\Visual Studio Projects\TestArea\TestDB.mdb;"

    Dim ds As DataSet
    Dim da As OleDbDataAdapter
    Dim sSQL As String = "SELECT * FROM Table1"
    Dim sSource As String = "Table1"

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.CreateDS()
        dg.DataSource = ds
        dg.DataMember = "Table1"
    End Sub
    Private Sub CreateDS()
        ds = New DataSet
        'Connect to database and specify sSQL
        da = New OleDbDataAdapter(sSQL, sConn)

        Try
            'using DataAdapter enter records from table to DataSet
            da.Fill(ds, sSource) 'fsSource is a table
        Catch objExc As System.Exception
            'if error happend then email or write to Log?
            Exit Sub
        End Try

    End Sub


    Private Sub btnPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click
        Dim obj, obj2 As Object

        obj2 = New DataView(ds.Tables("Table1"))
        dge.Print(obj2, ds.Tables("Table1"), CType(Me.BindingContext(dg.DataSource), CurrencyManager))
        MsgBox("Done")
    End Sub
End Class


'CLASS

Imports System.Drawing.Printing
Public Class DataGridPrintDocument
    Inherits PrintDocument

    Private printFont As Font
    Private headerFont As Font
    Private dataGridToPrint As DataGridEx
    Private objTable As DataTable
    Private objView As DataView
    Private privCM As CurrencyManager
    Private privNP As Integer
    Private privStrQuestion As String

    Public Sub New(ByVal dataGridToPrint As DataGridEx, ByVal objView As DataView, ByVal objTable As DataTable, ByVal privCM As CurrencyManager, _
    ByVal NP As Integer, ByVal Question As String)
        MyBase.New()
        Me.dataGridToPrint = dataGridToPrint
        If objView Is Nothing Then
            Me.objView = objTable.DefaultView
        Else
            Me.objView = objView
        End If
        Me.objTable = objTable
        Me.privCM = privCM
        privNP = NP
        privStrQuestion = Question
    End Sub

    Private npp As Integer
    'Override OnBeginPrint to set up the font we are going to use
    Protected Overrides Sub OnBeginPrint(ByVal ev As PrintEventArgs)
        MyBase.OnBeginPrint(ev)
        CurRow = 0
        CurCol = 0
        NextFirstCol = 0
        npp = 0
        headerFont = dataGridToPrint.HeaderFont
        printFont = dataGridToPrint.DefaultFont
    End Sub

    Dim CurRow, CurCol As Integer
    Dim NextFirstCol As Integer
    'Override the OnPrintPage to provide the printing logic for the document
    Protected Overrides Sub OnPrintPage(ByVal ev As PrintPageEventArgs)

        MyBase.OnPrintPage(ev)

        NextFirstCol = 0
        npp += 1

        Dim lpp As Single = 0
        Dim yPos As Single = 0
        Dim count As Integer = 0
        Dim leftMargin As Single = ev.MarginBounds.Left
        Dim topMargin As Single = ev.MarginBounds.Top
        Dim line As String

        'Work out the number of lines per page
        'Use the MarginBounds on the event to do this
        lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics)

        'Now iterate over the file printing out each line
        'NOTE WELL: This assumes that a single line is not wider than the page width
        'Check count first so that we don't read line that we won't print
        Dim tblStyle As DataGridTableStyle = dataGridToPrint.TableStyles(objTable.TableName)
        If tblStyle Is Nothing Then
            tblStyle = dataGridToPrint.CreateTableStyle(objTable)
        End If
        Dim rh As Integer = tblStyle.PreferredRowHeight
        Dim headerHeight As Integer
        If dataGridToPrint.CustomColumnHeaders = False Then
            headerHeight = rh
        Else
            headerHeight = dataGridToPrint.CustomColumnHeaderHeight
        End If
        Dim nrperpage As Integer = (ev.MarginBounds.Height \ rh) - (headerHeight \ rh)
        Dim i, j, w, l As Integer
        l = CInt(topMargin)
        w = CInt(leftMargin)
        Dim divPen As New Pen(dataGridToPrint.GridLineColor)
        Dim hfpen As New Pen(dataGridToPrint.HeaderForeColor)
        Dim hbBrush As New SolidBrush(dataGridToPrint.HeaderBackColor)
        Dim hfBrush As New SolidBrush(dataGridToPrint.HeaderForeColor)
        For j = CurCol To tblStyle.GridColumnStyles.Count - 1
            If w + tblStyle.GridColumnStyles(j).Width >= ev.MarginBounds.Right And j <> CurCol Then
                NextFirstCol = j
                Exit For
            End If
            If tblStyle.GridColumnStyles(j).Width = 0 Then GoTo continuefor
            Dim r As New Rectangle(w, l, tblStyle.GridColumnStyles(j).Width, headerHeight)
            Dim rf As New Drawing.RectangleF(w, l, r.Width, r.Height)
            ev.Graphics.FillRectangle(hbBrush, r)
            'ev.Graphics.DrawRectangle(Pens.Black, r)

            If dataGridToPrint.CustomColumnHeaders = False Then
                ev.Graphics.DrawString(tblStyle.GridColumnStyles(j).HeaderText, headerFont, hfBrush, rf)
            Else
                dataGridToPrint.PaintHeaderCell(ev.Graphics, tblStyle.GridColumnStyles(j).HeaderText, _
                    dataGridToPrint.CustomColumnHeaderFont, hfBrush, r, 0, 0)
            End If
            w += tblStyle.GridColumnStyles(j).Width
continuefor:
        Next
        l += headerHeight
        For i = CurRow To nrperpage + CurRow - 1
            If i >= objView.Count Then Exit For
            w = CInt(leftMargin)
            For j = CurCol To tblStyle.GridColumnStyles.Count - 1
                If w + tblStyle.GridColumnStyles(j).Width >= ev.MarginBounds.Right And j <> CurCol Then Exit For
                If tblStyle.GridColumnStyles(j).Width = 0 Then GoTo continuefor2
                Dim r As New Rectangle(w, l, tblStyle.GridColumnStyles(j).Width, rh)
                Dim rf As New Drawing.RectangleF(w, l, r.Width, r.Height)
                ev.Graphics.DrawRectangle(divPen, r)
                ev.Graphics.DrawString(objView.Item(i).Item(j).ToString(), printFont, Drawing.Brushes.Black, rf)
                w += tblStyle.GridColumnStyles(j).Width
continuefor2:
            Next
            l += rh
        Next

        l = CInt(topMargin)
        w = CInt(leftMargin)
        For j = CurCol To tblStyle.GridColumnStyles.Count - 1
            If w + tblStyle.GridColumnStyles(j).Width >= ev.MarginBounds.Right And j <> CurCol Then
                Exit For
            End If
            If tblStyle.GridColumnStyles(j).Width = 0 Then GoTo continuefor3
            Dim r As New Rectangle(w, l, tblStyle.GridColumnStyles(j).Width, headerHeight)
            ev.Graphics.DrawRectangle(Pens.Black, r)

            w += tblStyle.GridColumnStyles(j).Width
continuefor3:
        Next

        CurRow = i
        'If we have more lines then print another page
        If (CurRow < objView.Count) Then
            ev.HasMorePages = True
        Else
            If NextFirstCol = 0 Then
                ev.HasMorePages = False
            Else
                CurRow = 0
                CurCol = NextFirstCol
                ev.HasMorePages = True
            End If
        End If
        divPen.Dispose()
        hfpen.Dispose()
        hbBrush.Dispose()
        hfBrush.Dispose()
    End Sub
End Class


'CLASS

Public Class DataGridEx
    Inherits DataGrid

    Protected protCustomColumnsHeaders As Boolean
    Protected protOldHeaderFont As Font
    Protected protNRowsInCaption As Integer
    Protected protRowHeaderWidth As Integer

    Private components As System.ComponentModel.IContainer

    Public Function CreateTableStyle(ByVal Table As DataTable) As DataGridTableStyle
        Dim ts1 As New DataGridTableStyle
        ts1.MappingName = Table.TableName
        Dim i As Integer
        For i = 0 To Table.Columns.Count - 1
            If Table.Columns(i).DataType Is GetType(Boolean) Then
                Dim boolCol As New DataGridBoolColumn '       DataGridBoolColumnEx
                boolCol.MappingName = Table.Columns(i).ColumnName
                boolCol.HeaderText = Table.Columns(i).ColumnName
                ts1.GridColumnStyles.Add(boolCol)
            Else
                Dim TextCol As New DataGridTextBoxColumn '   DataGridTextBoxColumnEx
                TextCol.MappingName = Table.Columns(i).ColumnName
                TextCol.HeaderText = Table.Columns(i).ColumnName
                ts1.GridColumnStyles.Add(TextCol)
            End If
        Next
        Me.TableStyles.Add(ts1)
        Return ts1
    End Function

    Private storedPageSettings As System.Drawing.Printing.PageSettings
    Public Property PageSettings() As System.Drawing.Printing.PageSettings
        Get
            Return storedPageSettings
        End Get
        Set(ByVal Value As System.Drawing.Printing.PageSettings)
            storedPageSettings = Value
        End Set
    End Property

    Public Sub PageSetup()
        Try
            Dim psDlg As New PageSetupDialog

            If (storedPageSettings Is Nothing) Then
                storedPageSettings = New System.Drawing.Printing.PageSettings
            End If

            psDlg.PageSettings = storedPageSettings
            psDlg.ShowDialog()
        Catch ex As Exception
            'msgbox('error")
        End Try

    End Sub

    Public Sub Print(ByVal objView As DataView, ByVal objTable As DataTable, ByVal cm As CurrencyManager)
        Try
            'Assumes the default printer
            Dim pd As DataGridPrintDocument = New DataGridPrintDocument(Me, objView, objTable, cm, -1, Nothing)

            If Not (storedPageSettings Is Nothing) Then
                pd.DefaultPageSettings = storedPageSettings
            End If


            Dim dlg As New PrintDialog
            dlg.Document = pd
            Dim result As DialogResult = dlg.ShowDialog()

            If (result = DialogResult.OK) Then
                pd.Print()
            End If

        Catch ex As Exception
            'InformationMessageModule.InformationMessage("An error occurred printing the DataGrid - " & ex.Message)
        End Try

    End Sub

    Public Property CustomColumnHeaders() As Boolean
        Get
            Return protCustomColumnsHeaders
        End Get
        Set(ByVal Value As Boolean)
            If Value Then
                If protCustomColumnsHeaders = False Then
                    protOldHeaderFont = Me.HeaderFont
                    Dim g As Graphics = Me.CreateGraphics()
                    Me.HeaderFont = New Font(System.Drawing.FontFamily.GenericSerif, (protNRowsInCaption) * Me.HeaderFont.GetHeight(g) - 8, FontStyle.Regular, GraphicsUnit.Point)
                    g.Dispose()
                    protRowHeaderWidth = Me.RowHeaderWidth
                End If
                Me.CaptionVisible = False
                Me.ColumnHeadersVisible = True
            Else
                If Not Me.HeaderFont Is Nothing Then Me.HeaderFont.Dispose()
                Me.HeaderFont = Me.protOldHeaderFont
                Me.protOldHeaderFont = Nothing
                Me.CaptionVisible = True
                Me.ColumnHeadersVisible = True
            End If
            protCustomColumnsHeaders = Value
        End Set
    End Property

    Public ReadOnly Property CustomColumnHeaderHeight() As Integer
        Get
            Return Me.HeaderFont.Height
        End Get
    End Property

    Public ReadOnly Property CustomColumnHeaderFont() As Font
        Get
            If protCustomColumnsHeaders Then
                Return Me.protOldHeaderFont
            End If
            Return Me.HeaderFont
        End Get
    End Property

    Public Sub PaintHeaderCell(ByVal g As Graphics, ByVal Sentence As String, ByVal F As Font, ByVal br As Brush, ByVal Bounds As Rectangle, ByVal offsetx As Integer, ByVal offsety As Integer)
        Dim strs(protNRowsInCaption - 1), tt() As String
        tt = Sentence.Split(" "c)
        Dim i, j, n As Integer
        j = 0
        Dim s As String = ""
        Dim os As String
        os = s
        For i = 0 To protNRowsInCaption - 1
            strs(i) = ""
        Next
        For i = 0 To tt.Length - 1
            If s <> "" Then
                s &= " " & tt(i)
            Else
                s = tt(i)
                os = s
            End If
            Dim w As Integer = CInt(g.MeasureString(s, F).Width)
            If w >= Bounds.Width Then
                strs(j) = os
                If s <> tt(i) Then
                    s = tt(i)
                    os = tt(i)
                Else
                    s = ""
                    os = ""
                End If
                If j = protNRowsInCaption - 1 And i <= tt.Length - 1 Then
                    strs(j) = "..."
                    Exit For
                End If
                j += 1
            Else
                os = s
            End If
            If i = tt.Length - 1 And j <> protNRowsInCaption Then
                strs(j) = s
            End If
        Next
        n = Me.protNRowsInCaption - 1
        Dim h As Integer = (Bounds.Height Mod F.Height) \ 2
        For i = 0 To n
            Dim w As Integer = CInt(g.MeasureString(strs(i), F).Width)
            If w < Bounds.Width Then
                w = (Bounds.Width - w) \ 2
            Else
                If Bounds.Width > 0 Then
                    While w >= Bounds.Width
                        strs(i) = strs(i).Substring(0, strs(i).Length - 1)
                        w = CInt(g.MeasureString(strs(i), F).Width)
                    End While
                Else
                    Return
                End If
                w = 0
            End If
            g.DrawString(strs(i), F, br, Bounds.X + w + offsetx, Bounds.Y + h + offsety)
            h += F.Height
        Next
    End Sub

    Public Function GetHeaderWidth(ByVal g As Graphics, ByVal Text As String) As Integer
        Dim strs() As String
        strs = Text.Split(" "c)
        Dim ws(strs.Length - 1) As Integer
        Dim i, n As Integer
        n = Math.Min(ws.Length - 1, Me.protNRowsInCaption - 1)
        Dim f As Font
        If protCustomColumnsHeaders Then
            f = protOldHeaderFont
        Else
            f = Me.HeaderFont
        End If
        For i = 0 To n
            ws(i) = CInt(g.MeasureString(strs(i), f).Width)
            GetHeaderWidth = Math.Max(GetHeaderWidth, ws(i))
        Next
    End Function
End Class
0
 

Author Comment

by:Jerry_0001
ID: 13975558
Thanks for the fast responses!!

But I'm still having some trouble with it.

The page layout should look something like this.

First page:

Image                Title

Some text

Datagrid 1

Text

Datagrid 2

Text Datagrid 3

...


At the end of each page there should be a footer (a line and below of that should be page 1 of N)

Starting page 2 there also should be a header (a name and a als a line)


Is it possible to give me such an exemple, since I'm totally stuck ???

Thanks!!!!
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:Jerry_0001
ID: 13977258
Urgent help needed please.

This app has to be finished and tested next monday...


Thanks!!!!
0
 

Author Comment

by:Jerry_0001
ID: 13984733
Is there nobody who can help me on this ???
0
 
LVL 27

Expert Comment

by:planocz
ID: 13985698
There is not straight forward way to do what you are talking about.
You may have to go to to something like Crystal Reports.

Here is a link that gives you a few more ideas.

http://www.c-sharpcorner.com/Printing.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwinforms/html/printwinforms.asp
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 13986208
The image can be drawn in the PrintPage event with

e.Graphics.DrawImage(image, rect)

You will need to change fonts to draw your title, headers, footers, and/or text and datagrids.  That's demonstrated in the sample I posted above.  

You will have to calculate the positions where things are printed  yourself.  The only way to do this is to try it, and if you don't like what you have, modify it.  For the most part it's a matter of using the Graphics.MeasureString to calculate the height and width of  a string with a given font, then calculating where the next element will go based on that.  I use the

MyFontHeight = MyFont.GetHeight(ev.Graphics)

to calculate row height, since you want this to be a fixed number.

The lines can be drawn using the DrawLine function:

Dim pen As New Pen(Color.Black)
ev.Graphics.DrawLine(pen, point1, point2)

My advice is to get started yourself, and if you run into problems post back here with the code.
0
 

Author Comment

by:Jerry_0001
ID: 13986295
I will give it another try.

But what do I have to do I my first page is full ?

If I put everything in the printpage event handler, and the page is full (and I set HasMorePages = true), won't he start printing from the beginning again, instead of continuing where he stopped?

0
 

Author Comment

by:Jerry_0001
ID: 13994143
Ok, today I started to give it another try.

I have a first question though.

Is there a way to get the toal number of pages that will be printed ?

I would like to have a footer that has "Page 1 of 5" for example.

How can you do that ?
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 13995136
>If I put everything in the printpage event handler, and the page is full (and I set HasMorePages = true), won't he start printing from the beginning again, instead of continuing where he stopped?<

No because when you go to the print handler event, you track the number of rows you've printed in a module level variable.

>Is there a way to get the toal number of pages that will be printed ?

I would like to have a footer that has "Page 1 of 5" for example.

How can you do that ?<

Do a first run through to see how many rows/lines fit on the page.  Now use that number to calculate how many pages you will have.
0
 

Author Comment

by:Jerry_0001
ID: 14058404
Ok, I 've made a function for each line he has to print. When page is full he goes to another function that adds a page and then returns from where he came from.

Everything seems to work fine. It's a long and complicated solution but it does its job.

Thanks for the help.

Since you both helped me, I'll divide the points... Thanks!!
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 14061705
Glad you got it working, and thanks for the grade and the points.  :)
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create and use a custom WaterMark textbox class.  The custom WaterMark textbox class allows you to set the WaterMark Background Color and WaterMark text at design time.   IMAGE OF WATERMARKS STEPS Create VB …
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…
Suggested Courses
Course of the Month15 days, 17 hours left to enroll

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question