Click to Rate and Give Feedback
Related Articles

This month we demonstrate how easy it is to use IronPython to test .NET-based libraries.

James McCaffrey

MSDN Magazine June 2009

...

Read more!

Cobra, a descendant of Python, offers a combined dynamic and statically-typed programming model, built-in unit test facilities, scripting capabilities, and much more. Feel the power here.

Ted Neward

MSDN Magazine June 2009

...

Read more!

We demonstrate creating a peer-to-peer processing platform where multiple players function together for a common purpose: getting your work done.

Matt Neely

MSDN Magazine June 2009

...

Read more!

Memory usage can have a direct impact on how fast an application executes and thus is important to optimize. In this article we discuss the basics of memory optimization for .NET programs.

Subramanian Ramaswamy and Vance Morrison

MSDN Magazine June 2009

...

Read more!

This column shows you how to secure the .NET Services Bus and also provides some helper classes and utilities to automate many of the details.

Juval Lowy

MSDN Magazine July 2009

...

Read more!

Also by this Author

AJAX Extenders extend the behavior and features of ordinary Web controls so you can reduce postbacks and control input even better than with AJAX alone.

Dino Esposito

MSDN Magazine January 2008

...

Read more!

Achieving cross-browser compatibility for events is no easy task. The jQuery event handling API addresses the differences in event handling across browsers, allowing you to write more predictable JavaScript.

Dino Esposito

MSDN Magazine April 2009

...

Read more!

In this month's installment we build modal and modeless dialog boxes in jQuery and explain how to post data from them to the Web server.

Dino Esposito

MSDN Magazine May 2009

...

Read more!

Choosing the right design pattern for your ASP.NET Web application can help you achieve the separation of concerns between your presentation layer and the layers beneath it.

Dino Esposito

MSDN Magazine December 2008

...

Read more!

This month Dino Esposito explains how the browser interoperability layer in Silverlight addresses a number of your Silverlight / Web page interaction needs.

Dino Esposito

MSDN Magazine November 2008

...

Read more!

Popular Articles

Writing a Web application with ASP.NET is unbelievably easy. So many developers don't take the time to structure their applications for great performance. In this article, the author presents 10 tips for writing high-performance Web apps. The discussion is not limited to ASP.NET applications because they are just one subset of Web applications.

Rob Howard

MSDN Magazine January 2005

...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

Josh Smith

MSDN Magazine July 2008

...

Read more!

C# allows developers to embed XML comments into their source files-a useful facility, especially when more than one programmer is working on the same code. The C# parser can expand these XML tags to provide additional information and export them to an external document for further processing. This article shows how to use XML comments and explains the relevant tags. The author demonstrates how to set up your project to export your XML comments into convenient documentation for the benefit of other developers. He also shows how to use comments ...

Read more!

Cutting Edge
A New Grid Control in Windows Forms
Dino Esposito

Code download available at: CuttingEdge0504.exe (191 KB)
Browse the Code Online
Grid controls are essential in many of today's apps. Until now, though, most developers using Visual Basic® have had to buy third-party components to get an effective, easy to use grid component. The Windows® Forms DataGrid turned out to lack too many features for the average developer. Third-party grid controls are often more feature-rich than anything found in a system framework like the Microsoft® .NET Framework. But for in-house and personal applications that don't need a professional quality control, the new DataGridView in the forthcoming .NET Framework 2.0 is a viable alternative.
The DataGridView control is the successor to the Windows Forms DataGrid version 1.x. Once you have dropped the control onto a Windows Form, you'll see the popup window shown in Figure 1. You can declaratively set a number of features—add new rows, edit and delete the current row, reorder columns, bind the control to its data, and style it as needed.
Figure 1 DataGridView Smart Tag 
The DataGridView class allows customization of cells, rows, columns, and borders through properties such as DefaultCellStyle, ColumnHeadersDefaultCellStyle, CellBorderStyle, and GridColor. This alone exceeds the capabilities of the Windows Forms DataGrid version 1.x.
You can use a DataGridView control to display data with or without an underlying data source. If you don't specify a data source, you can create columns and rows that contain data and add them directly to the DataGridView using the control's user interface. Alternatively, you can set the usual DataSource and DataMember properties, bind the control to a data source, and have it automatically populated with data.
The DataGridView eliminates the most commonly reported snags of the previous version, including data caching. In version 1.x, there are no special capabilities for working with a very large amount of data. You have to either bind the control to the whole DataSet or implement a hand-written caching system. In the .NET Framework 2.0, the DataGridView control can work in virtual mode. Setting the VirtualMode property to true enables the control to display a subset of the available data (you can also use VirtualMode to provide a mix of bound and unbound columns).
You create a DataGridView with a set number of rows and columns and then handle the CellValueNeeded event to populate the cells. Virtual mode requires the implementation of an underlying data cache to handle the population, editing, and deletion of DataGridView cells based on actions of the user. As you can see, the mechanism is not entirely automatic but is much simpler than in version 1.x.
Data binding takes advantage of some new IDE features available in Visual Studio® 2005. As you can see in Figure 2, any data-bound Windows control can be bound to a variety of data sources—server databases such as SQL Server, local databases like Microsoft Access or SQL Server 2005 files, Web service methods, or custom business objects. The object that gets bound to the control is always a collection; the preceding data sources are just the storage media. Visual Studio 2005 generates classes and code to contain bindable data. In doing so, it requires you to select the data entities to use from the selected source.
Figure 2 Data Source Config Wizard 
Figure 3 shows the dialog box for picking up a table from a Microsoft Access database. Around that table, Visual Studio 2005 would construct an XSD schema file to represent a typed DataSet. In addition, the typed DataSet would be empowered with additional methods to load data on demand, optionally in parameterized way.
Figure 3 Declaring the Data Source 
The wizard and the options shown in Figure 2 and Figure 3 might change before the .NET Framework 2.0 ships. That's why I'll just give you the big picture here. To show you the new DataGridView control in action, I'll examine a handwritten data layer in which ADO.NET classes retrieve any data and custom collection objects convey data to the grid control.

Building a Simple Data Layer
A key enhancement in the .NET Framework 2.0, generic types are especially useful for creating custom collections in a snap. In .NET, generics are programming elements that can work with a variety of data types. You specify the data type when you declare the element. Among other things, generics can be used to create strongly typed collections. Here's an example:
Public Class EmployeeCollection 
       Inherits Collection(Of Employee)
End Class
Suppose you want to create and bind an array of objects representing employee data. You load database records from an Employee table into an instance of the Employee class and group employees together within a custom collection (shown in Figure 4). To create a custom collection in .NET Framework 1.x, you derive a new class from CollectionBase and override a couple of members—at the very minimum, the indexer property Item and the method Add. This action requires you to write a great deal of code, and the actual collection is only apparently typed. In reality, the custom collection is an ArrayList object, and anything you add to it, or get from it, is cast to the proper type before use.
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.OleDb

' Class designed to hold employee data
Public Class Employee
    Private _id As Integer
    Private _name As String
    Private _title As Integer
    Private _country As Integer

    Public Property ID() As Integer
        Get
            Return _id
        End Get
        Set(ByVal value As Integer)
            _id = value
        End Set
    End Property
    ... // other properties similar
End Class

' Collection of customers
Public Class EmployeeCollection : Inherits BindingList(Of Employee)
End Class

Public Class DataHelper
    Public Function Load() As EmployeeCollection
        Dim ds As New DataSet
        Dim adapter As New OleDbDataAdapter("SELECT * FROM Employees", _
            MySettings.Value.Connection)
        adapter.Fill(ds)

        Dim rows As New EmployeeCollection
        For Each row As DataRow In ds.Tables(0).Rows
            Dim e As New Employee
            e.ID = Int32.Parse(row("employeeid"))
            e.Name = row("lastname").ToString()
            e.Title = Int32.Parse(row("title"))
            e.Country = Int32.Parse(row("country"))
            rows.Add(e)
        Next

        Return rows
    End Function
End Class
Generics provide a dual advantage when you're moving data across tiers. You write much less code, and the code you do write is easier to maintain. Performance is significantly improved because no boxing is required for value types and no cast is performed in order to access the object in a strongly typed fashion.
Figure 4 shows a simple data layer expressed as a custom collection and a load method. The data is captured using ADO.NET classes and then placed into individual Employee objects for binding to the grid. Should you go with the "manual" approach described here, or are you better off exploiting the design-time capabilities of Visual Studio 2005? Get the best of both worlds: compose all of your low-level ADO.NET code into a class with a few public methods to perform the usual database operations. Next, bind this intermediate class to the control using the Object option shown in Figure 2.
The following code snippet shows how to fill a EmployeeCollection object and bind it to an instance of the DataGridView control:
Dim loader As New DataHelper()
Dim data As EmployeeCollection = loader.Load()
DataGridView1.DataSource = data
Figure 5 shows the grid in action. It doesn't look much different from an old-style DataGrid control.
Figure 5 The DataGridView Control in Action 

Customizing the DataGridView Control
Can you spot a drawback in the grid shown in Figure 5? The titles and countries are listed through numbers instead of more intelligible names. On the other hand, those numbers are the visible sign of a sensible data schema. Those numbers, in fact, represent foreign keys from cross-referenced tables. From the database design point of view, so far so good, but what about grid rendering? You need to transform those numbers into names—the country, the title, or whatever else they might stand for. In the .NET Framework 1.x, you first create a custom column type through an ad hoc class and then you add it to the grid's column collection. The DataGridView control has both enhanced capabilities and a richer set of built-in column types.
Figure 6 Editing the Columns of the DataGridView Control 
Figure 6 shows how to change the column type and all the choices available. By default, a grid column is rendered using a textbox column just as in version 1.x. To effectively render foreign keys, you need a combobox column bound to a distinct data source and a different pair of display/value members. The dialog in Figure 6 lets you change the column type to DataGridViewComboBoxColumn. Once you've done so, you can add a few extra properties to the column class to fill the combobox with both bound and unbound values. For example, you can set the DataSource property of the DataGridViewComboBoxColumn to the object that contains the bound items—all the titles and the countries to choose from. The DisplayMember property indicates the field to display through the combobox; ValueMember indicates the field to get and set the real value on the master table. Figure 7 illustrates the result.
Figure 7 Managing Foreign Keys in a DataGridView Control 
In read-only mode, the combobox doesn't drop down and is limited to showing the bound value. (You can also make use of the combobox column's DisplayStyle property here to hide the drop-down arrow. This makes the UI look even better.) In edit mode, use the combobox to select the new value for the cell. If you have to change the country cell, you can pick up the new value from a list of country names rather than from a meaningless sequence of numbers.
In addition to combobox and classic textbox columns, you can also have image, link, and button columns. Link and button columns display their items as clickable elements. Each click on the contents of an item is bubbled up as a CellContentClick event.
Each column can have its own set of visual properties as shown in Figure 8. You can control padding, colors, fonts, and borders. Column settings can be set declaratively, programmatically on a per-column basis, or for all the columns at once through the DefaultCellStyle property.
Figure 8 Setting the Styles of a Column 
The following code snippet sets the background color for alternating rows to override the default background set for all cells. In addition, it sets the Format property on the HireDate column to cause the bound date object to be formatted as "Month, Year."
grid.DefaultCellStyle.BackColor = Color.LightGray;
grid.AlternatingRowsDefaultCellStyle.ForeColor = Color.White;
grid.AlternatingRowsDefaultCellStyle.BackColor = Color.Black;
grid.Columns["HireDate"].DefaultCellStyle.Format = "y";
The width of each column can be set in a relative manner, unlike in Windows Forms 1.x grids. Aside from assigning a column a given number of pixels, you can require that the column determine its width automatically and adjust it based on the displayed data. The property AutoSizeCriteria can be set to any of the following values: None, HeaderOnly, Rows, HeaderAndRows, and HeaderAndDisplayedRows. HeaderOnly and Rows indicate that the column should be wide enough to display the entire header or the largest row. HeaderAndRows takes into account the width of the header and that of the largest row. Finally, HeaderAndDisplayedRows considers only the displayed rows and the header.

Cell Templates
If the column types supported out-of-the-box by the new Windows Forms DataGridView control don't meet your needs, you can create your own column types with cells that host controls of your choice. Creating custom column types is possible in version 1.x; it's as easy as deriving a class from a known base class. In the .NET Framework 2.0, things are even easier because of a new infrastructure for templating.
One thing you won't find in the Beta 1 list of column types—the set is subject to change by the time the platform ships—is a calendar column to display and edit dates in an easy fashion. To create a calendar column, you must define a class that derives from DataGridViewColumn and extend it with a custom cell template. The cell template is the area that is occupied by individual grid cells. The content of the cell template depends on the user interface of the column. Link columns contain a hyperlink, standard columns include a textbox, combo columns make up their user interface with a combobox. A custom column populates the cell template with any combination of Windows controls. The cells of this column display dates in ordinary text cells, but when the user edits a cell, a DateTimePicker control appears, as in Figure 9. Let's see how to create a calendar column.
Figure 9 The Custom Calendar Column in Action 
In Figure 10, the CalendarColumn class inherits from DataGridViewColumn and overrides the CellTemplate property. The CellTemplate property returns a DataGridViewCell object that represents the content of the cell. As Figure 10 demonstrates, the CalendarColumn constructor uses a made-to-measure cell object—the CalendarCell class.
Public Class CalendarColumn : Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New CalendarCell())
    End Sub

    Public Overrides Property CellTemplate() As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(ByVal value As DataGridViewCell)
            If Not (value Is Nothing) And _
              Not value.GetType().IsAssignableFrom( _
              GetType(CalendarCell)) Then
                Throw New InvalidCastException("Must be a CalendarCell")
            End If
            MyBase.CellTemplate = value
        End Set
    End Property
End Class
CalendarCell derives from the DataGridViewTextBoxCell class to avoid having to reimplement textbox display functionality. Alternatively, you can make it inherit from DataGridViewCell. The code for the CalendarCell class is split in two parts. One includes public properties such as the format of the date. The other portion of the code is the view editing control; that is, a class that derives from Control and implements the IDataGridViewEditingControl interface. This control provides the user interface when a particular cell enters into edit mode. For the CalendarCell, this user interface consists of a DateTimePicker control. For more details, take a look at the source code available for download from the link at the top of this article.
How can you use this new column with a DataGridView control? Once you bring the new column class into the project, it automatically becomes visible in the dropdown list of available column types. You can assign the calendar type to a column declaratively in the Visual Studio 2005 designer or proceed programmatically, as shown here:
Dim col As New CalendarColumn()
col.HeaderText = "Hire Date"
col.DataPropertyName = "HireDate"
Me.DataGridView1.Columns.Add(col)
The date picker control shows up only if the grid is working in edit mode. If the column is marked as read-only, the date picker remains hidden and the cell displays only static text.

The Data Connector
In the code download for this article, you'll see that the sample DataGridView is bound to a particular type of data source. A data source can be an enumerable data object or a data connector. The DataGridView accepts any of the following interfaces: IList, IListSource, IBindingList, IBindingListView.
IList and IListSource are old acquaintances to developers experienced in .NET. They are implemented by collections and special ADO.NET objects such as DataTable and DataSet. IBindingList has also been around since the .NET Framework 1.0 and derives from IList, adding support for change notification, AddNew semantics, and sorting (the new BindingList<T> generic class implements IBindingList). IBindingListView, new to the .NET Framework 2.0, further extends the IBindingList interface to add advanced sorting and filtering capabilities. The DataConnector class implements the IBindingListView interface.
In Windows Forms 2.0, the DataConnector component, renamed to BindingSource in Beta 2, is the data source object of choice; through it you can bind to a variety of data sources. You can bind the control to a data connector and have the connector component, in turn, bind to another data source or populate with data using a business object. The DataConnector binds to a physical data source through the DataSource and DataMember properties, as shown in the following code:
Me.EmployeesDataConnector.DataMember = "Employees"
Me.EmployeesDataConnector.DataSource = Me.MyEmployeesDataSet
When you connect a data source to a DataGridView control, Visual Studio 2005 creates three elements—the data connector, a data adapter, and a typed DataSet. The data adapter executes any query that fills the typed DataSet. The typed DataSet, in turn, is wrapped by the connector and populates the bound control.
The data connector provides a layer of abstraction between a Windows form and its data. All further interaction between form and data, including navigating, sorting, filtering, and updating, is performed through calls to the DataConnector.
As mentioned, a DataConnector encapsulates data and provides members for accessing that data. The Current property retrieves the current item; the property List retrieves the entire list. Currency management is handled automatically, but a number of related events are exposed anyway to allow for further customization. These events include CurrentItemChanged, CurrentChanged, DataSourceChanged, ListChanged, and BindingComplete.
It is worth noting that data sources bound to a DataConnector component can also be managed through the DataNavigator class. DataNavigator offers a VCR-like user interface for navigating the items of a list and performing a few common operations (Insert new, delete, update) on the currently selected record.

Sorting
The DataGridView class provides advanced sorting capabilities. Each column is given a SortMode property that determines whether the column is sortable or not. By default, all columns are sortable. To change this, set the SortMode property of a given column to NotSortable. You can do that both programmatically and declaratively at design time. The SortMode property also accepts two other values: Automatic (the default) and Programmatic. In both cases, sorting takes place in the default way, alphabetically. If automatic sorting is enabled, the sort direction glyph is displayed; otherwise, you have to show it yourself.
If the grid is configured for programmatic sorting, you have to rely on the sorting capabilities of the data source object in a data-bound scenario. If you're using a grid not bound to any data source, then you need to take care of the sorting yourself through the Sort method, shown here:
Dim titleComp As New TitleComparer
DataGridView1.Sort(titleComp)
The Sort method performs its magic by taking an instance of a class that implements the IComparer interface and determines the new order by comparing pairs of values.

Conclusion
All data-driven Windows and Web applications require a grid component. Grids need a good deal of extra functionality that the Windows Forms DataGrid control you get from the .NET Framework 1.x lacks. In the .NET Framework 2.0, the DataGridView control makes up for some of the limitations of the previous version. You'll find an improved user interface with more column types, more customization options, and even a virtual working mode that gets data only when required.
In this column I've only just scratched the surface of the DataGridView control with a sneak preview of the features that will be available with the next version of the .NET Framework. Note that the code and the discussion is based on the Beta 1 release and therefore all of the details are subject to change before the final release. In addition, you can almost certainly expect more features by the time the new version of the Windows Forms framework ships. Stay tuned.

Send your questions and comments for Dino to  cutting@microsoft.com.


Dino Esposito is a Wintellect instructor and consultant based in Italy. Author of Programming ASP.NET and the new book Introducing ASP.NET 2.0 (both from Microsoft Press), he spends most of his time teaching ASP.NET/ADO.NET classes and speaking at conferences. Get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.

Page view tracker