Click here to Skip to main content
15,884,018 members
Articles / Programming Languages / Visual Basic
Article

Printing with a custom DataGrid

Rate me:
Please Sign up or sign in to vote.
4.58/5 (41 votes)
20 Aug 20027 min read 431.4K   5.1K   69   38
This article shows how to implement print functionality with a custom DataGrid

Introduction

In many Windows applications, it could be useful to add some kind of print functionality. The DataGrid included in Windows Forms does not include any support for this. In this article I will show how to use a custom DataGrid written by me in Visual Basic .NET inheriting from class System.Windows.Froms.DataGrid to implement an application with print preview and obviously print support. If you already have a window with a DataGrid, you can go directly to the section - Using the custom DataGrid.

Print support is written to work, if the DataSource used in the DataGrid is a DataTable or a DataView. There is no support if the DataGrid is bound to a DataSet or some other kind of object (Arrays and so on). Modifications in source code for other DataSource objects would not be very difficult.

The custom DataGrid has also other useful features like the possibility to print column headers using more then one row (enabled by default), export data in XML and HTML formats and send data by E-mail.

This code is written in Visual Basic mainly because I was writing it for a true application in VB.NET. There is no reason why you cannot use it in a C# or a J# application. The following code is also written in VB.NET but with some minor changes (mainly because the syntax is different). It will also work fine in other .NET languages.

The basic application

The first thing you have to do is to create a new Windows Application Project with a Form hosting a standard DataGrid and a MainMenu object. Then you can add (using the designer or with code if you like), some menus in this form. Normally the main form can have a Sizable FormBorderStyle. The DataGrid will have the Dock property set to Fill.

Now you can add a database connection, a DataAdapter and a DataSet (typed or untyped) you will use, to fill the DataGrid (for the sake of simplicity this code is omitted here).

Image 1

In the demo project, you will find a test file called Demo.xml used to fill the DataSet with its data. In the sample application in the Form constructor, I simply use the method ReadXml to read a table named orders, in a DataSet named dsOrders.

VB
Public Sub New()
  MyBase.New()
  'This call is required by the Windows Form Designer.
  InitializeComponent()
  'Added Code 
  dsOrders.ReadXml("..\Demo.xml")
  objDataGrid.DataSource = dsOrders.Tables(0).DefaultView
End Sub

If you compile and run your project now, you will obtain a basic Windows application showing some data in a DataGrid.

Using the custom DataGrid

The first thing to do is to add to your project, a reference to the library hosting the custom DataGrid (customcontrols.dll) or to add to your solution the existing project with the DataGrid (customcontrols.vbproj). If you choose this second way, you must add a reference in your main project, to the second project you just added.

To transform the normal DataGrid to my custom DataGrid, the simplest way is by using the Search and Replace feature in Visual Studio. Open your Form in Code View and then use the Replace function (CTRL+H). As source text, you will insert System.Windows.Forms.DataGrid and as replace text insert CustomControls.DataGridEx.

Image 2

There is another way to use my custom DataGrid if you start by scratch, with a new Form. Simply you can customize the Visual Studio toolbox, adding my custom DataGrid (the class name is DataGridEx and is hosted in customcontrols.dll). Then instead of dragging in the designer, the standard DataGrid, you will drag the DataGridEx control. The result will be the same as in the previous approach.

If you try to compile and run the application now, you will obtain a runtime exception. This is because, to obtain print preview support and to support column headers using more then one row, you have to create a TableStyle, with information about every column (especially the column width). Looking in MSDN, you can find this kind of information, but this can be a long and extremely tedious process. To skip this, in the form constructor, you can call the method AdjustColumnWidths of the DataGrid (this method evaluates an optimum column width for each column, based on the table data). Alternatively you can invoke the method AdjustColumnWidthToTitles (that evaluates column widths based on column header text). This code can also be placed in the form Load event handler. If you call either of the two methods, you also have some interesting features like the possibility to display boolean columns in colored cells or to use custom cell controls.

VB
Public Sub New()
  ...
  objDataGrid.AdjustColumnWidths(dsOrders.Tables(0))
End Sub

To use custom column header text, you can use the method SetColumnName like in the following example.

VB
Public Sub New()
  ...
  objDataGrid.SetColumnName(dsOrders.Tables(0), "OrderID", "Order Number")
  objDataGrid.SetColumnName(dsOrders.Tables(0), "OrderDate", "Order Date")
End Sub

To add the Page Setup code, simply you will invoke the PageSetup method (a static/shared method) of the PageSetup class contained in the CustomControls namespace.

VB
Private Sub mnuPageSetup_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) _
  Handles mnuPageSetup.Click
    CustomControls.PageSetup.PageSetup()
End Sub

To add Print and Print Preview support, you can simply call the methods Print and PrintPreview of the DataGrid. The following code is quite more complex because it works also if the DataSource used is a DataTable and not only if it is a DataView.

VB
Private Sub mnuPrintPreview_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles mnuPrintPreview.Click
  Dim obj, obj2 As Object
  obj = objDataGrid.DataSource
  If TypeOf (obj) Is DataView Then
   obj2 = CType(obj, DataView).Table
  Else
   obj2 = obj
   obj = Nothing
  End If
  Me.objDataGrid.PageSettings = CustomControls.PageSetup.PageSettings
  objDataGrid.PrintPreview(CType(obj, DataView), CType(obj2, DataTable), _
   CType(Me.BindingContext(objDataGrid.DataSource), CurrencyManager), _
   25, "Do you wish to continue?")
End Sub 
Private Sub mnuPrint_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles mnuPrint.Click
  Dim obj, obj2 As Object
  obj = objDataGrid.DataSource
  If TypeOf (obj) Is DataView Then
   obj2 = CType(obj, DataView).Table
  Else
   obj2 = obj
   obj = Nothing
  End If
  Me.objDataGrid.PageSettings = CustomControls.PageSetup.PageSettings
  objDataGrid.Print(CType(obj, DataView), CType(obj2, DataTable), _
   CType(Me.BindingContext(objDataGrid.DataSource), CurrencyManager))
End Sub

Adding custom columns

To add custom columns in a DataGrid, you have to create your own ColumnStyle class inheriting from the class System.Windows.Forms.DataGridColumnStyle (or other classes inheriting from it).

In the CustomControls library you can find two sample classes usable in your applications. The first one is DataGridBoolColumnEx (a normal boolean column with a background painted in a custom color if the value in its cell is true) and the second is DataGridPushPinColumn (a boolean column with custom painting). In the sample application after calling AdjustColumnWidths, I added a call to a private function called AddOtherColumns. This function in the first 11 rows simply obtains a reference to the table loaded from the XML file and adds two new boolean columns initializing their values. Then two new ColumnStyles are created and initialized supplying the column header, the column name in the DataTable and the column width in the DataGrid. Finally these column styles are added to the table style used by the DataGrid.

VB
Public Sub New()
MyBase.New()
...
objDataGrid.AdjustColumnWidths(dsOrders.Tables(0))
AddOtherColumns()
...
End Sub


Private Sub AddOtherColumns()
  Dim myType As System.Type
  myType = System.Type.GetType("System.Boolean")
  Dim tbl As DataTable
  tbl = dsOrders.Tables(0)
  tbl.Columns.Add(New System.Data.DataColumn("BoolColumn", myType))
  tbl.Columns.Add(New System.Data.DataColumn("PinnedColumn", myType))
  Dim i As Integer
  For i = 0 To tbl.Rows.Count - 1
    tbl.Rows(i).Item("BoolColumn") = IIf(i Mod 3 <> 0, True, False)
    tbl.Rows(i).Item("PinnedColumn") = IIf(i Mod 3 = 0, True, False)
  Next
  Dim tst As DataGridTableStyle
  tst = objDataGrid.GetTblStyle(tbl)
  Dim cs As New CustomControls.DataGridBoolColumnEx(Color.Blue)
  cs.HeaderText = "Sample Boolean Column"
  cs.MappingName = "BoolColumn"
  cs.Width = 50
  tst.GridColumnStyles.Add(cs)
  Dim cs2 As New CustomControls.DataGridPushPinColumn()
  cs2.HeaderText = "Pinned Column"
  cs2.MappingName = "PinnedColumn"
  cs2.Width = 100
  tst.GridColumnStyles.Add(cs2)
End Sub

The following images show the form when the application is started and when the print preview is invoked.

Image 3

Image 4

Low level details

As mentioned earlier, the class DataGridEx inherits from the standard .NET DataGrid. It offers some more functionality and these new features are spread among classes contained in a DLL called CustomControls. The DataGridEx class offers many new methods. Mainly there are methods used for print support, to extend basic functionality and to work with table styles.

I will not explain every method and its functionality but I will only cite the most important and useful ones. If you look at the code, I think you will find many other features.

There is a method called HitCellTest usable to raise an event called CellHitTest. This event is fired based on mouse position and tells the user what is the cell the mouse pointer is over. A property called MouseOverNotificationEnabled is usable to enable or disable the notification of current cell based on mouse pointer position. If the notification is enabled, every second the mouse position is checked and an event called MouseOverNotification is eventually fired (this can be useful to display custom tooltips based on mouse position).

When you call the method AdjustColumnWidthToTitles a new TableStyle is created and for each column in the table passed as parameter, a ColumnStyle is created. The column width is evaluated based on the header font. The method AdjustColumnWidths simply calls AdjustColumnWidthToTitles and then makes a cycle on the table for each row and each column eventually enlarging the column width. These methods create column styles based on the datatype hosted in the columns. They try to create extended column styles (the ones written by me) so if there is for example a boolean column a DataGridBoolColumnEx is created.

The class PrintPreviewDialogEx inherits from the standard print preview dialog. The code in this class is quite interesting because the standard PrintPreviewDialog has a private toolbar. If I simply inherit from this class, I cannot obtain a standard reference to this toolbar and so I couldn't add any more toolbar buttons. The solution to this problem was to use the features of .NET reflection to obtain a reference to the private variable. The true print preview dialog usable by users is TablePrintPreviewDialog. It inherits from PrintPreviewDialogEx and adds some functionality as the export of data in XML or HTML and the possibility to send the data by E-mail.

Conclusion

Please E-mail me for upgrades, questions, bugs found, etc. Thanks.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralProblem Pin
stevebriz10-Mar-08 2:40
stevebriz10-Mar-08 2:40 
QuestionHow to add in title Pin
et_me11-Sep-07 5:56
et_me11-Sep-07 5:56 
GeneralPrint for Web Pin
akadiwala31-Aug-07 0:15
akadiwala31-Aug-07 0:15 
GeneralRe: Print for Web Pin
Duncan Edwards Jones31-Aug-07 0:56
professionalDuncan Edwards Jones31-Aug-07 0:56 
GeneralRe: Print for Web Pin
akadiwala31-Aug-07 18:24
akadiwala31-Aug-07 18:24 
Generalupdate database from datagird Pin
thanhdienhcm6-Aug-07 19:11
thanhdienhcm6-Aug-07 19:11 
QuestionPrinting issue Pin
singhswat25-May-07 4:41
singhswat25-May-07 4:41 
QuestionPrinting a formatted Datagrid Pin
superberti23-Mar-06 9:03
superberti23-Mar-06 9:03 
Questionadding background to datagrid header Pin
Member 23379705-Oct-05 23:06
Member 23379705-Oct-05 23:06 
Generaldb showing &lt;null&gt; Pin
Member 22270459-Sep-05 7:52
Member 22270459-Sep-05 7:52 
GeneralRe: db showing &lt;null&gt; Pin
yameen19-Oct-05 15:45
yameen19-Oct-05 15:45 
GeneralEmpty Messagebox Pin
kmistic19-Aug-05 9:43
kmistic19-Aug-05 9:43 
GeneralRe: Empty Messagebox Pin
et_me11-Sep-07 5:54
et_me11-Sep-07 5:54 
Generalcombobox in datagrid Pin
atul_moghe17-Aug-05 2:13
atul_moghe17-Aug-05 2:13 
GeneralIndex was out of range Exception Pin
youngster949-Jun-05 11:05
youngster949-Jun-05 11:05 
GeneralRe: Index was out of range Exception Pin
youngster9410-Jun-05 4:39
youngster9410-Jun-05 4:39 
GeneralDataGridColumnStyle Exception Pin
youngster949-Jun-05 9:10
youngster949-Jun-05 9:10 
GeneralRe: DataGridColumnStyle Exception Pin
ogallala14-Jun-07 5:05
ogallala14-Jun-07 5:05 
GeneralWork with Microsoft Access Pin
Yuragel18-May-05 3:11
Yuragel18-May-05 3:11 
Generaltablestyle does not apply Pin
brunko5-Jan-05 8:55
brunko5-Jan-05 8:55 
GeneralRe: tablestyle does not apply Pin
HarSha991-Oct-05 2:06
HarSha991-Oct-05 2:06 
GeneralAlmost but not quite Pin
Anonymous24-Sep-04 2:07
Anonymous24-Sep-04 2:07 
Generaldatagrid Pin
Anonymous12-Sep-04 4:59
Anonymous12-Sep-04 4:59 
Generalwin form datagrid Pin
Pradeep K V7-Sep-04 2:57
Pradeep K V7-Sep-04 2:57 
GeneralPrinting issue Pin
thapa da man20-Jun-04 15:05
thapa da man20-Jun-04 15:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.