|
Introduction
For those that don't know me, I run a website about the ASP.NET Datagrid control, http://www.datagridgirl.com/. Inevitably, my first article here at CodeProject also focuses on the Datagrid control. I also enjoy answering Datagrid questions here at CodeProject, and around the web. A common question that comes up involves grouping similar data, and providing a visual cue to the user when a repeating data element has changed.
The Standard Datagrid
In a typical Datagrid, all columns are treated "equally". For example, you may wish to display Sales by Date, and to meet that end, you might include an ORDER BY Date clause. However, if the end user is primarily interested in seeing how these sales vary by date, then this standard implementation might not make that obvious enough to them, as shown:

The Separated Datagrid
A better approach might be to add a separator row between each new group of sales for a given day, as seen at the top of the article. To do that, take advantage of two Datagrid events, ItemDataBound, and PreRender. We'll also need two variables public to the page: Public LastDateValue As DateTime = Convert.ToDateTime("1/1/1901")
Public NewValues(100) As String
ItemDataBound
During the grid's ItemDataBound event, detect whether the current row has a SalesDate that varies from the previous row. If it is different, store the value in the NewValues array, and update the public variable for the LastDateValue. You could also do some other manipulation here during the ItemDataBound event. For example, if you wanted to also display a total for each date, that would be calculated during this event. Private Sub dgSales_ItemDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _ Handles dgSales.ItemDataBound
If e.Item.ItemType = ListItemType.Item Or _ e.Item.ItemType = ListItemType.AlternatingItem Then
NewValues(e.Item.ItemIndex) = ""
If e.Item.DataItem("SalesDate") <> LastDateValue Then
LastDateValue = e.Item.DataItem("SalesDate")
NewValues(e.Item.ItemIndex) = String.Format("{0:D}", _ e.Item.DataItem("SalesDate"))
End If
End If
End Sub
PreRender
First off, I'm a fan of casting sender back to a Datagrid, and working with that rather than simply referring to the Datagrid by its ID. That makes it easier if you ever copy/paste this code to another Datagrid---errr, I mean, if you move this code into a reusable component--yeah, that's what I meant. Anyway. You're already aware that the Datagrid ultimately renders as a <table> in HTML, but you may not have realized that you can access that table on the server-side using DG.Controls(0). This table object is useful for several last-minute formatting tricks, including this one. Private Sub dgSales_PreRender(ByVal sender As Object, _ ByVal e As System.EventArgs) _
Handles dgSales.PreRender
Dim DG As DataGrid = CType(sender, DataGrid)
Dim Tbl As Table = DG.Controls(0)
Dim DGI As DataGridItem
Dim Cell As TableCell
Dim i As Integer, iAdded As Integer = 0
For i = 0 To NewValues.GetUpperBound(0)
If NewValues(i) <> "" Then
DGI = New DataGridItem(0, 0, ListItemType.Header)
Cell = New TableCell
Cell.ColumnSpan = 3
Cell.Text = NewValues(i)
DGI.Cells.Add(Cell)
Tbl.Controls.AddAt(i + iAdded + 1, DGI)
iAdded = iAdded + 1
End If
Next
End Sub
Conclusion
With a few tricks, the Datagrid can help provide rich information to your client user base. Don't by limited by the standard out-of-the-box output from this control - use the Datagrid's events to format the output to your heart's desire, and provide a more useful user interface.
An Alternate Approach
This problem can also be solved by nesting another Datagrid within the main Datagrid, binding the outer grid to a list of dates, and the inner grid to the sales, filtered for that date. I may cover this technique in a future article.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 41 (Total in Forum: 41) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Hi - I have a grid and have applied the separtor code as described in this article
However, on my actual data rows I have a button which then goes into view the details of the current record.
On the button click we get the items ID froma label in the grid as follows:
CType(e.Item.FindControl("lblScheduleID"), Label).Text
We then pass this ID to the new page to retrieve the records associated with it
However, the item indexes seem all out of alignment - so if I have rows as follows:
Separator 1 Item 1 Item 2 Item 3 Separator 2 Item 4 Item 5 etc
If I select Item 1 the item index is 0 and so it seems to then access separator 1 when do e.item.findcontrol - this falls over.
If I select Item 3 the index is 2 and we then get the ID from Item 2 since the indexes are out of aligment
Any suggestions?
many thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I need to validate the data in the cell of datagrid as soon as i leave the cell.. for example : u cannot have numbers in name.. but my column type is varchar so its accepting numbers also.. i have tried currentCellChanged event but some how it dosnt meet my requirement... i tried Keypress and Keydown events but satisfy the requirement... pls help me..
Ram vara prasad
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Dear Marcie , i have insert Datagridseparator in e-commerce website and works fine but i have now problem... I have this problem : When i insert product in shopping cart the rows inserted not correspond to row that i select.. For example i click on products of rows number five and insert rows of products ten... Excuse me for bad English. Please help me ... Thanks a lot. Giuseppe
This is my Addcart Routine :
Sub AddCart(ByVal Source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Dim objTextBox = e.Item.FindControl("txtQtaArt")
If e.CommandSource.CommandName = "addtocart" Then
Dim objConnection1 As SqlConnection = New SqlConnection(Application("sqlConnection"))
Dim objCommand1 As SqlCommand = New SqlCommand objCommand1.Connection = objConnection1
objCommand1.CommandText = "INSERT INTO tblShoppingCart(UserID, CodiceArt, Produttore, Descrizione, Quantita, Prezzo)" & _ "VALUES(@UserID,@CodiceArt,@Prd,@Descri,@Qt,@Prz)"
objCommand1.Parameters.Add(New SqlParameter("@UserID", SqlDbType.VarChar, 4)) objCommand1.Parameters.Item("@UserID").Value = Session("IdCliente")
objCommand1.Parameters.Add(New SqlParameter("@CodiceArt", SqlDbType.VarChar, 21)) objCommand1.Parameters.Item("@CodiceArt").Value = (e.Item.Cells(1).Text)
objCommand1.Parameters.Add(New SqlParameter("@Prd", SqlDbType.VarChar, 16)) objCommand1.Parameters.Item("@Prd").Value = (e.Item.Cells(2).Text)
objCommand1.Parameters.Add(New SqlParameter("@Descri", SqlDbType.VarChar, 120)) objCommand1.Parameters.Item("@Descri").Value = (e.Item.Cells(4).Text)
objCommand1.Parameters.Add(New SqlParameter("@Qt", SqlDbType.VarChar, 4)) objCommand1.Parameters.Item("@Qt").Value = objTextBox.Text
objCommand1.Parameters.Add(New SqlParameter("@Prz", SqlDbType.Float, 8)) objCommand1.Parameters.Item("@Prz").Value = (e.Item.Cells(5).Text)
Try
objConnection1.Open() objCommand1.ExecuteNonQuery()
objConnection1.Close() Catch Response.Write("Spiacenti ma ci sono alcuni problemi tecnici") End Try
Totali() 'aggiorna totali
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hello I have some questions about the use of Datagrids. At first, I have an ArrayList with object how can i load it into a Datagrid?(In my case i have an ArrayList containing objects with 2 members, a name and a value. Name can be anything, but it's value is a predefined type, like picture, file, String, number etc.). The second one is, I want one of my columns to contain different things, like when my Arraylist contains a file type attribute when i click on the value cell I want an openfileDialog to apear, but if i have an picture type, when clicked on the value cell i want a PictureDialog to apear, or when I have a date in want a DateTime picker to apear. Thank you. Sincerly, Szabo Jozsef.
just a n00b
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
Well, it may look nice and such, but it's pretty useless as far as I can tell if you don't use auto generated columns - once I switch to my own definition of columns (TemplateColumn), a row insert is not doing anything visible.. Dunno if I'm doing anything wrong, but I tried to add a row in both PreRender and ItemCreated - it works when AutoGenerateColumns=true, but not otherwise..
Btw, what's the deal with creating an array of 100 elements and then filling only those items that have indexes correspondent to rows that will need to be inserted, and leaving the rest empty??? This looks a bit strange, or is it just me? I always thought there are other ways of doing this.. Or maybe I am overengineering here, I don't know...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
I have a datagrid that displays financial information. Using your example as a base I was able to insert divider rows at specific points in the first grid control.
(divider) Income Statement 2004 2003 2002 Gross Income X X X Pretax Income X X X Net Income X X X (divider) Balance Sheet 2004 2003 2002 Assets X X X etc
What I'd like to do is if the numbers in a row are zero, then don't display the row. The problem is if I exclude them at the database level, then the divider rows will be off. When I try with PreRender, the data has not been bound yet so I don't know what the numbers are. How can I go through the final table and hide the correct rows? Do I have to use JavaScript or is there some equivalent of PostRender?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I found a better way to do it. First on the database end I included which group it's part of and an isAdmin variable so the data returned was like:
Income Statement, Gross Income, X1, X2, X3 WHERE X1 > 0 OR X2 > 0 OR X3 > 0 OR isAdmin > 0
You see this module could either display the data or edit it all at once. The isAdmin variable returns all the rows even if the data is 0s. Then in the page after the datagrid was bounded (binded?) I went through the table and added the seperator rows when the group_id changed.
iCounter = 0 Group_ID = 1 'we're starting with the first group because the datagrid seems to put a header row in by default For Each thisRow In tbFinancialPivot.Rows iCounter += 1 If Group_ID <> Convert.ToInt16(thisRow("Group_ID")) Then Group_ID = Convert.ToInt16(thisRow("Group_ID")) DividerRow(Convert.ToString(thisRow("Group_Name")), iCounter) iCounter += 1 End If Next
The DividerRow subroutine adds the row with the first parameter being the text and the second being where in the table to stick it. This row had to be custom built using DataGridItem and TableCell and datagrid.controls(0). Since we're adding divider rows to the table, we have to count them as they are added. For example the 5th row becomes the 6th. It involved a bit of hard coding, but nothing that can't be changed or made dynamic if the need arises.
Hope I'm clear and that this helps people in similar situations
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I handled this slightly differently and I am currious of your thoughts. I have a public function as follows.
private dim strLastName as string = string.empty
Public function DesnameBinder(strDesName as string) as string if strDesName <> strLastName then strLastName = strDesName return "<br><a class='subheading'>" & strDesName & "</a><br>" else return nothing end if End function
In my ascx file in the datagrid item template I put this. <%# DesnameBinder(DataBinder.Eval(Container.DataItem, "DesignerName")) %> Do you see any problems with this. I works well.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
the code above is able to place the added table row at the appropriate place but it is not rendering the cell inside the row. i.e it is redering as <tr> </tr> instead of <tr><td>category1 </td></tr>
Any ideas are greatly appreciated.
Thanks, Anand
p.s i used exactly what you had given in the c# code
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
just for the record. i am also getting the same problem.
has anyone found a solution to this problem yet?
M
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
As i couldn't find any solution to this, i've tried another way and it works : 1) in the page_load, before the databinding, i modify the original DataSet : each time i need to place a subheader i create a new DataRow with my title and a special value to detect such a row. 2) the modified dataset is used as the datagrid's datasource. 3) in the ItemDataBound event, i read each row to detect my subheader : i modify the item rendering by removing some cells and changing ColumnSpan
I couldn't find any other example where someone adds some new datagrid items but MS provides many samples where they modify the existing ones. I don't know if there is a reason for that ... If you want more details, just ask me.
regards Xavier
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Setting ShowHeader to "true" helped for me.
In PreRender you can hide the main Header with:
Tbl.Controls(0).Visible = False
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
| I had the exact same problem. the "ShowHeader="True"" method made no difference for me. What I DID find what the problem was being caused by a hidden column I had. When it was the first column, the header was never drawn, just the " | " part. Once I moved the hidden column to the last position, everything worked perfectly.
-Roamer
| Sign In·View Thread·PermaLink | 5.00/5 (2 votes) |
|
|
|
 |
|
|
I set the demo project however if I do a postback I lose all of the rows of the second group plus the headers. Has any else had problems with this? Should the datagrid keep the ViewState?
R
|
| Sign In·View Thread·PermaLink | 2.33/5 (3 votes) |
|
|
|
 |
|
|
Not sure what I'm doing wrong, but at this point...
tbl.Controls.AddAt(i + iAdded + 1, dgi);
I'm getting -- System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index
Any Clues appreciated, I've banged on this for like 2 hours trying to figure out what's wrong and all the code looks correct.
Jim
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I need add a new header (title) but i have the problem with the viewstate. the code its ok, but when click in ORDER, haev problem with view state,
protected override void OnPreRender(EventArgs e) { DataGridItem dgitem = new DataGridItem (0, 0, ListItemType.Header) ; TableCell tblCell = new TableCell(); tblCell.ColumnSpan = this.Columns.Count; tblCell.Text = "Title"; dgitem.Cells.Add(tblCell); this.Controls[0].Controls.AddAt (1, dgitem); base.OnPreRender (e); }
Fernando Finelli
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
That works really well, however if you just want to do simple grouping on an item in the actual parsed table you can use this:
private void DATAGRID_PreRender(object sender, System.EventArgs e) { DataGrid dgMain = (DataGrid)sender; Table tblMain = (Table)dgMain.Controls[0]; string strPreviousValue = "";
for(int intCount = 0; intCount < tblMain.Controls.Count; intCount++) { DataGridItem objItem = (DataGridItem)tblMain.Controls[intCount]; if (objItem.ItemType == ListItemType.Item || objItem.ItemType == ListItemType.AlternatingItem) { string strValue = objItem.Cells[INDEX_OF_CELL].Text; if (strValue != strPreviousValue) { strPreviousValue = strValue; DataGridItem dgiNew = new DataGridItem(0, 0, ListItemType.Item); TableCell tdNew = new TableCell(); tdNew.ColumnSpan = objItem.Cells.Count; tdNew.Text = strValue; dgiNew.Cells.Add(tdNew); tblMain.Controls.AddAt(tblMain.Controls.IndexOf(objItem), dgiNew); } } } }
Not as advanced, but less code
|
| Sign In·View Thread·PermaLink | 5.00/5 (2 votes) |
|
|
|
 |
|
|
Lately I have been using the DataGrid with AutoGenerateColumns="true" and I have been running into some problems with my text wrapping. Even when I set both the Header-Style and Item-Style wrap properties to False, the data still wraps. Upon examing the html put out by the webserver, I noticed that the definiton for the row is correct as <tr nowrap="nowrap"...." however every cell is simply <td>Data</td>. If I place the nowrap inside the cell tag, as so <td nowrap>Data</td> then it finally doesn't wrap. However I would like to have the datagrid do this for every cell programatically as obviously hand editing isn't an option. Is there a callback or some other way to set data inside the cell tag itself? is there another setting for cells that's overriding the <tr nowrap> somewhere else? There is an article in MSKB about a BUG with this, however it only states how to deal with it if you're explicitly defining your columns, which I don't intend to. Thanks
Chuck Lane Software Developer
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Greetings DataGridGirl. Thanks for the excellent example. I do have a question though. On post back the text in the separator rows is lost. Does adding the rows in the prerender event exclude them from the viewstate? If so, can I add the rows another way? Thanks a bunch!
Buill
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|