At this point it should be clear that there's good RAD support for binding a simple object to a form for editing. Of course there's also support for binding a collection of objects to a grid for display or editing.
Displaying a collection of objects is relatively straightforward. Making the collection of objects editable takes a bit more effort as we write the code in our classes. In either case, the UI is mostly code-free since we can use RAD data binding techniques like we did with the simple object earlier.
Creating a collection of Product objects is straightforward. In fact it is much easier in .NET 2.0 since we can make use of generic collections. Here's the code:
Imports System.ComponentModel.Collections.Generic
Public Class ProductList
Inherits BindingList(Of Product)
Protected Overrides Function AddNewCore() As Object
Dim prod As Product = Product.NewProduct("", 0)
Add(prod)
Return prod
End Function
End Class
The new BindingList(Of T) generic base class handles almost every detail of creating a strongly-typed collection. We provide the type, in this case Product, and the generic base class automatically ensures that we have strongly-typed methods such as Item and Remove.
The only method for which we need to write code is the AddNewCore method. This method is defined by the base class, and if we want to allow the user to add new items by just navigating to the end of the grid control, we need to override this method. When the user navigates to the last row in a grid control, this method is called to create a new object and add it to the collection. The new object is also returned as the result of the method.
In order to support in-place editing of child objects, we need the child class to implement System.ComponentModel.IEditableObject. This interface hasn't changed from .NET 1.x, and its implementation is beyond the scope of this article. I discussed implementation of this interface in an MSDN article; Windows Forms Data Binding and Objects (http://msdn.microsoft.com/library/en-us/dnadvnet/html/vbnet02252003.asp).
The modified Product class that implements the interface is available in Listing 1, and the changes required to ProductList are in Listing 2. The key is that IEditableObject allows simple undo capabilities, so when the user presses Esc in the grid, the object can revert its state to the values the object had before the user started editing that row in the grid. What makes it more complex is when the user presses Esc to cancel the addition of a new object. In that case, the child object is responsible for removing itself from the collection.
Given the final ProductList and Product classes, we can set up a form with a grid control to view and edit the data. To do this, we'll click the Add New Data Source button in the Data Sources window's toolbar to bring up the wizard. This time we'll select the ProductList class as our data source as shown in Figure 9.

Figure 9. Selecting the ProductList class as a data source.
Now the Data Source window displays both the Product and ProductList classes as possible data sources as shown in Figure 10.

Figure 10. Data Source window displaying both data sources.
Due to the way this pre-beta version of Visual Studio 2005 works, we can't simply drag the ProductList item onto the form and get a meaningful grid display. That is the design goal, but that particular approach doesn't work in the alpha build I'm using. Instead, we need to drag the Product class onto the form, first making sure to use its drop-down menu to select the DataGridView option. The result is a form with a ProductDataNavigator control (the toolbar for VCR-style navigation) and a DataGridView control. Figure 11 shows the form after I've set the DataGridView to dock to the full form.

Figure 11. ProductView form with generated controls.
Notice that the grid columns are set up to match the properties from the Product class. All we need to do now is bind the ProductDataConnector to a ProductList object. Here's the code behind the ProductView form:
Public Class ProductView
Private Sub ProductView_Load( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
Dim list As New ProductList
list.Add(Product.GetProduct("Oak 2x4x8", 3.59))
list.Add(Product.GetProduct("Pine 2x6x8", 10.29))
list.Add(Product.GetProduct("1lb 6 penny nails", 14.39))
ProductDataConnector.DataSource = list
End Sub
End Class
We create a ProductList object and populate it by creating some Product objects. Notice that we're using the new GetProduct factory method, so the Product objects can differentiate between objects already in the grid and ones added dynamically through the AddNewCore method. Remember that the AddNewCore method uses the NewProduct factory method.
Then the ProductDataConnector object's DataSource property is set to the new ProductList object. Since the ProductList object contains a list of Product objects, the data connector is able to take the data from the collection and provide it to the grid control appropriately.
Again, the way this should work is that we'd have a ProductListDataConnector, since we would have dragged the ProductList class directly onto the form to do the binding. In this pre-beta release however, we are using this slight workaround instead.
The end result is that our form displays a grid with the data. The user can edit the data in each row. Pressing Esc on a row while editing will undo the edits. The user can also remove a row by clicking on the left to highlight the row and pressing the Delete key.
We can also add new rows by navigating to the last row in the grid as shown in Figure 12.

Figure 12. Adding a new row to the grid and the underlying collection.
Navigating to another row will commit the new addition. Pressing Esc, on the other hand, will cause the new row to be removed.
Finally, note that the data navigation toolbar is functional. Without having to write any code, we've enabled the user to navigate between rows and to add or remove rows by clicking in the toolbar.
The Save button in the toolbar is disabled. To use it, we'd have to enable it in the form designer and write code behind that button to properly save our object's data to disk.
<< Introduction Conclusion >>