Dr. Ernest Bonat, Ph.D. founded Visual WWW in 2000. Visual WWW is committed to providing high-quality software business applications and establishing long-term relationships with our clients. We specialize in the design, development, test and implementation of database business applications using Microsoft / Oracle / IBM DB2 / Open Source (LAMP) technologies, including PC-based, Client/Server and Internet web applications.
Ernest is a pioneer in Visual Basic windows development and has been using it since version 1.0 for DOS in 1990. Ernest has more than 20 years experience in computer software development, mathematical modeling and simulation of electronic and microelectronic devices, and manufacturing technological processes, starting from Fortran IV and Basic in 1980, and until today with Microsoft .NET technology. Sometimes he's awake at 4:00 a.m. on Saturday and Sunday mornings working on Visual Basic .NET programming optimization solutions and best practices for his clients and friends.
By Ernest Bonat, Ph.D.
Visual WWW, Inc.
Understanding and programming MySQL data load into the DataGridView control is a must for Windows Application Developers today. Many times, in real production environment the end-users need to see a result set of data to make daily business decisions. The main question is about how many records they need to see when the control gets loaded. The answer of the question is defined by the client's business rules. At this point application performance is a critical issue for Application Developers. In this paper I'll show you how load MySQL data into the DataGridView control using VB.NET bound and unbound programming modes. A timer class named Stopwatch for VB.NET will be used to measure the execution time between theses two programming modes.
MySQL Database Server 5.0.51b
MySQL Connector/NET 5.2.2
Toad for MySQL Freeware 3.1.1
Microsoft Visual Basic 2008 Express Edition
Bound and unbound programming modes are defined based on how the controls are linked to the database table. A control is called bound if some of his database properties are linked directly to a column(s) in a table. This control displays the contents of the column and when the user edits the data in the control, it overwrites the data in the table. An unbound control is not linked to a column in a table. Data load and update for this control is done by the code behind written by the Application Developers. Before Microsoft .NET framework appear, Application Developers tried to develop Windows Client/Server applications using bound controls in Visual Basic 6.0. The data load and update using this programming mode was a very slow and unstable process. Because of that we, Application Developers, decided to develop Client/Server applications in unbound mode. Of course, the development time of these applications increased, but they did run fastest and stable. It was, for us, the only choice at this time. With Visual Studio .NET (2002, 2003, 2005 and 2008) Microsoft continues improving bound Windows controls. One of these controls is the DataGridView control.This control represents a data table view with standard structure of columns and rows. The DataGridView control can be used to load and edit the data at the same time. In this paper, it will be used to show data load techniques only. For some reasons I have not seen many Windows applications developed with editable DataGridView control. Before start looking at the VB.NET programming code, let's look at the code timer class named Stopwatch.
Stopwatch represents a high-resolution code timer class for VB.NET using Kernel32 API functions. The main objective of this class is for precisely measuring how long specific operations take to complete. Because one of my topics in this paper is to compare application performance between bound and unbound modes I have decided to use this class. The Kernel32 API function QueryPerformanceCounter() contains an excellent finer-grained tick counter. This timer boasts near microsecond (0.000001) resolution and is easily callable from any VB.NET Windows applications. Listing 1 shows how to use the Stopwatch object class swatch to measure how long a VB.NET code will run. As you can see a new single instant is started and stopped, and the elapsed time gives us the duration to run the VB.NET code.
Private mTimeDouble As Double Private swatch As New Stopwatch() swatch.Reset() swatch.Start() 'VB.NET code swatch.Stop() mTimeDouble = swatch.ElapsedMilliseconds * 0.001
Listing 1: Stopwatch object class swatch implementation in VB.NET code
Let's look at the loading process of MySQL data into the DataGirdView control using VB.NET bound and unbound programming modes.
To illustrate how to load MySQL data into the DataGridView control a simple VB.NET 2008 project was developed (Figure 1). As you can see, the form contains a DataGridView control with ten columns and two groups of bound and unbound data load buttons (Load 1 and Load 2). Both groups show the execution time in seconds (s) for 20,000 records of MySQL data load.

Figure 1: Bound and unbound DataGridView control project
Let's look at the code of the buttons Load 1 (bound mode) and Load 2 (unbound mode). Listing 2 shows the click event code of the bound Load 1 button. The Using statement creates and destroys the instance of a class UnboundClass (Listing 3). The ADO.NET Connection String mMySQLConnectionString has been passed as a constructor to the class. Just a reminder that this Connection String value is defined and stored in the app.config file as I explained in the article "Define and Store MySQL ADO Connection String in VB.NET 2005". The procedure BoundDataLoading() (Listing 4) is called to load the data. The DataGridView UnboundDataGridView and the TexBox RecordCountTextBox are passed by reference. These procedures is showing below.
Private Sub BoundLoadButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles BoundLoadButton.Click
swatch.Reset()
swatch.Start()
Cursor = Cursors.WaitCursor
Try
Using BoundObject As New UnboundClass(mMySQLConnectionString)
Call BoundObject.BoundDataLoading(UnboundDataGridView, _
RecordCountTextBox, _
mErrorMsgString)
If Not IsNothing(mErrorMsgString) Then
Cursor = Cursors.Default
MessageBox.Show(mErrorMsgString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End If
End Using
Catch exError As Exception
MessageBox.Show(exError.Message, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End Try
Cursor = Cursors.Default
swatch.Stop()
mTimeDouble = swatch.ElapsedMilliseconds * 0.001
BoundTimeTextBox.Text = mTimeDouble.ToString
End Sub
Listing 2: Click event of the bound Load 1 button
Imports MySql.Data.MySqlClient
Namespace UnboundLibrary
Public Class UnboundClass
Inherits ObjectDisposeClass
Private mDataView As DataView
Private mObjectjValue As Object
Private mMySQLConnectionString As String
Public Sub New(ByVal pMySQLConnectionString As String)
mMySQLConnectionString = pMySQLConnectionString
End Sub
End Class
#Region "IDisposable Object ..."
Public Class ObjectDisposeClass
Implements IDisposable
Private disposedValue As Boolean = False
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free unmanaged resources when explicitly called
End If
' TODO: free shared unmanaged resources
End If
Me.disposedValue = True
End Sub
End Class
#End Region
End Namespace
Listing 3: Class UnboundClass with IDisposable implementation
The procedure BoundDataLoading() shown in Listing 4 load the data into the DataGridView UnboundDataGridView with the DataView object mDataView. This object contains the data defined in the user stored procedure `usp_test_select_all` (Listing 5). This stored procedure selects all columns and data from table `test` (Listing 6), and was developed and tested using Toad for MySQL 3.1.1 freeware version from Quest Software, Inc. The table `test` gets loaded with data from the Excel spreadsheet by using the Import Wizard.
The Using statement was applied four times for the following ADO.NET objects: MySqlConnection, MySqlCommand, MySqlDataAdapter and DataSet. This statement allows automatically creating and destroying these unmanaged resources without declaring and initializing these objects. Let's find out how the DataGridView UnboundDataGridView gets formatted and loaded with data.
Public Sub BoundDataLoading(ByRef pDataGridViewControl As DataGridView, _
ByRef pTextBoxRecordCount As TextBox, _
ByRef pErrorMessageString As String)
Dim RecordCountInt32 As Int32
Try
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = "usp_test_select_all"
End With
Using mMySqlDataAdapter As New MySqlDataAdapter(mMySqlCommand)
Using mDataSet As New DataSet
mDataSet.Clear()
mMySqlDataAdapter.Fill(mDataSet, "test")
mDataView = New DataView
mDataView.Table = mDataSet.Tables("test")
End Using
End Using
End Using
End Using
RecordCountInt32 = mDataView.Count
pTextBoxRecordCount.Text = FormatNumber(RecordCountInt32.ToString, 0)
Call DataGridViewBound(pDataGridViewControl)
pDataGridViewControl.DataSource = mDataView
Catch exError As Exception
pErrorMessageString = exError.Message
End Try
End Sub
Listing 4: Procedure BoundDataLoading() for data loading into the DataGridView UnboundDataGridView
DROP PROCEDURE IF EXISTS `vwww`.`usp_test_select_all`; CREATE PROCEDURE `usp_test_select_all`() BEGIN SELECT `id`, `column2`, `column3`,`column4`, `column5`, `column6`, `column7`, `column8`, `column9`, `column10` FROM `test` ORDER BY `id`; END;
Listing 5: User stored procedure `usp_test_select_all` to select all columns and data from table `test`
DROP TABLE IF EXISTS `vwww`.`test`; CREATE TABLE `test` ( `id` int(20) NOT NULL auto_increment, `column2` varchar(50) NOT NULL, `column3` varchar(50) NOT NULL, `column4` varchar(50) NOT NULL, `column5` varchar(50) NOT NULL, `column6` varchar(50) NOT NULL, `column7` varchar(50) NOT NULL, `column8` varchar(50) NOT NULL, `column9` varchar(50) NOT NULL, `column10` varchar(50) NOT NULL, PRIMARY KEY (`id`), KEY `ix1_test` (`column2`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Listing 6: Table `test` definition
The procedure DataGridViewBound() formats the DataGridView UnboundDataGridView as shown in Listing 7. First the grid is formatted itself and then column by column. This standard code is used to format the DataGridView control before it gets loaded with data.
Private Sub DataGridViewBound(ByRef pDataGridViewControl As DataGridView)
With pDataGridViewControl
.AlternatingRowsDefaultCellStyle.BackColor = Color.LightYellow
.DefaultCellStyle.SelectionBackColor = Color.LightBlue
.SelectionMode = DataGridViewSelectionMode.FullRowSelect
.RowTemplate.Height = 17
.AllowUserToOrderColumns = True
.AllowUserToDeleteRows = False
.AllowUserToAddRows = False
.ReadOnly = True
.MultiSelect = False
.Columns.Clear()
Dim ColumnID As New DataGridViewTextBoxColumn
With ColumnID
.DataPropertyName = "id"
.Name = "ID"
.HeaderText = "ID"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(ColumnID)
Dim Column2 As New DataGridViewTextBoxColumn
With Column2
.DataPropertyName = "Column2"
.Name = "Column2"
.HeaderText = "Column2"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column2)
Dim Column3 As New DataGridViewTextBoxColumn
With Column3
.DataPropertyName = "Column3"
.Name = "Column3"
.HeaderText = "Column3"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column3)
Dim Column4 As New DataGridViewTextBoxColumn
With Column4
.DataPropertyName = "Column4"
.Name = "Column4"
.HeaderText = "Column4"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column4)
Dim Column5 As New DataGridViewTextBoxColumn
With Column5
.DataPropertyName = "Column5"
.Name = "Column5"
.HeaderText = "Column5"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column5)
Dim Column6 As New DataGridViewTextBoxColumn
With Column6
.DataPropertyName = "Column6"
.Name = "Column6"
.HeaderText = "Column6"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column6)
Dim Column7 As New DataGridViewTextBoxColumn
With Column7
.DataPropertyName = "Column7"
.Name = "Column7"
.HeaderText = "Column7"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column7)
Dim Column8 As New DataGridViewTextBoxColumn
With Column8
.DataPropertyName = "Column8"
.Name = "Column8"
.HeaderText = "Column8"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column8)
Dim Column9 As New DataGridViewTextBoxColumn
With Column9
.DataPropertyName = "Column9"
.Name = "Column9"
.HeaderText = "Column9"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column9)
Dim Column10 As New DataGridViewTextBoxColumn
With Column10
.DataPropertyName = "Column10"
.Name = "Column10"
.HeaderText = "Column10"
.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
End With
.Columns.Add(Column10)
End With
End Sub
Listing 7: Procedure DataGridViewBound() for formatting the DataGridView UnboundDataGridView in unbound mode
The unbound programming mode of developing business database applications has been the most popular for Windows Application Developers. In .NET Framework, as I explained before, Microsoft tremendously improved the Windows and Internet bound controls. The main idea behind this effort was to decrease development time and increase application stability and flexibility. We already know from previous years that using unbound application development in VB 6.0 increases performance and stability. Here is the real question: Can we get the same results by using bound controls in VB.NET 2008? Unfortunately I don't have enough development references from developers and friends at this point. I can show you that the bound DataGridView is faster than the unbound. Let's look at the code and execution time. Listing 8 shows the click event code of the unbound Load 2 button. This code is similar to the code of the bound Load 1 button (Listing 2). The only difference is that the procedure UnboundDataLoading() (Listing 9) gets called at this time.
Private Sub UnboundLoadButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles UnboundLoadButton.Click
swatch.Reset()
swatch.Start()
Cursor = Cursors.WaitCursor
Try
Using UnboundObject As New UnboundClass(mMySQLConnectionString)
Call UnboundObject.UnboundDataLoading(UnboundDataGridView, _
RecordCountTextBox, _
mErrorMsgString)
If Not IsNothing(mErrorMsgString) Then
Cursor = Cursors.Default
MessageBox.Show(mErrorMsgString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End If
End Using
Catch exError As Exception
MessageBox.Show(exError.Message, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End Try
Cursor = Cursors.Default
swatch.Stop()
mTimeDouble = swatch.ElapsedMilliseconds * 0.001
UnboundTimeTextBox.Text = mTimeDouble.ToString
End Sub
Listing 8: Click event of the bound Load 2 button
Listing 9 shows the procedure UnboundDataLoading() for loading the data into the DataGridView UnboundDataGridView by using the same user stored procedure `usp_test_select_all` (Listing 5). In this case the ADO.NET mMySqlDataReader object was created to loop through it and get data loaded into the DataGridView row by row. First of all, in unbound mode, to format the DataGridView (Listing 7) the property DataPropertyName is not required at all. So the procedure DataGridViewUnbound() is the same as DataGridViewBound() without setting the property DataPropertyName. The one-dimensional array CellsArrayObject() is used with Object data type to locally store any data from the MySQL data reader mMySqlDataReader by using the GetValues() method. The entire row is added to the DataGridView control with the Add method of the property Rows. The variable RecordCountInt32 counts how many rows have been added to the DataGridView control.
Public Sub UnboundDataLoading(ByRef pDataGridViewControl As DataGridView, _
ByRef pTextBoxRecordCount As TextBox, _
ByRef pErrorMessageString As String)
Dim CellsArrayObject() As Object
Dim RecordCountInt32 As Int32
Try
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = "usp_test_select_all"
End With
Using mMySqlDataReader As MySqlDataReader = _
mMySqlCommand.ExecuteReader(CommandBehavior.SingleResult)
With mMySqlDataReader
If .HasRows Then
Call DataGridViewUnbound(pDataGridViewControl)
ReDim CellsArrayObject(.FieldCount - 1)
While .Read
.GetValues(CellsArrayObject)
pDataGridViewControl.Rows.Add(CellsArrayObject)
RecordCountInt32 += 1
End While
pTextBoxRecordCount.Text = FormatNumber(RecordCountInt32.ToString, 0)
End If
End With
End Using
End Using
End Using
Catch exError As Exception
pErrorMessageString = exError.Message
End Try
End Sub
Listing 9: Procedure UnboundDataLoading() for data loading into the DataGridView UnboundDataGridView
Table 1 shows the execution time data load results for bound and unbound DataGridView control. The shown values are measured in seconds using the code timer class Stopwatch. As you can see the bound mode offers a much better performance than the unbound for different amounts of records.
| Modes / Records | 1,000 | 3,000 | 5,000 | 10,000 | 20,000 |
| Bound | 0.116 | 0.209 | 0.317 | 0.622 | 1.281 |
| Unbound | 0.708 | 1.796 | 2.946 | 5.894 | 11.643 |
Based on the execution time data load results provided in this paper for bound and unbound DataGridView control, a simple conclusion can be made: Bound DataGridView control implementation provides a good performance in MySQL/VB.NET Windows Applications development. Unbound DataGridView control is not necessary to be implemented.
To download the source codes and a PDF format for this article go to Visual WWW Downloads.
Read and post comments on this article in the MySQL Forums. There are currently 6 comments.
