![]() |
Database »
Database »
Data Access
Beginner
License: The Code Project Open License (CPOL)
DataGrid101: Using Windows.Forms DataGridBy omriTutorial on the usage of Windows.Forms.DataGrid |
VB, Windows, .NET 1.0, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
Visual Studio .NET comes with an out-of-the-box grid control called DataGrid. Complete with data-binding and many nifty features, it seems like a handy control. However, once you start using it, you may find that its usage is somewhat cumbersome and - in many real-life situations - downright puzzling. This article attempts to lead the beginner through the ropes of using DataGrid, including:
DataGrid is extremely easy to use when addressing a single table. Its navigation abilities give a very sexy (although I tend to be a skeptic regarding usefulness) view of table relations. In order to avoid the obvious, I designed a simple yet realistic domain with structure and requirements that do not fit the simple binding abilities of DataGrid. I call this domain Cars and I will use it for examples throughout this article:
Car has 3 attributes: licensePlate, carType and price CarType has 2 attributes: name and manufacturer Manufacturer has a nameThis domain can be found in the attached ZIP in 3 forms:
The requirement is simple: write a grid-like screen to manage car prices. A snapshot was given above.
Why does DataGrid binding fall short? DataGrid defines its DataSource as a single IList, be it DataTable or object Array (multiple tables will create a "navigate" interface). While it is easy to infer all data relevant for a car by walking through relations/references, DataGrid column binding does not support a "dot" notation. In other words, when binding to a list of Cars, I can display Car.licensePlate, but not Car.carType.name. Let us walk through 3 possible solutions, each with its pros and cons.
When it comes to data querying, there is nothing easier or more maintainable than using SQL. A simple SQL join statement can feed the relevant data into a DataTable fashioned for our use. Pros: fast, easy, simple and maintainable. Don't stop reading here, because there are several flaws…
An example of this approach can be found in the attached ZIP: SingleQueryCars\SQLJoinBasedForm.vb.
Microsoft tutorials emphasize the DataSet ability to store complex table structures allowing higher client independence and fewer round trips. Once we embrace this philosophy, we are required to join the tables on the client side: no SQL. Unfortunately, .NET does not include a Joiner utility class, so we need to join data in our code. The procedure is quite simple:
DataSet and, in it, a table with the required structure child table, in our case Cars Here is the code:
Dim carRec As DataRow
Dim viewRec As DataRow
For Each carRec In MyCarsDataSet.Tables("Cars").Rows
viewRec = MyGridViewDataSet.Tables("CarView").NewRow
viewRec("license") = carRec("license")
viewRec("type") = carRec.GetParentRow("TypesCars"). _
Item("typeName")
viewRec("make") = carRec.GetParentRow("TypesCars"). _
GetParentRow("ManufacturerTypes").Item("ManufacturerName")
MyGridViewDataSet.Tables("CarView").Rows.Add(viewRec)
Next
Essentially this is the same as what we did in an SQL join. The main benefit is client/server de-coupling, i.e. no server coding for grid purposes. The biggest drawbacks are client-side performance and additional procedural code. An example of this approach can be found in the attached ZIP: JoinBasedCars\LoopBasedJoinForm.vb.
In many cases, it is best to leave DataSets as a link to the underlying DB and perform data manipulation using a class hierarchy (a.k.a. an "Object Domain"). The more logic in your application, the better this approach will serve you. Furthermore, in some cases Object Models are all we have. An example is when our server insists on handing data in Object form. Since "dot notation" is not supported, how can we display properties of anything but our "root objects" (i.e. cars)?
The solution is simple, although it may seem cumbersome at first: we create a new class (usually referred to as the "viewer" class), which wraps the root object and exports all needed data as properties. For instance, in our example we would code a CarViewer class wrapping a Car object and exporting the properties licensePlate, typeName and manufacturerName. We would then bind an ArrayList of these to the DataGrid.
It turns out that this solution is extremely powerful, since it gives us a natural place for a user-interface related code that is non-trivial: things like calculated attributes, complex use cases (how about "switch plates with another car"), etc. In fact, it usually makes sense to use viewer-based grids instead of performing "procedural joins," even if you have the data in a DataSet and not an Object Domain.
Not surprisingly, the author did not invent this concept. It is an adaptation of a well-known paradigm called MVC (Model-View-Controller) and you are welcome to read any of the multitudes of excellent articles available on the Internet. An example of this approach can be found in the attached ZIP: ObjectCars\ViewerBasedForm.vb.
No matter how you perform data binding, if your application views dynamic data you are bound to refresh the grid at some time. It turns out that this is another trivial task that was made "un-obvious." Here's how:
DataGrid CurrencyManager Note the parameter to BindingContext. This is where most people fail. It should be a reference to the exact object you bound.
' Get currency manager
Dim cs As CurrencyManager = _
CType(MyDataGrid.BindingContext(MyCarsDataSet.Cars), _
CurrencyManager)
' Refresh
cs.Refresh()
For all you c#/c++/j# types: CType is the VB.NET cast operator.
Now that we have all relevant data bound, it's a good idea to make it human-readable. Grid formatting is relatively easy, but from the amount of discussion devoted to it in news groups, one can infer that Microsoft did not expose it very neatly. I will try to sort the basics and give some pointers to more advanced stuff.
All grid formatting is centered about the TableStyles collection accessible via the grid property window. Here is how it works:
GridColumnStyles) MappingName property Once you understand this, many of the basic tasks are, well, basic. Here are some examples:
GridColumnStyle orderThe only trick left is to determine the right mapping name:
DataSet DataTable, use the table name as defined in the schema ArrayList) Examples for this can be found in all 3 forms supplied in the attached ZIP. Of course, all these properties are accessible at runtime, allowing easy implementation of features such as "re-arrange columns," "hide column," etc.
Unfortunately, some extremely useful features we expect of a DataGrid are not easily implemented and require more advanced programming. Chief among these are:
Once we understand that the heart of formatting is the DataGridColumn class, then it is evident that to achieve advanced formatting we need to extend it in a manner that fits us. Excellent pointers for such work can be found here (combo in grid).
In real life applications, grids will usually have more than one context menu: column header menu will differ from cell menu; row header menu might differ from both and sometimes the menu may be affected by selection areas. Although DataGrid has only one ContextMenu, managing this kind of behavior is simple enough:
DataGrid.MouseDown event:
DataGrid.ContextMenu according to context Note that this works since your handler is called before the context menu is displayed. Here is an example that shows a context menu only when clicked over a cell. It stores cell coordinates for later use by the context menu handlers:
Private Sub MyDataGrid_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyDataGrid.MouseDown
Dim hi As System.Windows.Forms.DataGrid.HitTestInfo
hi = MyDataGrid.HitTest(e.X, e.Y)
' Test if the clicked area was a cell.
If hi.Type = DataGrid.HitTestType.Cell Then
Me.MyDataGrid.ContextMenu = Me.GridContextMenu
Me.manipulatedRow = hi.Row
Me.manipulatedColumn = hi.Column
' of course I could have saved the whole "hi" structure.
Else
Me.MyDataGrid.ContextMenu = Nothing
End If
End Sub
That's it, folks. The attached ZIP contains 3 projects:
SingleQueryCars is a read-only grid based on an SQL join; it mainly shows column customization. JoinBasedCars has similar functionality, but it also exemplifies client side join of several DataSet tables ObjectCars is a bit more interesting (as I believe this is the best way most of the time); it demonstrates all discussed and a bit more, including:
There are already several .NET grids on the market which look much better than DataGrid and more will come. However, when used correctly, DataGrid can still take you a long way to a usable user interface and it is certainly worth your while experimenting with it further.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 14 May 2003 Editor: Genevieve Sovereign |
Copyright 2003 by omri Everything else Copyright © CodeProject, 1999-2009 Web13 | Advertise on the Code Project |