Mar 16, 2009 08:51 AMjaiswarvipinLINK
The IsCurrent property determines which row is the current one. There can be only one current row or no current row if the IsCurrent property is set to null. Most themes visualize the current and the selected rows with the same style although technically IsCurrent and IsSelected are independent of one another. The IsCurrent functionality is related to the keyboard support.
Please Refer below code and detail. THis WIll help you fix error and put more effective way to show the child.
The Concept
While there are a number of examples of showing hierarchical data using the Master/Detail record concept, what I was looking for was the ability to display collapsible child rows under those of their parents in the same grid. Something similar to the way the Winforms DataGrid works when you feed it a hierarchical dataset. Unfortunately the ASP.NET DataGrid will only accept one of the tables within a hierachical dataset in its .DataBind method. In order to work around this we need to create a single DataTable object that contains all the rows we want to show when all the nodes of the hierarchy are expanded. Along the way we will leave ourselves some clues in the DataTable rows that tell us which ones are parent nodes and which are children. Once we have created this specially formatted DataTable we can then bind it to the DataGrid, apply a few formatting functions, and presto; hierarchical data served up in an ASP.NET DataGrid control.
Getting The Data
The first thing we are going to need to get this job done is some hierarchical data. For the purposes of this sample I chose to use the Northwind DB that comes with SQL Server as it is something that most developers have access to (no pun intended). Rather than just a simple parent child relationship I thought we would really push the envelope and try three levels. The tables we are going to work with are the Customers, Orders, and Order Details tables. As usual when accessing data we start by creating a connection object. Seeing as we are connecting to a SQL Server database we can use the SQLClient Namespace and its objects to gain a little performance boost. I have declared a module level variable to contain my Connection object and then call the Open method in the Page's Load Handler.
PrivateSub PageLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load |
The only piece of this code worth remarking on is the line where we set moConn.ConnectionString. All we are doing here is storing the connection string in the Web.Config file and accessing it through the System.Configuration.ConfigurationSettings.AppSettings object. I find this a handy trick so that the assembly doesn't need to be recompiled when the project is redeployed on another machine and the connection string changes. Once we have an open connection we call the LoadResults Sub and the real work begins.
Private Sub LoadResults; Dim oRow As DataRow Dim oTable As DataTable = New DataTable('RESULTS') Dim sSQL As String Dim dOrderTotal As Decimal Dim oDA As SqlClient.SqlDataAdapter Dim oCustomerRow As DataRow Dim oOrderRow As DataRow Dim oDetailRow As DataRow Dim oDS As New DataSet() 'set up columns in the results table oTable.Columns.Add('CompanyName', GetType(System.String)) oTable.Columns.Add('OrderDate', GetType(System.DateTime)) oTable.Columns.Add('ProductName', GetType(System.String)) oTable.Columns.Add('Quantity', GetType(System.Int32)) oTable.Columns.Add('UnitPrice', GetType(System.Decimal)) oTable.Columns.Add('Total', GetType(System.Decimal)) 'send a batch of selects as the data adapters SelectCommand sSQL = 'SELECT * FROM CUSTOMERS WHERE CompanyName < 'C' & _ ' 'SELECT * FROM ORDERS WHERE CustomerID < 'C' & _ 'SELECT [ORDER DETAILS].OrderId, [ORDER DETAILS].Quantity, [ORDER DETAILS].UnitPrice, ' & _ 'ProductName FROM [ORDER DETAILS], PRODUCTS WHERE [ORDER DETAILS].ProductId = PRODUCTS.ProductId' oDA = New SqlClient.SqlDataAdapter(sSQL, moConn) 'map the tables that are returned from the DB to the ones we will create in the dataset oDA.TableMappings.Add('Customers', 'Customers') oDA.TableMappings.Add('Customers1', 'Orders') oDA.TableMappings.Add('Customers2', 'Details') oDA.Fill(oDS, 'Customers') 'set up the relationships oDS.Relations.Add('Customer_Order', oDS.Tables('Customers').Columns('CustomerID'), oDS.Tables('Orders').Columns('CustomerID')) oDS.Relations.Add('Order_Detail', oDS.Tables('Orders').Columns('OrderId'), oDS.Tables('Details').Columns('OrderId'), False) 'loop through the tables getting child rows as necessary For Each oCustomerRow In oDS.Tables('Customers').Rows dOrderTotal = 0 oRow = oTable.NewRow() oRow('CompanyName') = oCustomerRow('CompanyName') oTable.Rows.Add(oRow) For Each oOrderRow In oCustomerRow.GetChildRows('Customer_Order') oRow = oTable.NewRow() oRow('OrderDate') = oOrderRow('OrderDate') oTable.Rows.Add(oRow) For Each oDetailRow In oOrderRow.GetChildRows('Order_Detail') oRow = oTable.NewRow() oRow('ProductName') = oDetailRow('ProductName') oRow('Quantity') = oDetailRow('Quantity') oRow('UnitPrice') = oDetailRow('UnitPrice') 'calculate on the fly oRow('Total') = (oDetailRow('UnitPrice') * oDetailRow('Quantity')) oTable.Rows.Add(oRow) 'add to running total dOrderTotal += oRow('Total') Next Next oRow = oTable.NewRow() oRow('ProductName') = 'Customer Total' oRow('Total') = dOrderTotal oTable.Rows.Add(oRow) Next dgValues.DataSource = oTable dgValues.DataBind() SetHierarchical(False ) End Sub |
Let's walk through this Sub. At the top we declare a number of variables including a DataTable to hold our specially formatted results as well as a DataSet and DataAdapter for pulling the hierarchical data out of the DB. We then set up the columns that we want to include in our 'RESULTS' DataTable that we will eventually bind to the DataGrid. Once this is done we can begin the process of pulling the grandparent, parent, and child records from the database. This is done by assigning a batch of SQL select commands as the SelectCommand property of the DataAdapter. We send one SQL select for each table that we want created in the DataSet. In this case we will issue three, one for each level in our hierarchy. Note that the third SQL select is selecting from two different tables and includes a join between them. This is to include some data from a fourth table that is what we want to include as part of the Order Detail information. Because this data is in a table that has a one to one relationship with the Order Details table we can just lump it together with the other order details information in the DataSet. We then add three TableMappings to the DataAdapter to assign names to the DataTables that will be created in our DataSet. We will call these tables Customers, Orders and Details to represent the three levels in our hierarchy. Once we call the Fill method of the DataAdapter our three tables in the DataSet will be filled with the data that we selected with our three SQL statements. Another minor point is that I have added WHERE clauses in the SQL to limit the amount of data we return from the DB.
Now we have data in our DataSet but how does it know about which tables are parents and which are children? Just like we do in building a relational database we need to create relationships between the table objects. In ADO.NET we do this using the System.Data.DataRelation object. In the next two line of code we add two DataRelations to the DataSets relations collection. The first one is called Customer_Order and makes the CustomerID of the Customers table the parent column while the CustomerID associated with the Orders table is the child column. The second relationship is similar except that it joins the Orders and Details tables in the dataset using their respective OrderID columns. The one other difference is that I have set the CreateConstraints flag to false in the second instance. By default a DataRelation is created with constraints meaning that if a there are keys in the child table that don't exist in the parent table, an exception is thrown. In our case we didn't limit the selection of rows in the Details table but we did in the Orders table, so to avoid the exception we need to set the the CreateConstraints flag to False.
We are now ready to start reading the data out of the DataSet and into our RESULTS table. To do this we use a set of nested loops which iterate through the rows in each table in the DataSet. We create a new row in the RESULTS for each row in the Customers Table and then enter the Orders loop. The interesting thing about this loop is how we get the child rows that pertain to the current Customer row. The method of the DataRow that does this is called, oddly enough, GetChildRows. We pass it the name of the DataRelation that joins these two tables and the DataSet is smart enough to know which rows in the order table belong to the current customer. The same thing happens when we need to loop through the detail rows for each order but this time we pass the Order_Detail DataRelation to GetChildRows. At the end of the Customer loop we add another row that shows a total of dollars spent by that customer that we have been storing in the running total variable dCustomerTotal. You'll notice that for each row in all of the three tables in our DataSet we add another row to our RESULTS table. The difference from one row to the next however is which columns we populate. These are the clues that we will use when we apply our formatting functions to the DataGrid which will give it the functionality that we are looking for.
Format The DataGrid
At the end of the LoadResults Sub we call SetHierarchical, passing it a value of False. This is the Sub where we apply the formatting that will give our DataGrid the look an feel of that lusted after WinForms one. Let's look at the code.
Private Sub SetHierarchical(ByVal bExpanded As Boolean) |
Just because you have called the DataBind method of the DataGrid object doesn't mean that you have to live with the results format-wise. After calling DataBind you can access all the rows of data and change them one by one. It does add a little performance penalty of course but that's a small price to pay for some cool additional functionality. Another trick we have used in this DataGrid is that not all of the columns are databound. Two additional columns have been added to display the plus and minus signs that the user will click to expand and collapse the nodes on our grid. In the SetHierarchical sub we loop through all the rows in the DataGrid and format them based on the clues we added in the rows in RESULTS table. For example, we test to see that there is data in the second cell of each row. If there is then we know that it is a customer row (remember we left the CompanyName column blank in all others when filling the results table). Similarly if the fourth row is filled then it is an Order row. The bExpanded variable is boolean that tells us whether to initialize the grid with all nodes expanded or collapsed. We have passed in False so we will hit the Else part of this If statement. In this section of the code we hide the plus signs on all non expandable rows and most importantly we hide the rows themselves for all rows but those at the top or Customer level. Remember when we set the Visible property of a DataGridRow to False it will not be rendered. This way when the grid is displayed we are only rendering the rows that the user will see while the ViewState will continue to hold all the rows from our RESULTS table that we bound to for later use.
Expand And Collapse
At this point running the code should display the grid in the collapsed position. All that remains is to add the code behind the events that are triggered when the user clicks the plus or minus signs. On closer inspection of the two non bound rows that were added to the grid we discover that they are actually LinkButton columns with the Command name changed to a custom value. The DataGrid object was built with extensibility in mind so it allows you to change the Command name of any button column. Now when the user clicks the button instead of firing one of the prewired command events like the CancelCommand, it fires the more generic ItemCommand. This then is the event handler where we will put our expand and collapse code.
Private Sub dgValuesItemCommand(ByVal source As Object, _ ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgValues.ItemCommand Dim iCount As Int32 = e.Item.ItemIndex + 1 If e.CommandName = 'ExpandOrder' Then If CType(e.Item.Cells(2).Controls(0), LinkButton).Text = '-' Then 'Hide all child rows in the node careful not to hide the Customer row Do While dgValues.Items(iCount).Cells(3).Text = ' ' And dgValues.Items(iCount).Cells(1).Text = ' ' dgValues.Items(iCount).Visible = False iCount += 1 If iCount > = dgValues.Items.Count Then Exit Do Loop 'change the minus to a plus CType(e.Item.Cells(2).Controls(0), LinkButton).Text = '+' Else 'Show all child rows in the node Do While dgValues.Items(iCount).Cells(3).Text = ' ' dgValues.Items(iCount).Visible = True iCount += 1 If iCount > = dgValues.Items.Count Then Exit Do Loop 'change the plus to a minus CType(e.Item.Cells(2).Controls(0), LinkButton).Text = '-' End If ElseIf e.CommandName = 'ExpandCustomer' Then If CType(e.Item.Cells(0).Controls(0), LinkButton).Text = '-' Then 'Hide all child rows in the node DoWhile dgValues.Items(iCount).Cells(1).Text = ' ' dgValues.Items(iCount).Visible = False 'if this is an order row set the plus sign as we are collapsing it too If dgValues.Items(iCount).Cells(3).Text <> ' ' Then CType(dgValues.Items(iCount).Cells(2).Controls(0), LinkButton).Text = '+' End If iCount += 1 If iCount > = dgValues.Items.Count Then Exit Do Loop 'hide the total row dgValues.Items(iCount - 1).Visible = False 'change the minus to a plus CType(e.Item.Cells(0).Controls(0), LinkButton).Text = '+' Else 'Show all Order rows in the node Do While dgValues.Items(iCount).Cells(1).Text = ' ' If dgValues.Items(iCount).Cells(3).Text <> ' ' Then dgValues.Items(iCount).Visible = True iCount += 1 If iCount > = dgValues.Items.Count Then Exit Do Loop 'show the total row dgValues.Items(iCount - 1).Visible = True 'change the plus to a minus CType(e.Item.Cells(0).Controls(0), LinkButton).Text = '-' End If End If End Sub |
As you can see, we test e.CommandName for the string that we set in fig 2 above. For the Customer rows it is ExpandCustomer and for the Order rows it is ExpandOrder. Basically we do the same thing in both sections. If the node has a plus sign then we expand it otherwise, we collapse. Again we use the clues that we left in our data to decipher what type of row it is and take action based on that. There are a few tricky things that need to be done like checking for the last row in the grid and dealing with the total rows but most of this code is pretty simple plumbing.
Please refer the http://www.dotnetjunkies.ddj.com/Tutorial/841522C9-FFBD-4C57-BD48-F62B55057FF3.dcik to gte more details
Grid rows and cells selection in C# from data grid in Windows Forms applications Home >WinForms UI Controls >User Manual >Grid Rows and Cells Selection in C# The KetticGridView also allows the end users to select a single row or cell as well as multiple rows and cells at a time from the data that are shown in grid. UI designers can easily customize the GridView rows and cells selection manually for their C# Windows Forms application. The rows and cells selections allow users to perform selections like, select a single row, select multiple rows, select a single cell, and select multiple cells. How to Select a Single Row in GridView The GridView C#.NET component allows the users perform a single row selection in GridView. To achieve this, we need to change the value of the IsSelected property to True. The simple C# code snippet below shows how to enables the property. It is also possible to achieve this by selecting a single grid row to make it current with the C# code below. The two approaches can add the grid rows to the GridView.SelectedRows collection. How to Select Multiple Grid Rows The GridView C#.NET component allows the users perform multiple rows selection in GridView. To achieve this, we need to change the value of the IsSelected property to True. The C# code below shows how to select multiple grid rows. The above C# code will add the four grid rows to the SelectedRows collection in GridView. To access the instances of the grid rows selected, we can use the simple C# code as following. How to Select a Single Grid Cell The GridView C#.NET component applies the same approach of a single row selection to select cells in GridView. Firstly we need change the value of the IsSelected property to True. And then use the C# code below to select a single grid cell. The grid cell that has been chosen will be added to the GridView.SelectedCells collection. How to Select Multiple Cells in GridView To perform multiple cells selection in GridView, we need change the value of the IsSelected property of to True for all grid cells that will be selected. The C# code below shows how to enable the property to them. The above C# code will add the four grid cells to the SelectedCells collection in GridView. To access the instances of the grid cells selected, we can use the simple C# code as following. | UI Controlsfor Windows Forms .NET WinForms UI Overview.NET WinForms UI Features.NET WinForms UI GuideC# WinForms UI DesignVB.NET WinForms UI Design |