Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Create a Custom DataGridView Column : Page 4

Creating custom column types for the DataGridView control isn't nearly as tricky as it once was.

Finishing the Column
That takes care of the cell part of the story. To finish creating the column, add a default constructor to the DataGridViewBarGraphColumn class, and modify the code so that it sets the column's CellTemplate property, and forces the column to be read only:

[Visual Basic] Public Class DataGridViewBarGraphColumn Inherits DataGridViewColumn Public Sub New() Me.CellTemplate = _ New DataGridViewBarGraphCell() Me.ReadOnly = True End Sub Public MaxValue As Long Private needsRecalc As Boolean = True Public Sub CalcMaxValue() End Sub End Class [C#] public class DataGridViewBarGraphColumn : DataGridViewColumn { public DataGridViewBarGraphColumn() { this.CellTemplate = new DataGridViewBarGraphCell(); this.ReadOnly = true; } public long MaxValue; private bool needsRecalc = true; public void CalcMaxValue(){} }

Finally, you need to provide a way for the column to calculate its maximum value. Because the column really doesn't provide any simple way to hook itself into the parent control's set of events, the simple solution is to have a procedure (CalcMaxValue) that each cell calls as it's being painted. Of course, you don't really need to calculate the maximum value for each cell; it's just that there's no other obvious and simple self-contained location in which to place code that you're guaranteed won't run until all the data has been loaded. (There may be other solutions to this particular problem, but the technique shown here is relatively benign, and works fine.)

Modify the CalcMaxValue procedure so that it scans the values in the current column, tracking the maximum value. When the procedure is done, it sets the NeedsRecalc field to false, so that subsequent calls to the procedure don't calculate the maximum value again:

[Visual Basic] Public Sub CalcMaxValue() If needsRecalc Then Dim colIndex As Integer = Me.DisplayIndex For rowIndex As Integer = 0 To _ Me.DataGridView.Rows.Count - 1 Dim row As DataGridViewRow = _ Me.DataGridView.Rows(rowIndex) MaxValue = Math.Max(MaxValue, _ CLng(row.Cells(colIndex).Value)) Next needsRecalc = False End If End Sub [C#] public void CalcMaxValue() { if (needsRecalc) { int colIndex = this.DisplayIndex; for (int rowIndex = 0; rowIndex < this.DataGridView.Rows.Count; rowIndex++) { DataGridViewRow row = this.DataGridView.Rows[rowIndex]; MaxValue = Math.Max(MaxValue, Convert.ToInt64(row.Cells[colIndex].Value)); } needsRecalc = false; } }

You could make the needsRecalc field public, so that other callers could reset it. If, for example, you wanted to allow users to change values in the column, you would need to force a recalc of the maximum value. It's also possible that you could trap cell-changing events of the parent grid, and force a recalculation if a value in the corresponding column changed. I'll leave that as an exercise for the reader!

Figure 3. Column Type: Select your new class type, specifying the type of column you'd like in your grid.
That's it—that's all you need. You've indicated that the template for cells in this column should use the DataGridViewBarGraphCell class, that this column should be read only, and added code that calculates the maximum value of the column.

Back in your form's Design view, edit the columns in the DataGridView control. For the UnitsInStock column (or whatever numeric column you're using), select the DataGridViewBarGraphColumn type, as shown in Figure 3. Click OK, then save and run your project. If all goes well, you should see a DataGridView that includes a bar graph column as shown in Figure 1.

Of course, you're not limited to just this sort of customization. You can inherit from any of the various cell types, including columns that display Button, CheckBox, ComboBox, Image, or Link controls. If you inherit from the correct class, you'll find that most of the work has been done for you. In addition, each base class provides many protected methods you can override, giving you the capability to create exactly the cell type that you need. As in any such project, knowledge of the classes in the System.Drawing.Drawing2D namespace really comes in handy—take some time to investigate the classes you've seen in this example, if you haven't worked with them before. You'll certainly use them again.

Ken Getz is a senior consultant with MCW Technologies and splits his time between programming, writing, and training. He specializes in tools and applications written in Visual Studio .NET and Visual Basic. Ken is the author of the highly rated .Finalize() column in CoDe Magazine. He is also the co-author of several best-selling books, including Access 2002 Developer's Handbooks with Paul Litwin and Mike Gunderloy, Visual Basic Language Developer's Handbook with Mike Gilbert, and VBA Developer's Handbook with Mike Gilbert (Sybex). He co-wrote several training courses for Application Developer's Training Company, including VB.NET, ASP.NET, Access 2000 and 97, Visual Basic 6, and Visual Basic 5 seminars. He has also recorded video training for AppDev covering VB.NET, ASP.NET, VB6, Access 2000, and Access 97. Ken is a frequent speaker at technical conferences and has spoken often at Microsoft's Tech-Ed conference. Ken's also a technical editor for Access-VB-SQL Advisor magazine and a columnist for Informant Publications' asp.netPRO magazine..
Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date