Microsoft Windows Forms DataGrid Control: An Introduction

September 5, 2002

Note This document is based on the original spoken WebCast transcript. It has been edited for clarity.

Manisha Gupta: Hello, everyone. Thank you for joining us for today's Support WebCast. This is Manisha Gupta, and also here is my co-presenter Ramesh Thyagarajan, who will be covering half of the presentation.

So let's quickly review the agenda (slide 2) for today. We'll start the session with a brief introduction to Microsoft® Windows® Forms DataGrids, followed by a comparison of the Microsoft Visual Basic® 6.0 and the Windows Forms DataGrid controls. We'll also discuss a few important properties, methods, and events of Windows Forms DataGrids. Then we'll have topics like how to perform design-time and run-time data binding, along with sample code, how to create master-details lists, the parent/child hierarchy, managing the appearance of the DataGrid, and adding controls to the DataGrid. We'll also discuss the most frequently asked questions, followed by a question-and-answer session.

What is Windows Forms DataGrid? (Slide 3) It's a control that displays ADO.NET data in a series of rows and columns. If bound to a data source with a single table containing no relationships, the data appears in simple rows and columns, as in a spreadsheet. If bound to a data source with multiple related tables, and its navigation is enabled on the grid, the grid will display expanders in each row that allow navigation from a parent table to a child table.

It also provides a user interface for a dataset, navigation between related tables, and rich formatting and editing capabilities. You can format by applying border styles, gridline styles, fonts, caption properties, and data alignment, and by alternating background colors between rows. The appearance of the grids can be further formatted by creating DataGridTableStyle and DataGridColumnStyle objects and adding them to their respective collections. This topic will be covered in detail by Ramesh later in our presentation.

The thing to remember is that display and manipulation of data are separate functions. The control handles the user interface, while data updates are handled by the Windows Forms data binding architecture and by ADO.NET data providers.

The following slide (slide 4) has a table that lists Visual Basic 6.0 properties and their Visual Basic® .NET equivalents. The DataGrid control in Visual Basic 6.0 is replaced by the Windows Forms DataGrid control in Visual Basic .NET. The names of some properties, methods, events, and constants are different. In some cases, there are differences in behavior.

The Visual Basic .NET DataGrid control does not need data-specific methods or events because all actions are performed through the data source. Because of the separation of presentation and data functionalities, the data source can be changed with or without user interface input. Also, multiple controls bound to the same data source will always stay in sync. Properties for viewing and navigation in the DataGrid, such as TabAction, EnterAction, AllowArrows, WrapCellPointer, and Scrollable are no longer needed. For example, the grid functions as though the Scrollable property is set to true. If more data exists than can be displayed, a scroll bar appears automatically. Excel-style navigation through the grid is the default, allowing the user to move forward with a TAB key and backward with the SHIFT+TAB key combination. You can get a longer list from MSDN® online (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbconcomparisonofadodatagridandadoplusdatagrid.asp).

In our next two slides we have a list of the important properties, methods, and events that are most commonly used. The CurrentCell property (slide 5) gets or sets which cell has a focus. It's not available at design time. Setting the CurrentCell property will cause the grid to scroll and show the cell if it is not already visible. The CurrentRowIndex property sets the index of the selected row. It allows you to iterate through a parent table's rows, even if you're viewing the child table rows.

The DataSource property sets the data source that the grid is displaying data for. If the DataSource reference contains more than one table, you must set the DataMember property to one of the tables. At the same time, if the DataSource is a DataTable, DataView, collection, or array, setting the DataMember property throws an exception.

The PreferredColumnWidth property is used to set the default width of the grid columns in pixels, but there are a couple of things to remember. First, set this property before resetting the DataSource and DataMember properties, or the property will have no effect. Second, this property cannot be set to a value less than 0.

The PreferredRowHeight property, as the name suggests, is used to set the preferred row height for the DataGrid. The TableStyles property of DataGrid gets the collection of DataGridTableStyle objects for the grid. We use GridTableStylesCollection to create customized views of each table displayed by the DataGrid control, which will be discussed in detail by Ramesh.

Among the commonly used methods, we have the SetDataBinding (slide 6) method that sets the DataSource and DataMember properties at run time. The BeginEdit method is intended to notify the DataGrid controls when the user has begun an editing operation. When the control is in edit mode, multiple edits can be made, and the constants will be temporarily unenforced. Similar to the BeginEdit method, the EndEdit method is intended to notify the DataGrid when an edit operation is ending. It is called to quit the edit mode.

The BeginEdit method is called implicitly when the user changes the value of a data bound control. The EndEdit method is called implicitly when you invoke the DataTable object's AcceptChanges method.

Our next method is the GetCurrentCellBounds method. This gets a Rectangle that specifies the four corners of the selected cell. This can be used when you want to add controls to the DataGrid. We'll use it in our sample code later in the presentation.

Another important method is the HitTest method, which gets information about the DataGrid control at a specified point on the screen, such as the row and column number of a clicked point on the grid, using the x and y coordinates passed to the method. The DataGrid.HitTestInfo class is used in conjunction with a HitTest method to determine which part of the DataGrid control the user has clicked. The HitTest method takes an x and y argument supplied by the DataGrid control's DragDrop, DragEnter, MouseDown, MouseMove, MouseUp, and MouseWheel events. This method will also be discussed in detail by Ramesh.

The following two methods, Select and Unselect, selects or clears a specified row in the DataGrid. By default DataGrid allows multiple selection. For example, you can select the entire row by calling the DataGrid.Select method from within the MouseUp event.

Among commonly used events, we have the CurrentCellChanged event that occurs when the CurrentCell property has changed. For example, to detect when the user of the DataGrid selects a different cell, you can use this event to respond appropriately. KeyDown, KeyPress, and KeyUp events occur when a key is pressed while the control has focus, and they occur in the same order as shown on the slide. The ShowParentDetails event occurs when the ShowParentDetails button is clicked. This is helpful when navigating related DataTables.

Before we enter into the discussion of data binding, I would like to give a brief introduction of the Windows Forms data binding architecture (slide 7). Like I mentioned earlier, the display and manipulation of data are separate functions. The control handles the user interface, while data updates are handled by the Windows Forms data binding architecture and by data providers. Therefore, multiple controls bound to the same data source will stay in sync.

For example, when the grid is bound to a DataSet object, the columns and rows are automatically created, formatted, and filled. Following the generation of the DataGrid control, columns can be added, deleted, rearranged, and formatted as needed.

Windows Forms DataGrid binding allows you to access data from databases, as well as data in other structures, such as arrays and collections. In Windows Forms, you combine to not just traditional data sources, but also to almost any structure that contains data. How the data gets into the structure is not important; you can bind to an array of values that you calculate at run time, read from a file, or derive from the values of other controls.

Furthermore, you can bind any property of any control to the data source. In traditional data binding, you typically bind the display property to the data source. With the .NET Framework, you also have the option of setting other properties we are binding as well. Some examples of what you might use binding for include setting the graphic of an image control, setting the background color of one or more controls, or setting the size of controls. In other words, data binding becomes an automatic way of setting any run-time accessible property of any control on the form.

For Windows Forms data binding to be possible, there must be data providers (slide 8). It is easiest to approach the architecture of Windows Forms data binding from the provider side.

The beginnings of binding for Windows Forms and its controls lie in the providers of data. In Windows Forms, you can bind into a wide variety of structures, from simple arrays to complex data rows, data views, and so on.

At the minimum, a bindable structure must support the IList interface. As structures are based on increasingly capable interfaces, they offer more features that you can take advantage of when data binding.

You can bind to all of the objects listed because they implement the IList interface or a more sophisticated interface that encapsulates the functionality of IList, such as IBindingList or IEditableObject interfaces.

In the case of a DataTable, you're really binding to the table's default view. In the case of DataView, you're binding to a fixed picture of the data rather than a clean, updating data source. As we all know, the DataView object is a customized view of a single data table that may be filtered or sorted. When you bind to a DataSet, you're binding to the DataSet's default DataViewManager. The DataViewManager object is a customized view of the entire DataSet and will log us to a DataView, but with relations included.

For arrays to act as a data source, they must implement the IList interface.

For the DataGrid control to display any data, it should be bound to a data source using the DataSource and the DataMember properties at design time. As you see on the slide (slide 9), the DataSource property is set to DataSet11, which can be created by either using Server Explorer or the tool box in the IDE. You can drag and drop the data components like a Connection object and the respective DataAdapter on the form, or you can create a data connection in Server Explorer and drag the table views entered on to the form. Later you can generate the DataSet by clicking on the Data menu of the IDE and selecting Generate DataSet, which will generate by default DataSet11.

After you have set the DataSource property, you can set the DataMember property by selecting from the list of tables in the combo box, and in this case it's the Orders table.

In the following slide (slide 10) we have sample code that shows how to accomplish data binding at run time. This is accomplished by using the SetDataBinding method. In the following sample code, all you're doing is creating a DataSet object, opening a connection to the server containing the table you wish to see, and creating a data command object. The data command object contains the instructions that the database uses to select, delete, or insert a set of records. In this case, the simple SELECT statement is used, creating a data adapter for the table you wish to retrieve, and then we set the CommandType to CommandType.Text, and then we set the SelectCommand property of the data adapter to the data command object created earlier.

You must instruct the data adapter how to map the retrieved table. This is necessary because the DataSet object may contain tables that are named differently than the name retrieved from the database. For example, if the name of the table is not easily understood, it can be mapped to a friendlier name. Now you can perform the database actions by calling the Fill method on the adapter object, passing the DataSet object. Then you use the SetDataBinding method of the DataGrid control to bind the grids to the DataSet object.

The thing to note here is the SetDataBinding method also requires that you name the table in the dataset that you want to bind to. If you want to use the DataMember property to choose the table, you can leave the quotation marks empty. You do the same for binding the DataView and DataViewManager objects.

If your dataset contains a series of related tables, you can use two DataGrid controls to display the data in a master-detail format (slide 11). One DataGrid is designated to be the master grid and the second is designated to be the details grid. When you select an entry in the master list, all of the related child entries are shown in the details list. As you can see, there are no expanders shown on the master DataGrid. This is done by setting the AllowNavigation property of the DataGrid controls to false. The sample code in our next slide (slide 12) shows how to set a master-details relationship between the customers and orders.