Printing DataGridView from Right to Left
Asked Answered
T

1

1

I'm new in printing in vb.net, what I want to do is printing DataGridView items I searched online for code and I found this source from MSDN, the code work perfect but what I want is printing the DataGridView from Right to left, how can I do this. thanks.

This is the source code which I get from Printing DataGridView Example:

Public Class Form1 

    ''' <summary> 
    ''' structire to hold printed page details 
    ''' </summary> 
    ''' <remarks></remarks> 
    Private Structure pageDetails 
        Dim columns As Integer 
        Dim rows As Integer 
        Dim startCol As Integer 
        Dim startRow As Integer 
    End Structure 
    ''' <summary> 
    ''' dictionary to hold printed page details, with index key 
    ''' </summary> 
    ''' <remarks></remarks> 
    Private pages As Dictionary(Of Integer, pageDetails) 

    Dim maxPagesWide As Integer 
    Dim maxPagesTall As Integer 

    ''' <summary> 
    ''' this just loads some text values into the dgv 
    ''' </summary> 
    ''' <param name="sender"></param> 
    ''' <param name="e"></param> 
    ''' <remarks></remarks> 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        DataGridView1.RowHeadersWidth = CInt(DataGridView1.RowHeadersWidth * 1.35) 
        For r As Integer = 1 To 100 
            Dim y As Integer = r 
            Dim fmt As String = "R{0}C{1}" 
            DataGridView1.Rows.Add() 
            DataGridView1.Rows(r - 1).SetValues(Enumerable.Range(1, 10).Select(Function(x) String.Format(fmt, y, x)).ToArray) 
            DataGridView1.Rows(r - 1).HeaderCell.Value = r.ToString 
        Next 
    End Sub 

    ''' <summary> 
    ''' shows a PrintPreviewDialog 
    ''' </summary> 
    ''' <param name="sender"></param> 
    ''' <param name="e"></param> 
    ''' <remarks></remarks> 
    Private Sub btnPreview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPreview.Click 
        Dim ppd As New PrintPreviewDialog 
        ppd.Document = PrintDocument1 
        ppd.WindowState = FormWindowState.Maximized 
        ppd.ShowDialog() 
    End Sub 

    ''' <summary> 
    ''' starts print job 
    ''' </summary> 
    ''' <param name="sender"></param> 
    ''' <param name="e"></param> 
    ''' <remarks></remarks> 
    Private Sub btnPrint_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPrint.Click 
        PrintDocument1.Print() 
    End Sub 

    ''' <summary> 
    ''' the majority of this Sub is calculating printed page ranges 
    ''' </summary> 
    ''' <param name="sender"></param> 
    ''' <param name="e"></param> 
    ''' <remarks></remarks> 
    Private Sub PrintDocument1_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint 
        ''this removes the printed page margins 
        PrintDocument1.OriginAtMargins = True 
        PrintDocument1.DefaultPageSettings.Margins = New Drawing.Printing.Margins(0, 0, 0, 0) 

        pages = New Dictionary(Of Integer, pageDetails) 

        Dim maxWidth As Integer = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width) - 40 
        Dim maxHeight As Integer = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Height) - 40 + Label1.Height 

        Dim pageCounter As Integer = 0 
        pages.Add(pageCounter, New pageDetails) 

        Dim columnCounter As Integer = 0 

        Dim columnSum As Integer = DataGridView1.RowHeadersWidth 

        For c As Integer = 0 To DataGridView1.Columns.Count - 1 
            If columnSum + DataGridView1.Columns(c).Width < maxWidth Then 
                columnSum += DataGridView1.Columns(c).Width 
                columnCounter += 1 
            Else 
                pages(pageCounter) = New pageDetails With {.columns = columnCounter, .rows = 0, .startCol = pages(pageCounter).startCol} 
                columnSum = DataGridView1.RowHeadersWidth + DataGridView1.Columns(c).Width 
                columnCounter = 1 
                pageCounter += 1 
                pages.Add(pageCounter, New pageDetails With {.startCol = c}) 
            End If 
            If c = DataGridView1.Columns.Count - 1 Then 
                If pages(pageCounter).columns = 0 Then 
                    pages(pageCounter) = New pageDetails With {.columns = columnCounter, .rows = 0, .startCol = pages(pageCounter).startCol} 
                End If 
            End If 
        Next 

        maxPagesWide = pages.Keys.Max + 1 

        pageCounter = 0 

        Dim rowCounter As Integer = 0 

        Dim rowSum As Integer = DataGridView1.ColumnHeadersHeight 

        For r As Integer = 0 To DataGridView1.Rows.Count - 2 
            If rowSum + DataGridView1.Rows(r).Height < maxHeight Then 
                rowSum += DataGridView1.Rows(r).Height 
                rowCounter += 1 
            Else 
                pages(pageCounter) = New pageDetails With {.columns = pages(pageCounter).columns, .rows = rowCounter, .startCol = pages(pageCounter).startCol, .startRow = pages(pageCounter).startRow} 
                For x As Integer = 1 To maxPagesWide - 1 
                    pages(pageCounter + x) = New pageDetails With {.columns = pages(pageCounter + x).columns, .rows = rowCounter, .startCol = pages(pageCounter + x).startCol, .startRow = pages(pageCounter).startRow} 
                Next 

                pageCounter += maxPagesWide 
                For x As Integer = 0 To maxPagesWide - 1 
                    pages.Add(pageCounter + x, New pageDetails With {.columns = pages(x).columns, .rows = 0, .startCol = pages(x).startCol, .startRow = r}) 
                Next 

                rowSum = DataGridView1.ColumnHeadersHeight + DataGridView1.Rows(r).Height 
                rowCounter = 1 
            End If 
            If r = DataGridView1.Rows.Count - 2 Then 
                For x As Integer = 0 To maxPagesWide - 1 
                    If pages(pageCounter + x).rows = 0 Then 
                        pages(pageCounter + x) = New pageDetails With {.columns = pages(pageCounter + x).columns, .rows = rowCounter, .startCol = pages(pageCounter + x).startCol, .startRow = pages(pageCounter + x).startRow} 
                    End If 
                Next 
            End If 
        Next 

        maxPagesTall = pages.Count \ maxPagesWide 

    End Sub 

    ''' <summary> 
    ''' this is the actual printing routine. 
    ''' using the pagedetails i calculated earlier, it prints a title, 
    ''' + as much of the datagridview as will fit on 1 page, then moves to the next page. 
    ''' this is setup to be dynamic. try resizing the dgv columns or rows 
    ''' </summary> 
    ''' <param name="sender"></param> 
    ''' <param name="e"></param> 
    ''' <remarks></remarks> 
    Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage 
        Dim rect As New Rectangle(20, 20, CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width), Label1.Height) 
        Dim sf As New StringFormat 
        sf.Alignment = StringAlignment.Center 
        sf.LineAlignment = StringAlignment.Center 

        e.Graphics.DrawString(Label1.Text, Label1.Font, Brushes.Black, rect, sf) 

        sf.Alignment = StringAlignment.Near 

        Dim startX As Integer = 50 
        Dim startY As Integer = rect.Bottom 

        Static startPage As Integer = 0 

        For p As Integer = startPage To pages.Count - 1 
            Dim cell As New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.ColumnHeadersHeight) 
            e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) 
            e.Graphics.DrawRectangle(Pens.Black, cell) 

            startY += DataGridView1.ColumnHeadersHeight 

            For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1 
                cell = New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.Rows(r).Height) 
                e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) 
                e.Graphics.DrawRectangle(Pens.Black, cell) 
                e.Graphics.DrawString(DataGridView1.Rows(r).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) 
                startY += DataGridView1.Rows(r).Height 
            Next 

            startX += cell.Width 
            startY = rect.Bottom 

            For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1 
                cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.ColumnHeadersHeight) 
                e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) 
                e.Graphics.DrawRectangle(Pens.Black, cell) 
                e.Graphics.DrawString(DataGridView1.Columns(c).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) 
                startX += DataGridView1.Columns(c).Width 
            Next 

            startY = rect.Bottom + DataGridView1.ColumnHeadersHeight 

            For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1 
                startX = 50 + DataGridView1.RowHeadersWidth 
                For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1 
                    cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.Rows(r).Height) 
                    e.Graphics.DrawRectangle(Pens.Black, cell) 
                    e.Graphics.DrawString(DataGridView1(c, r).Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) 
                    startX += DataGridView1.Columns(c).Width 
                Next 
                startY += DataGridView1.Rows(r).Height 
            Next 

            If p <> pages.Count - 1 Then 
                startPage = p + 1 
                e.HasMorePages = True 
                Return 
            Else 
                startPage = 0 
            End If 

        Next 

    End Sub 

End Class 
Territorialize answered 13/10, 2016 at 1:5 Comment(12)
When you can simply use an RDLC report to print DataGridView, why do you want to print the DataGridView using GDI+ code?Amritsar
If for any reason you can't/don't want to use rdlc reports, you can use a run-time t4 template containing html codes to print your data model in a more friendly/flexible way than using GDI+. Take a look at this example.Amritsar
Because i don't have any idea about RDLC report, its impossible and easy ?? and do i need to install the RDLC report on the client machine ?Territorialize
The choice is yours, probably you know your requirement better. But deploying RDLC reports using clickonce or any other installer is really simple and reporting tools are created for such tasks.Amritsar
Anyway to make that printing RTL, you need to make some changes in the posted code, for example all DrawString methods, should use a StringFormat with StringFormatFlags.DirectionRightToLeft. Also instead of drawing rectangles from left to right, you should correct coordinates to support drawing from right to left. It's completely possible to correct that problems with patience. But rdlc solution is much better.Amritsar
Also take a look at this post.Amritsar
@RezaAghaei i remember you from the last time :) i will try to modify the code, but i prefer to see how the RDLC work, what I'm fried from problems, i heard that Crystal Reports and other similar cause problems for customers.Territorialize
I remember you too :) - RDLC reports are really simple and useful and easy to deploy. But surely you need to read more about them to decide if you want to use in your customer environment. In general, in a data-base application, printing without reporting tools is painful and somehow is reinventing the wheel IMO.Amritsar
@RezaAghaei thank you i'm so greatful that you here :)Territorialize
@RezaAghaei Hello Reza, i want to ask you something about this post you mentioned to me in the above comment link, every thing perfect but i'm using different way to load data to DataGridView see the code, is there possible way to use my Fillgrid method to do it.Territorialize
When you have a DataTable which you filled using a DataAdapter, it's better to use the data table to feed the report. I saw your code and you have a data table. Use it. For more information take a look at this post and this oneAmritsar
@RezaAghaei can you help me with this if you have time plese linkTerritorialize
A
1

To make that printing RTL, you need to make some changes in the posted code

  1. You should use a StringFormat having StringFormatFlags.DirectionRightToLeft format glag.
  2. Also instead of drawing rectangles from left to right, you should correct coordinates to support drawing from right to left.

To solve the first issue, it's enough to add the mentioned flag to the string format:

sf.FormatFlags = sf.FormatFlags Or StringFormatFlags.DirectionRightToLeft

To solve the second issue, you should create such method:

Public Function GetRTLCoordinates(container As Rectangle, drawRectangle As Rectangle) _
    As Rectangle
    Return New Rectangle(container.Width - drawRectangle.Width - drawRectangle.X, _
        drawRectangle.Y, drawRectangle.Width, drawRectangle.Height)
End Function

Then in the code, after each line of code which calculated a rectangle named cell, add this line of code:

cell = GetRTLCoordinates(rect, cell)

Code

Here is the changed version of PrintDocument1_PrintPage method. Don't forget to copy GetRTLCoordinates method which is mentioned above.

Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
    Dim rect As New Rectangle(20, 20, CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width), Label1.Height)
    Dim sf As New StringFormat
    sf.Alignment = StringAlignment.Center
    sf.LineAlignment = StringAlignment.Center
    sf.FormatFlags = sf.FormatFlags Or StringFormatFlags.DirectionRightToLeft

    e.Graphics.DrawString(Label1.Text, Label1.Font, Brushes.Black, rect, sf)

    sf.Alignment = StringAlignment.Near

    Dim startX As Integer = 50
    Dim startY As Integer = rect.Bottom

    Static startPage As Integer = 0

    For p As Integer = startPage To pages.Count - 1
        Dim cell As New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.ColumnHeadersHeight)
        cell = GetRTLCoordinates(rect, cell)
        e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell)
        e.Graphics.DrawRectangle(Pens.Black, cell)

        startY += DataGridView1.ColumnHeadersHeight

        For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1
            cell = New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.Rows(r).Height)
            cell = GetRTLCoordinates(rect, cell)
            e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell)
            e.Graphics.DrawRectangle(Pens.Black, cell)
            e.Graphics.DrawString(DataGridView1.Rows(r).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf)
            startY += DataGridView1.Rows(r).Height
        Next

        startX += cell.Width
        startY = rect.Bottom

        For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1
            cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.ColumnHeadersHeight)
            cell = GetRTLCoordinates(rect, cell)
            e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell)
            e.Graphics.DrawRectangle(Pens.Black, cell)
            e.Graphics.DrawString(DataGridView1.Columns(c).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf)
            startX += DataGridView1.Columns(c).Width
        Next

        startY = rect.Bottom + DataGridView1.ColumnHeadersHeight

        For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1
            startX = 50 + DataGridView1.RowHeadersWidth
            For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1
                cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.Rows(r).Height)
                cell = GetRTLCoordinates(rect, cell)
                e.Graphics.DrawRectangle(Pens.Black, cell)
                e.Graphics.DrawString(DataGridView1(c, r).Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf)
                startX += DataGridView1.Columns(c).Width
            Next
            startY += DataGridView1.Rows(r).Height
        Next

        If p <> pages.Count - 1 Then
            startPage = p + 1
            e.HasMorePages = True
            Return
        Else
            startPage = 0
        End If

    Next

End Sub
Amritsar answered 13/10, 2016 at 3:2 Comment(6)
This is perfect, I'm reading about Report Viewer also and it's interesting, and really thank you, you the best man:)Territorialize
You're welcome :) - Good job, I posted the fixed because it seems it's a popular example from MSDN.Amritsar
Hello again, i want to ask you something about Report Viewer, I'm trying to create new report , i already added the Report viewer dll file but when i try to add report i can't find the reporting in categories see picture 1 and it must be like this picture 2 do you want me to ask new qustion ??Territorialize
@Rabeeaqabaha hmmm, it shows Reporting category for me. Also if I select visual C# items (which I can not see in your screenshot) it shows report between items at middle list. I'm using Visual Studio 2013 and also in 2010 is the same. Also I've not added a dll or anything. It's by default there after installing Visual Studio. What's the version of your Visual Studio? And by the way, Hi.Amritsar
Hii :) yes this weird , I'm using Visual studio 2015 Enterprise, and there's no Report Viewer control before I added the Dll file, now there is Control but i cant find it in the categories, i will try to reinstall it.Territorialize
Take a look at this post. Probably it will solve your problem, but if for any reason it didn't solve the problem, probably you can download them from here.Amritsar

© 2022 - 2024 — McMap. All rights reserved.