3.16. Use a Better Data-Bound Grid
The DataGrid that shipped with .NET 1.0 and 1.1 had a slew of limitations. It was difficult to customize, nearly impossible to extend, and had no support for important features like modifying or filling the DataGrid programmatically, accessing individual cells, or applying per-cell formatting. In many cases, VB developers avoided the DataGrid altogether and used third-party grids or even older COM-based controls like the MSFlexGrid. (In fact, third-party component developers regularly thanked Microsoft for making enhanced grid components an easy sell.)
Note: . NET's DataGrid was a significant disappointment in an otherwise state-of-the-art framework. Now the Windows Forms team fills in the gaps with a first-rate grid.
In designing .NET 2.0, the Windows Forms team decided it would be nearly impossible to remedy the shortcomings without breaking backward compatibility. So, they chose to introduce an entirely new DataGridView control with support for all the missing features and more.
3.16.1. How do I do that?
You can bind the DataGridView to a DataTable object in the same way that you would bind a DataGrid. Here's the bare minimum code you might use to bind a table named Customers:
DataGridView1.DataSource = ds DataGridView.DataMember = "Customers"
Of course, to put this code to work, you need to create the DataSet object ds and fill it with information. For a complete example that adds the necessary ADO.NET code for this step, refer to the downloadable content for this chapter.
When you use this code, the DataGridView creates one column for each field in the data source, and titles it using the field name. The grid also has a significant amount of out-of-the-box functionality. Some of the characteristics you'll notice include:
Before going any further with the DataGridView, there are two methods you'll want to consider using right away: AutoResizeColumns( ) and AutoResizeRows( ). AutoResizeColumns( ) extends all columns to fit header text and cell data. AutoResizeRows( ) enlarges the row with multiple lines to fit header text and cell data (the DataGridView supports automatic wrapping). Both of these methods accept a value from an enumeration that allows you to specify additional options (such as extending the column just to fit all the columns, or just the header text):
' Create wider columns to fit data. DataGridView1.AutoResizeColumns( _ DataGridViewAutoSizeColumnsMode.AllCells) ' Create multi-line columns to fit data. DataGridView1.AutoResizeRows( _ DataGridViewAutoSizeRowsMode.HeaderAndColumnsAllCells)
You can also use the AutoResizeColumn( ) and AutoResizeRow( ) methods to change just a single column or row (specified as an index number).
Once you have created a DataGridView and populated it with data, you can interact with it through two useful collections: Columns and Rows. The Columns collection exposes a collection of DataGridViewCell objects, one for each column in the grid. You can set the order in which columns are displayed (by setting an index number in the DisplayIndex property), hide a column altogether (set Visible to false), or freeze a column so that it always remains visible even as the user scrolls to the side (set Frozen to true). You can also modify the column header text (HeaderText), the size (Width), and make it non-editable (ReadOnly). To look up a column, use the index number or the corresponding field name.
For example, here's the code you need to change some column properties in the OrderID column of a bound DataGridView:
' Keep this column visible on the left at all times. DataGridView1.Columns("CustomerID").Frozen = True DataGridView1.Columns("CustomerID").DisplayIndex = 0 ' Configure the column appearance. DataGridView1.Columns("CustomerID").HeaderText = "ID" DataGridView1.Columns("CustomerID").Resizable = DataGridViewTriState.True DataGridView1.Columns("CustomerID").MinimumWidth = 50 DataGridView1.Columns("CustomerID").Width = 50 ' Don't allow the values in this column to be edited. DataGridView1.Columns("CustomerID").ReadOnly = True
The Rows collection allows you to access individual DataGridViewRow objects by index number. Once you have a DataGridViewRow, you can examine its Cells collection to look up individual values in that row.
However, it's more likely that you'll want to access just those rows that correspond to the current user selection. The DataGridView actually provides three related properties that can help you:
For example, if you're using DataGridViewSelectionMode.FullRowSelect, you can use the following code to retrieve the current selection and display a specific field from each selected row when the user clicks a button:
Note: You can control the type of selection that's allowed by setting the DataGridView.SelectionMode property. Different values allow selection for individual cells, rows, or columns. DataGridView.MultiSelect determines whether more than one item can be selected at a time.
Private Sub cmdSelection_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdSelection.Click For Each SelectedRow As DataGridViewRow In DataGridView1.SelectedRows MessageBox.Show(SelectedRow.Cells("CustomerID").Value) Next End Sub
For a full example that puts all of these ingredients together, refer to the BetterDataGrid example in the downloadable samples.
3.16.2. What about...
...doing more with the DataGridView? The features described so far provide a snapshot of DataGridView basics, but they only scratch the surface of its customizability features. For more information, refer to the following two labs in this chapter (Section 3.17 and Section 3.18).