ASP.NET GridView Linking to another GridView - asp.net

Ok here is another thing is currently stumping me and I can not seem to find an answer that fits my need. On an aspx page I have a Grid View that gets it data from a SQL query and populates the Grid View, which is working fine. On this grid view though the first column (Column 0) has a line number that was returned from the query. There could one line or 10 or more lines in the grid view data that is returned.
What I am trying to do is add a hyperlink/LinkButton/Button (Not sure which) to the grid view in a column before the line number. I currently have a TemplateField with an asp link button which i can get to show up.
What I can't seem to figure out, and I have been looking for a couple of days now, is how to pull the cell with the line number to pass to my code behind (VB.NET) to have it run a secondary query that will populate another grid view with the line item details.
Here is how I currently have the gridview set up:
<asp:GridView ID="gvDetailSecondLevel" runat="server" AutoGenerateColumns="false" Width="1010px" CellPadding="4" ForeColor="#333333" GridLines="Horizontal" CssClass="SecLvlDtl" OnRowCommand="gvDetailSecondLevel_RowCommand">
<Columns>
<asp:TemplateField ItemStyle-VerticalAlign="Middle">
<ItemTemplate>
<asp:LinkButton ID="btnLineDetail" runat="server" CssClass="dtlButtons" CommandName="Edit" Text="Line Detail" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Line #" HeaderText="Line #" />
<asp:BoundField DataField="Part Desc" HeaderText="Part Description" />
<asp:BoundField DataField="PCS" HeaderText="PCS" />
<asp:BoundField DataField="WT" HeaderText="WT" />
<asp:BoundField DataField="SF" HeaderText="SF" />
</Columns>
The rest of the grid view is formatting so I did not list it for brevity.
The current code behind (Which I am sure is wrong because it throws an error) is as follows:
Protected Sub gvDetailSecondLevel_RowCommand(sender As Object, e As GridViewCommandEventArgs)
'Setup Variables
Dim lineNumber As String = gvDetailSecondLevel.Rows(sender.RowIndex).Cells(0).Text
'Make controls visibile
btnClear.Visible = True
SOThirdLevel.Visible = True
'Retrieve Thrid Level SQL Data
bindThirdLevel(lineNumber)
End Sub
The error that gets thrown says "Public Member 'RowIndex' on type 'GridView' not found"
I should also mention that the second grid view is in a div tag that is on the same page. I am not trying to jump to another apsx page. (I can do that just fine)
So what I need is to figure out how to get the line number cell data passed to the code behind so that I can process it.
Anyone have any thoughts?
Thanks in advance for any help or ideas.

To get your row index, you need to use the GridViewCommandEventArgs object (e). Here is some code to get what you need:
Convert.ToInt32(e.CommandArgument)
Also, cells(0) will give you the cell containing your linkbutton. So, the line that gets the row number will look like this:
Dim lineNumber As String = gvDetailSecondLevel.Rows(Convert.ToInt32(e.CommandArgument)).Cells(1).Text

This is how I finally got it to work.
'Get RowIndex
Dim rowIndex As Integer = Convert.ToInt32(e.CommandArgument)
'Reference Grid View Row
Dim row As GridViewRow = gvDetailSecondLevel.Rows(rowIndex)
'Get Cell value
Dim lineNumber As String = row.Cells(1).Text
I was able to then pass the variable lineNumber to my sub and it works great now!
Thanks Wenadin for the pointer!

Related

ASP.NET bound hyperlink as SQL Server stored procedure parameter?

I've currently got a gridview that is populated with summary data. I've created a hyperlink from a bound field labeled ticket_num. What I'm wanting to accomplish is to click the hyperlink and have that call a stored procedure. I need to pass that text of that hyperlink into the stored procedure in SQL Server. So the flow is something like this...
User clicks link
The text of that hyperlink is passed into a parameter for the SQL Server stored procedure to use
Call the stored procedure and display results on new page
Any ideas? The stored procedure is created, connection into the server via ASP.NET is created. Everything works thus far but I can't figure this piece of it out.
Well, since you want some code to execute, then LITTLE reason exists to use a hyper-link, since that not what you need nor want.
You don't mention if you prefer to "hide" the URL and hyper link anyway? (often for security, this is not all such a bad idea).
so, if you need to hide/not show/don't want the value in the grid, then our simple button click can get the row id of the grid (also hidden), and then get the value, and then pass to sql server.
Or, you can add to the button the URL or value to the button - just use command argument. That way you don't have to hit the database again based say on row PK id.
So, here is a simple grid. I have the URL as a row on the grid, but lets also shove/put it into the button for you to see how this works.
So, simple grid:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table" >
<Columns>
<asp:BoundField DataField="Fighter" HeaderText="Fighter" />
<asp:BoundField DataField="Engine" HeaderText="Engine" />
<asp:BoundField DataField="Thrust" HeaderText="Thrust" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="Preview">
<ItemTemplate>
<asp:Image ID="Image2" runat="server" ImageUrl = '<%# Eval("ImagePath") %>' Width="140" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn"
CommandArgument = '<%# Eval("ImagePath") %>' OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And our code to load up the above:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGridF()
End If
End Sub
Sub LoadGridF()
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM Fighters", conn)
conn.Open()
Dim rstData = New DataTable
rstData.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = rstData
GridView1.DataBind()
End Using
End Using
End Sub
And now we have this:
Note how we have a PLAIN JANE button - after all, it not really a hyper link we need, is it?
So, for our button click event, we have this:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
Debug.Print("Row index click = " & gRow.RowIndex)
' get database row PK id
Dim iPK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
' now we have database row PK - walk the dog, do payroll processing
' do ANYTHING we want like get the database row etc.
Debug.Print("Data base row PK id = " & iPK)
Debug.Print("Command button arugment = " & btn.CommandArgument)
End Sub
And output is then:
So, you can see, we did not need (or want a hyper link), but a simple button, and that click event can:
get any value from the given grid row
get the database PK id (hidden - never exposed to client side (good security).
get the row click index
get parameters passed to button
And we can use gRows.Cells() to get the other row values (for data fields)
And of course gRow.FindControl("ctrl name") for templated columns.

How to handle nulls with Eval

I can have a record with all filled in fields and then without an SO_ID or SO_Num. I want my eval to be able to handle these and just return a '-' in the grid column when this happens while still returning all other data for that row. I've tried other solutions online and couldn't find one that works.
<dx:GridViewDataColumn FieldName="SO_Num" VisibleIndex="19" runat="server" Caption="Sales Order Number">
<DataItemTemplate>
<a id="clickElement" href="../sales/order/view.aspx?ID=<%# Eval("SO_ID").ToString()%>"><%#Eval("SO_Num").ToString()%></a>
</DataItemTemplate>
</dx:GridViewDataColumn>
You can use a in-line "iif(), and thus this:
<asp:TemplateField HeaderText="City">
<ItemTemplate>
<asp:TextBox ID="txtCity" runat="server"
Text = '<%# IIf(IsDBNull(Eval("City")), "-", Eval("City")) %>'
></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
So, in above, if say Eval("City") is null, then we display a "-", else we display Eval("City")
Edit: display of values and some button to click and navgate are DIFFERENT!!
As I pointed out, if you need a Eval() in the GridView, and want to convert a null say into a "-", then do that (but, I fail to see why I would want to display some "-" in the GV. Why do that?? Seems rather strange to me?
However, if you have a button on the GV row, and you want to click on that button to jump or navigate to some other page? Fail to see how such a button click and navigate has ANY REALATIONSHIP to what we display? Why are the two concepts connected? I fail to see any sensible logic here?
If you want to drop in a pane jane button, or even a link button (no difference here), then wire up a click event for that given button you drop in.
so, say in our GV, we drop in a button to view a given row of data. Say this GV with hotels, and a button.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
CssClass="table table-hover" Width="50%"
DataKeyNames="ID" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:TemplateField HeaderText="Hotel Name">
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server"
Text='<%# Eval("HotelName") %>' >
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn "
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle CssClass="pagenavi" />
</asp:GridView>
so, we just dropped in a plane jane button for operations on that one row.
So, our code to load is this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL As String =
"SELECT * from tblHotelsA ORDER BY HotelName"
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
Dim rst As New DataTable
rst.Load(cmdSQL.ExecuteReader)
GridView1.DataSource = rst
GridView1.DataBind()
End Using
End Using
End Sub
And our results are now this:
Ok, so now lets wire up that plane jane button click.
As a normal rule, you can double click on a button to build the click event, or bring up the property sheet, choose events tab/section, and then add the click event. However, since the button is in the GV, then we have to add the click event this way (in the markup).
Type in OnClick=, and when you hit the "=" sign, intel-sense will popup a dialog to create the event.
You get this:
So, we select create new event. Don't seem like anything occurred, but flipping to code behind, we have a click event stub, and our code thus can be this:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row click index = " & gRow.RowIndex)
Debug.Print("Row click database PK id = " & intPKID)
' now do whatever you want with this row information.
' to get values from non templated columns, use cells()
' to get values from tempated columns, use findcontrol
'eg:
' get last name (Boundfield)
Debug.Print("Last name = " & gRow.Cells(1).Text)
' get hotel name - template - "label"
Dim lblHotel As Label = gRow.FindControl("txtHotel")
Debug.Print("Hotel name (label) = " & lblHotel.Text)
End Sub
output:
So, as noted, I fail to see why ANY issue occurs here in regards to some data in a column of the GV being null?
In your case, just navigate based on the button click to anything you want, based on any value you want.
say like this:
<asp:TemplateField HeaderText="View">
<asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn "
OnClick= "cmdView_Click"
CommandArgument = '<%# Eval("SO_ID") %>' />
</asp:TemplateField>
And then in code behind:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row click index = " & gRow.RowIndex)
Debug.Print("Row click database PK id = " & intPKID)
Dim intSOID = btn.CommandArgument
Dim strURLJumpTo = "../sales/order/view.aspx?ID=" & intSOID
Response.Redirect(strURLJumpTo)
So, you are free to cook up any URL navagation you want.
NOTE VERY close how I used the data keys feature of the GV. That allowed me to have, use, get, play with the database PK row id, but NEVER do I have to expose or show or have or mess with the database PK id in the actual GV markup.
This is not only nice, but also is a HUGE deal from a security point of view, since then the user, the browser (client side) thus NEVER has to see, or know or can munge or play with the database PK row id - it is 100% server side managed.
And in fact if you SO_ID or whatever values are not in the GV, or the user does not care? Then I would NOT pass the values in the URL as so called "query parms" of the URL, but would in fact pass the values in session() like say this:
Session("OrderID") = intPKID
Response.Redirect("../sales/order/view.aspx")
Then in the page load event of the target page, I do this:
and NEVER EVER forget to check/use the ispostback in your page load event.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
ViewState("OrderID") = Session("OrderID")
Now, in that code behind, and that page, you use this to get/use/have the orederid
dim intOrderID = ViewState("OrderID")
So, on page load (first page, ispostback = false), you transfer the session() value to the ViewState. And you do this since the user might have more then one copy of the browser running - and thsu using session() to pass the value is ok, but session() is global to the ONE user, where as ViewState is per page. So, that's why we transfer to ViewState on page load, since FROM THAT POINT onwards in that page, and code behind, we use ViewState. If we used session() in the code behind, then it is global, and if more then one copy of the browser is running or even multiple tables, they will all have the same session() value.
So, say you click on a house to view or buy?
Well, then they might open another tab - display same GV, and click on a different row. If we use session, you now display two pages - but both have the one and same row PK id value - and if you click buy house, you get the wrong house if your code behind uses session(), but with ViewState, the issue does not exist.
And thus, you can even dump your ugle "id" and parameters out of the URL - they look much nicer, but are also much more secure, and the user thus does not have to see, or know about things such as database row PK junk and stuff.

How do I put data from database to hidden field?

I call stored procedure and then store given data in this datatable variable
DataTable data = CallToSP();
In this DataTable there are 3 columns -> CustomerId, CustomerName, CustomerSecondaryId;
I am displaying two of them -> CustomerId and CustomerName.
My goal is to make a hidden field in .aspx file and get it's value from CodeBehind.
What's unclear to me is that I do not understand how data is mapped.
Project I'm currently working on uses TelerikUI. It has RadGrid.
RadGridViewCustomer.DataSource = data;
RadGridViewCustomer.DataBind();
foreach (GridDataItem dataItem in RadGridViewCustomer.MasterTableView.Items)
{
PutDataInGridView(dataItem); //At some point in this method, I need to take CustomerSecondaryId
}
I tried to put this in aspx but it says that it's not a known element
<telerik:GridClientSelectColumn UniqueName="CustomerSecondaryId" Visible="false" />
Planned to get data from CodeBehind like this :
string customerSecondaryId = dataItem["CustomerSecondaryId"].Text;
As a result I am getting "&nbsp ;" every time for each dataItem.
I do not want to use DataRowCollection. I only want to put data in hidden field and get it's value when needed. Any ideas?
Well I not used the telerick grid, but I am betting that it follows both GridView, and listview.
And VERY common goal is to display some data, but NOT have to expose the PK id from the database.
However, GridView and Listview BOTH have built-in provisions for this need (easy of getting, grabbing the PK row of data, but NOT exposing that PK ID.
The asp.net repeater does NOT have this desired ability, but there are some nice cool tricks to get the PK or row ID from a repeater, and again not having to send the PK id to the client side, nor expose it.
So, lets do a really simple example
SELECT ID, HotelName, City, Province, Description, Active
So, we want to display (send) to say client side the HotelName, City, Province but say we click on a row, and want to get the hotel row PK id? (quite common need).
So, we drop in a grid, and have this markup:
(and lets drop is a plane jane asp.net button for the row click).
(and I actually perfer listview since it allows plane jane asp.net controls for the grid without that silly "template" tag set - but lets just go with GridView.
So, we have this markup:
<div style="width:50%;padding:25px">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table">
<Columns>
<asp:BoundField DataField="HotelName" HeaderText="HotelName" ItemStyle-Width="200" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Province" HeaderText="Province" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:CheckBoxField DataField="Active" HeaderText="Active" />
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:Button ID="cmdRowFun" runat="server" Text="Row Click" OnClick="cmdRowFun_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
And this code to fill the grid:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid
End If
End Sub
Sub LoadGrid()
Using cmdSQL As _
New SqlCommand("SELECT ID, HotelName, City, Province, Description, Active FROM tblHotels " &
"ORDER BY HotelName",
New SqlConnection(My.Settings.TEST4))
cmdSQL.Connection.Open()
GridView1.DataSource = cmdSQL.ExecuteReader
GridView1.DataBind()
End Using
End Sub
output:
Ok, so now the row click button:
Protected Sub cmdRowFun_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.Parent.Parent
Dim intPK As Integer
intPK = GridView1.DataKeys(gRow.RowIndex).Item("ID")
Debug.Print("Row clicked = " & gRow.RowIndex)
Debug.Print("PK Value = " & intPK)
Debug.Print("Hotel = " & gRow.Cells(0).Text)
End Sub
Output for a row click:
Row clicked = 1
PK Value = 72
Hotel = Banff Rocky Mountain Resort
So, we get the Grid row clicked. We can now launch a form, or whatever and pull that whole record say for editing a form or whatever.
So, we get the row clicked on.
From that we can get the row index.
From that we get/grab from the control the DataKeys with row index and thus get the PK value.
Note VERY careful the setting called DataKeyNames.
So, we NEVER had to expose the PK client side.
You can certainly include a hidden field, or even make up (fake) attribute of the button, and do this:
<asp:Button ID="cmdRowFun" runat="server" Text="Row Click"
OnClick="cmdRowFun_Click"
MyPK = '<%# Eval("ID") %>'
/>
So, now we get PK with:
Dim btn As Button = sender
Debug.Print("Pk value = " & btn.Attributes.Item("MyPK").ToString)
So, you can add the PK value to buttons, labels or even just a hiddenfield in the grid. And you can even add the attribute to the button I dropped into the grid.
Now, of course in that last example, then you ARE exposing the PK id to the client side browser. I suppose in some cases, this might not matter, but I as a general rule try to avoid outright exposing or having database PK id exposed or even required in the client side markup. It is is a big security hold you should avoid.
You have to check the telerick docs, but I suspect they have some kind of DataKeys collection that is part of their grid control, since both GridView, ListView has this feature - and it designed exactly for your question and problem.
That problem is to display data, but not show the PK value, and in fact not even have to include it in the mark-up that is rendered to the browser client side.

How do I assign OnRowCommand to multiple IDs ASP.net and VB.net Backend

I have 2 GridViews with separate IDs
I need the backend code to update the one being viewed when a button is clicked.
` Protected Sub savestatus(sender As Object, e As EventArgs)
Dim btn As Button = TryCast(sender, Button)
Dim row As GridViewRow = CType(((CType(sender, Button)).NamingContainer), GridViewRow)
Dim rowindex As Integer = row.RowIndex
Dim code As String = GridView1.DataKeys(row.RowIndex).Values(0).ToString()
Dim type As Int32 = GridView1.DataKeys(row.RowIndex).Values(1)
Dim statusid As Integer
Dim checkLocked, checkerror As CheckBox
' For Each row As GridViewRow In GridView1.Rows
checkLocked = CType(GridView1.Rows(rowindex).FindControl("lock"), CheckBox)
checkerror = CType(GridView1.Rows(rowindex).FindControl("error"), CheckBox)
If checkerror.Checked Then ' error
statusid = 2
End If
If checkLocked.Checked Then
statusid = 3
End If`
How do I make the GridView1 a variable depending on which grid view the button is pressed in.
Ok, it would have helped a lot to at least show the button and a few rows of the gridview markup.
There are about 10 ways to do this. (really !!!).
However, in your case, two check boxes, and you need actions to occur when a check box is changed – AND say change the other one!!
Now I am using two check boxes – but it could be a text box or whatever I change.
So, say I have this grid markup
Some columns + TWO un-bound check boxes.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Province" HeaderText="Province" />
<asp:TemplateField HeaderText="Good">
<ItemTemplate>
<asp:CheckBox ID="chkGood" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkGood_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Bad">
<ItemTemplate>
<asp:CheckBox ID="chkBad" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkBad_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Ok, and now the code to load the grid:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
Using cmdSQL As New SqlCommand("SELECT ID, HotelName, City, Province from tblHotels",
New SqlConnection(My.Settings.Test3))
cmdSQL.Connection.Open()
GridView1.DataSource = cmdSQL.ExecuteReader
GridView1.DataBind()
End Using
End If
End Sub
And thus we have this:
Ok, so far - very simple.
Now note CLOSE at the markup for the two check boxes.
And while dropping a button or whatever on a normal form - you can then double click to JUMP to the code behind event/stub?
Well, for buttons or whatever you drop INSIDE of a grid, you can't double click on the control to create + jump to the code behind stub.
But, WHILE in the markup, you can start typing the event, and you get this:
Note VERY careful how the intel-sense pops up a option to create the event. So click on that option. Nothing seems to happen, but NOW we get a code stub behind.
So, we have this code stub for the chkOk event:
Protected Sub chkGood_CheckedChanged(sender As Object, e As EventArgs)
Dim ckBox As CheckBox = sender
Dim RowID As Integer = ckBox.Attributes.Item("MyRowID")
Dim gvRow As GridViewRow = GridView1.Rows(RowID)
If ckBox.Checked = True Then
' do whatever if true - say un-check the "bad" check box
Dim ckBoxBad As CheckBox = gvRow.FindControl("chkBad")
ckBoxBad.Checked = False
Else
' code here if the user just un-checked the "good" check box
End If
End Sub
Note a few things:
We pick up the button click - then shove it into a checkbox control. This is just a lot easier to get the check box value, and our CUSTOM MyRowID
(and this works if it was a button for example).
We then get the custom made up Attribute we added called "MyRowID"
MyRowID ='<%# Container.DataItemIndex %>'
You can see the expression in the Markup - it passes the current row id. Sometimes, I'll pass other values from the row and you can do this like this:
<asp:CheckBox ID="chkBad" runat="server"
AutoPostBack="true"
OnCheckedChanged="chkBad_CheckedChanged"
MyRowID ='<%# Container.DataItemIndex %>'
MyPKID = '<%# Eval("ID") %>' />
So in above, I pass both RowID and a custom MyPKID (so the Eval() expression can be used to pass any valid data row avaialble at binding time. Its often handy then having to grab and mess with a data row - you JUST grab the button from sender - and you don't care about gridview or anything else to get a few extra values. (just a FYI tip). So for example, I REALLY don't want the PK row id as the first row. So I could remove it and STILL use the above idea to PASS the pk row id - all columns can be used - even if a control is NOT in the grid - as long as the column exists during the data binding process - you can grab it.
So, now we pick up the current GridRow - and we are free to modify whatever we want on that row.
In my simple example, we pick up the OTHER check box - and un-check if it was checked. But we could say update other things on that row.
And I did the same for the chkBad check box. And I have really the same as the first chkBox code stub. Eg this:
Protected Sub chkBad_CheckedChanged(sender As Object, e As EventArgs)
Dim ckBox As CheckBox = sender
Dim RowID As Integer = ckBox.Attributes.Item("MyRowID")
Dim gvRow As GridViewRow = GridView1.Rows(RowID)
If ckBox.Checked = True Then
' user checked the bad box, un-check the good one
Dim ckBoxGood As CheckBox = gvRow.FindControl("chkGood")
ckBoxGood.Checked = False
Else
' code here if the user just un-checked the "bad" check box
End If
End Sub
So in above we just hard right past the GridView bult in events.
So in above, if you check one box and the other is checked - we un-check it. Needless to say, I would use a button list, or a checkbox list, and that above code would of course then not be required. But it still a good example on how to pluck/get the current row. And then get/pluck controls from that row.
Note that for the first 3 rows (the databound), you can NOT use findControl, and they are referenced using the gvRow.Cells(0) (starting at 0 to N columns. So no findcontrol is required for these databound columns or autogenerated ones. They do NOT have a name - you have to use number starting at 0 in the cells collection. Of course for "templated" controls that we added as per above? Then you do in fact use findcontrol as per above.

Filter gridview with OnChecked Event

I have a gridview loaded from a database with a checkbox in the first column. What I want is that when clicking on any row the grid will be filtered to show only the rows in which the value of the second cell is the same as the one I clicked on (having the option to select more rows with that value). Removing the checks he would reload all the information again. I tried doing it using the OnChecked event, but due to AutoPostBack it filters but loses the value of the checkbox checked. What is the best way to do what I want? I hope I have been explicit. Thank you
asp:GridView ID="gridview" runat="server" AutoGenerateColumns="false" DataKeyNames="Id">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkRow" OnCheckedChanged="chkRow_CheckedChanged" AutoPostBack="true" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Document" ItemStyle-Width="10%" HeaderText="Documento" />
<asp:BoundField DataField="Entidad" ItemStyle-Width="55%" HeaderText="Entidad" />
</Columns>
For Each row As GridViewRow In gvEncomendasPendentes.Rows
If row.RowType = DataControlRowType.DataRow Then
Dim chkRow As CheckBox = TryCast(row.Cells(0).FindControl("chkRow"), CheckBox)
If chkRow.Checked Then
'Bind with filter
End If
End If
Next
Does the table display the entire dataset at all times? If so, I'd check into client-side filtering (JavaScript). Not having the round trip to the server/database would make your page a lot more responsive.
But assuming that's not the case, and to actually answer the posted question, here's how I'd approach it in the VB.Net Code. Try changing the Change Event like this:
Protected Sub chkRow_CheckedChanged (sender as object, e As EventArgs)
Dim chk As CheckBox = DirectCast(sender, CheckBox)
dim grv As GridRowView = DirectCast(chk.NamingContainer, GridRowView)
dim hdn AS HiddenField = grv.FindControl("hdnFieldWithValue")
If chk.Checked then
AddValueToFilerList(hdn.Value)
Else
RemoveValueFromFilterList(hdn.Value)
End If
BindGridView()
End Sub
Your way does work, but that has a Log(N) time on processing, as it needs to look at every row in the grid. The above way will KNOW which checkbox was checked, and deal with it appropriately.
And this small change to the markup:
<asp:hiddenField runat='server' id='hdnFilterList' />
<asp:GridView ID="gridview" runat="server" AutoGenerateColumns="false" DataKeyNames="Id">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkRow" OnCheckedChanged="chkRow_CheckedChanged" AutoPostBack="true" runat="server" checked='<%# CheckValueInFilterList(Eval("Entidad")) %>' />
<asp:hiddenField id="hdnFieldWithValue" runat="server" value='<%# Eval("Entidad")' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Document" ItemStyle-Width="10%" HeaderText="Documento" />
<asp:BoundField DataField="Entidad" ItemStyle-Width="55%" HeaderText="Entidad" />
</Columns>
You'll also a function something like this:
Protected Function CheckValueInFilterList(input as string) as boolean
Return MyListOfFilterItems.Contains(input)
End Function
The purpose of that would be to see if the input is contained in your list of filters you're applying to your query and returning true/false depending on that.
Which is directly related to why you're not retaining a check in the checkboxes between postbacks. Since you're rebinding the grid, the existing form state is thrown away in favor of the values from the bind. Since your markup lacked a setter for the checkbox's checked state in the databind, it always resorted to false.
I also added a hidden field to the Markup which would contain the filter list as some type of serialized string (JSON, XML, etc). I'd read it from that field on the Page Load where IsPostBack is True, and always write the list to it in the PreRender event. The point is to just have that passed back and forth through the posted form state so that you can remember them between postbacks.
Maintaining that list of filters in a control outside of the gridview as described does add a bit of overhead in the data that is sent to/from the server on roundtrips. But I wouldn't expect that to a large amount, and would expect a preformance increase due to not having to iterate through the entire gridview item list on every postback.

Resources