so I have a gridview which is displaying a db table I have.
I dont create the table manually obviously as this is what <%#Eval("text")%>' and Bind() do.
I created a unique table with my sql query to add two rows of sum in the bottom of the table, ( 2 last records), my question is:
is there a way in which I can access those rows to style them?
I think its not possible but still Im asking maybe Ill find out that theres a way.
thanks
Yes, the need to style a individual cell (auto generated columns, or bound columns), or even style the whole row.
Let's do all 3
First up, style a "cell" (these are in the cells collection, and autogenerated columns, and NON templated columns).
So, say this simple grid
<asp:GridView ID="GridView1" runat="server" Width="40%"
AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table table-hover">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:TemplateField HeaderText="Description">
<ItemTemplate>
<asp:Label ID="lblDescript" runat="server"
Text='<%# Eval("Description") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Active" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:CheckBox ID="chkActive" runat="server"
Checked='<%# Eval("Active") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
(note how I included BOTH templated columns which have plain jane asp.net controls like label, textbox etc., or the built in GV columns (data bound ones, or auto generated)
Code to load the GV is this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadData()
End If
End Sub
Sub LoadData()
Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName"
Using conn As New SqlConnection(My.Settings.TEST4)
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 we now have this:
Ok, so let's do example 1 - lets color the "city" Colum in above. Note how "city" is a "bound field".
So, auto generated, or bound fields = cells collection.
So, for things like conditional formatting, even combine some columns, change color, hide show images, or whatever?
you use the GV row databound event. (and this applies well to listview, repeaters, etc.)
So, lets say for city = Edmonton, we want a light blue cell, and for city = "Edmonton", we want some light green.
So, we use the row bound event.
This code:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Select Case e.Row.Cells(3).Text
Case "Edmonton"
e.Row.Cells(3).BackColor = System.Drawing.Color.SkyBlue
Case "Banff"
e.Row.Cells(3).BackColor = System.Drawing.Color.LightGreen
End Select
End If
End Sub
And we now have this:
However, the above for "templated" colums works a bit different.
Lets say we want to highligh the description when check box = true (checked).
In that case, we can't (don't) use the cells colleciton, but for templated controls, we use find control.
Like this:
Dim cActive As CheckBox = e.Row.FindControl("chkActive")
If cActive.Checked Then
Dim lblDes As Label = e.Row.FindControl("lblDescript")
lblDes.Font.Italic = True
e.Row.Cells(4).BackColor = System.Drawing.Color.LightGray
End If
NOTE how we EVEN set the font to italic in the label control.
And now we see this:
NOTE VERY close how we use findcontrol to get those templated controls.
Last but not least - bonus:
let's highlight the whole row for active, and NOT EVEN have the check box column in the GV (but it is in the database source).
So, say this markup for the columns:
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:TemplateField HeaderText="Description">
<ItemTemplate>
<asp:Label ID="lblDescript" runat="server"
Text='<%# Eval("Description") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
So, "active" check box is NOT EVEN displayed in the GV, (but, it is in the data source that feeds the gv).
So, we can do this:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Dim gData As DataRowView = e.Row.DataItem ' Grid data - NOT the GV row!!!!!!
If gData.Item("Active") = True Then
e.Row.Style.Add("Background-color", "LightGray") ' highlight whole row.
End If
End If
End Sub
And we now get this:
Just keep in mind that the "dataitem" (the full database row) is ONLY avaiable during the row data bind, and it WILL/DOES go out of scope once data binding is complete.
so basically using pretty much similar logics as albert's reply, I ended up catching two last rows manually and changing their colors.
protected void grdReports_PreRender(object sender, EventArgs e)
{
//finding 2 last rows, changing backcolor to the sum specified color '#5D7B9D' and the forecolor to white
//first table ( annual )
GridViewRow lastRow = grdReports.Rows[grdReports.Rows.Count - 1];
lastRow.BackColor = System.Drawing.Color.FromName("#5D7B9D");
lastRow.ForeColor = Color.White;
GridViewRow secondToLastRow = grdReports.Rows[grdReports.Rows.Count - 2];
secondToLastRow.BackColor = System.Drawing.Color.FromName("#5D7B9D");
secondToLastRow.ForeColor = Color.White;
//second table ( requested )
GridViewRow lastRowSecond = grdReports2.Rows[grdReports2.Rows.Count - 1];
lastRowSecond.BackColor = System.Drawing.Color.FromName("#5D7B9D");
lastRowSecond.ForeColor = Color.White;
GridViewRow secondToLastRowSecond = grdReports2.Rows[grdReports2.Rows.Count - 2];
secondToLastRowSecond.BackColor = System.Drawing.Color.FromName("#5D7B9D");
secondToLastRowSecond.ForeColor = Color.White;
}
Related
I have an ASPX page that includes a GridView. I want to be able to select a row from the grid, and populate another section of the page based on the selected row. It works if I have EnableEventValidation="false" in the <%# Page %> line, but I have been told that I cannot use that because of a security concern. When I don't include it, selecting a grid row throws an "Invalid postback or callback argument" exception.
How can I implement row selection without disabling event validation?
Here is my code:
ASPX page:
<asp:GridView runat="server" ID="TheGrid" AutoGenerateColumns="false" DataKeyNames="id" EmptyDataText="No Data Found" AllowSorting="true">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" ReadOnly="true" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" ReadOnly="true" SortExpression="LastName" />
<asp:BoundField DataField="Email" HeaderText="Email" ReadOnly="true" SortExpression="Email" />
</Columns>
</asp:GridView>
ASPX.VB code:
Protected Sub TheGrid_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles TheGrid.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes("onclick") = Page.ClientScript.GetPostBackClientHyperlink(TheGrid, "Select$" & e.Row.RowIndex)
e.Row.Attributes("style") = "cursor:pointer"
End If
End Sub
Protected Overrides Sub Render(writer As HtmlTextWriter)
ClientScript.RegisterForEventValidation("TheGrid")
MyBase.Render(writer)
End Sub
Note that when I select a row, the exception is thrown somewhere between Page_Load and Render.
Ok, lets wire up the GV two ways.
First way, drop in a plane jane button for the row click
(we will delete the button and add row click in 2nd example).
so, say this simple GV
<div id="MyGridArea" runat="server" clientidmode="static">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID"
CssClass="table table-hover" Width="60em" GridLines="None"
ShowHeaderWhenEmpty="true">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn myshadow"
OnClick="cmdEdit_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
Note how we just dropped in a plane jane button.
And our code to fill out above 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()
Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName"
GridView1.DataSource = Myrst(strSQL)
GridView1.DataBind()
End Sub
And now we have this:
Ok, so next part is to drop in a "div" area that allows edit of one row.
So, again, quite much plan jane controls.
And we can "hide" grid when we edit, and show edit area div.
But, I often use jQuery.UI. Really amounts to much the same code as simple hide/show, but this way jQuery.UI turns that SAME div into a nice pop up area.
So, that div area might look like this:
<div id="EditRecord" runat="server" style="float: left; display: none" clientidmode="Static">
<br />
<div style="float: left" class="iForm">
<label>HotelName</label>
<asp:TextBox ID="txtHotel" runat="server" f="HotelName" Width="280">
</asp:TextBox>
<br />
<label>First Name</label>
<asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox>
<br />
<label>Last Name</label>
<asp:TextBox ID="tLN" runat="server" f="LastName" Width="140"></asp:TextBox>
<br />
<label>City</label>
<asp:TextBox ID="tCity" runat="server" f="City" Width="140"></asp:TextBox>
<br />
<label>Province</label><asp:TextBox ID="tProvince" runat="server" f="Province" Width="75"></asp:TextBox>
</div>
etc. etc. etc.
so, now lets wire up the button above.
The button will simple:
Get current grid row
Get PK id
Load up div and display.
So, that code is this:
Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
EditRow(gRow.RowIndex)
End Sub
Sub EditRow(rowNum As Integer)
Dim intPK As Integer = GridView1.DataKeys(rowNum).Item("ID")
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intPK
Dim rstData As DataTable = Myrst(strSQL)
' load up our edit div
fLoader(Me.EditRecord, rstData.Rows(0))
ViewState("PK") = intPK
ScriptManager.RegisterStartupScript(Me.Page, Page.GetType, "mypopedit", "popedit()", True)
End Sub
As noted, I added a popup from jQuery.UI, but we could just use plain jane "div" and hide/show the grid and show the edit area (or like you have, have that edit area in full view).
(fLoader is a routine I built some time ago - I like everyone became VERY tired of typing code over and over to fill out text boxes etc., so for any text box etc., I use a "made up" attribute called f="Data base column name", and that routine just loops the controls on the form and fills them out. No different than hand coding simple assignments to controls, but with this code I can re-use it over and over.
So, we now see, get this:
Ok, so the only next goal is to add a row click
(and not use that edit button).
So, then all we need is a routine that will get the current row index, and call our edit row routine we have above.
So, we use row data bound, and add that click event that way.
but, do note how the above button click gets the current row. That nice short code works for repeaters, listview etc. (we used naming container).
But, if you want a row click in place of that button?
Then on row data bound, add this code:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes.Add("onclick",
"__doPostBack('myrowedit'," & e.Row.RowIndex & ")")
End If
End Sub
And in our page load event, we have this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid()
Else
If Request("__EVENTTARGET") = "myrowedit" Then
EditRow(Request("__EVENTARGUMENT"))
End If
End If
End Sub
And a handy dandy routine that returns a table based on SQL I used above was this:
Public Function Myrst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using mycon As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, mycon)
mycon.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
So, all in all, not a lot of code.
You can try the above working example here:
http://www.kallal.ca/Website11/WebForm2
I have a drop down list 'Country', 'city' text box and an 'Add' button. The country drop down is NOT mandatory so I can just add a city without a country, adding an empty 'country' to the gridview works OK. the problem when I click on 'Edit' in the gridview it binds it to the first country in the list it does not just show a blank:
<asp:DropDownList ID="DDLCountry" runat="server" AutoPostBack="true" AppendDataBoundItems="true" OnSelectedIndexChanged="DDLCountry_SelectedIndexChanged" InitialValue="">
<asp:ListItem Text="------------------------ Select ------------------------" Value="" />
</asp:DropDownList>
<asp:TextBox ID="txtCity" runat="server"></asp:TextBox>
<asp:Button ID="btnNewLList" runat="server" OnClick="btnNewLList_Click" Text="Add new Country"/>
<asp:GridView ID="gvAddNewCountry" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" OnRowCommand="gvAddNewCountry_RowCommand" OnRowDeleting="gvAddNewCountry_RowDeleting" OnRowDataBound="gvAddNewCountry_RowDataBound" OnRowUpdating="gvAddNewCountry_RowUpdating" OnRowEditing="gvAddNewCountry_RowEditing" OnRowCancelingEdit="gvAddNewCountry_RowCancelingEdit" ShowHeaderWhenEmpty="True">
<EmptyDataTemplate>
No Data
</EmptyDataTemplate>
<Columns>
<asp:TemplateField HeaderText="Actions">
<ItemTemplate>
<asp:Button ID="btnEdit" runat="server" Text="Edit"/>
</ItemTemplate>
</asp:TemplateField>
<asp:CommandField ButtonType="Button" ShowEditButton="true" ShowCancelButton="true">
</asp:CommandField>
<asp:TemplateField HeaderText="Country>
<ItemTemplate>
<asp:Label ID="lblCountry" runat="server" Text='<% #Eval("Country") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList ID="DDCountry" runat="server" AppendDataBoundItems="True" AutoPostBack="false"></asp:DropDownList>
</EditItemTemplate>
</asp:TemplateField>
Code behind:
Protected Sub gvAddNewCountry_RowCommand(sender As Object, e As GridViewCommandEventArgs)
If e.CommandName = "Edit" Then
Dim rowCountryToEdit As Integer = e.CommandArgument
Dim ddListCountry As DropDownList = (CType(gvAddNewCountry.Rows(CInt(e.CommandArgument)).FindControl("DDCountry"), DropDownList))
ddListCountry.DataSource = (From x In Country Where x.Domain = "lCountry" Order By x.Description Select x).ToList()
ddListCountry.DataTextField = "Description"
ddListCountry.DataValueField = "ID"
ddListCountry.DataBind()
End If
End Sub
Thanks for your help X
Ok, so when you have/want a ddl in a gv row?
We require TWO steps.
First step: Load up the list of choices for the dll
2nd step: set the ddl to the current row value, or blank (no choice) if null no value exists yet for the current row. This ALSO means we have to get/grab the current row value for the dll, and set the ddl to reflect this existing choice.
So, this is a two step process.
And the "event" we typical use for this is the row bind event. (all such controls from listview, gridview and more have this event).
Also, a VERY nice helper tip? During (but ONLY during) the data bind event, you have FULL USE of ALL columns from the data source - EVEN COLUMNS NOT in the gv!!!
I don't have a entity database first setup that you have, but lets load up a gv with a combo box:
So, our gv:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
CssClass="table table-hover" Width="50%"
DataKeyNames="ID">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" />
<asp:BoundField DataField="LastName" HeaderText="First Name" />
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Description" HeaderText="Descripiton" />
<asp:TemplateField HeaderText="Rating">
<ItemTemplate>
<asp:DropDownList ID="cboRating" runat="server"
DataTextField="Rating"
DataValueField="ID">
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And our code to fill is this:
Dim rstRating As New DataTable
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()
' get data for rating combo
rstRating = MyRst("SELECT ID, Rating FROM tblRating ORDER BY ID")
' get data for grid
GridView1.DataSource = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName")
GridView1.DataBind()
End Sub
Public Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
Note VERY carefull in above, I created a page wide (class wide) data table called rstRating. This will go out of scope after the data bind, but we ONLY need it to persit DURING the gv data bind operating (since for each row of the gv, we don't want to run that query over and over - we need this same pick list for the dll).
Ok, so now we see/get this:
The only part we need is to load up the dll, and set it for each row. We use the RowDataBound event.
So, code for that was this:
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
' bind drop down for each row
If e.Row.RowType = DataControlRowType.DataRow Then
' get combo
Dim rDrop As DropDownList = e.Row.FindControl("cboRating")
rDrop.DataSource = rstRating
rDrop.DataBind()
rDrop.Items.Insert(0, New ListItem("Please Select", "0"))
' now get current row value for rating.
Dim gData As DataRowView = e.Row.DataItem
If IsDBNull(gData("Rating")) = False Then
rDrop.Text = gData("Rating")
End If
End If
End Sub
So, for each row, get the dll.
For for that row, load up with choices
And THEN for that row, set the ddl to the current row value (but check for null, and don't set - it will then show our "select" choice value.
I have a datepicker calendar in JS. Once a user clicks on a date, that date gets passed to the asp:TextBox as string. I would like to query my database with the text in textbox without having to click on a button. Is this possible?
Well, there is what, 100's of JS date pickers?
(which one you using might have helped here).
So, it going to depend on how the JS works.
Lets drop in a text box, set autopostback = true, and then add a text change event.
So, we have this markup:
<h2>Choose Date</h2>
<asp:TextBox ID="txtDate" runat="server"
TextMode="Date" AutoPostBack="True" ></asp:TextBox>
and the code behind is this:
Protected Sub txtDate_TextChanged(sender As Object, e As EventArgs) Handles txtDate.TextChanged
Debug.Print("Date changed to " & txtDate.Text)
End Sub
Now run the page and we have this:
Note how that text box already shows a date picker if you set textMode = date. Maybe you don't need a JS date picker then?
Ok, so lets select a date:
And because I have autopost-back = true, I see this in output:
Output:
So, in your server side text changed event - it should trigger. But that triggering of the text changed event may well depend on how and what kind of out of the 1000+ date picker JS examples I can find on the web that you are using here.
So now lets query the database based on that value then right?
So, say we have this markup:
<h2>Choose Date</h2>
<asp:TextBox ID="txtDate" runat="server" TextMode="Date" AutoPostBack="True" ></asp:TextBox>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="VisitDate" HeaderText="Vist" DataFormatString="{0:yyyy-MM-dd}" />
<asp:BoundField DataField="Description" HeaderText="Description" />
</Columns>
</asp:GridView>
We select a date, and show visits for that date.
Our code thus becomes this:
Protected Sub txtDate_TextChanged(sender As Object, e As EventArgs) Handles txtDate.TextChanged
Debug.Print("Date changed to " & txtDate.Text)
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL =
"SELECT * from tblHotels WHERE VisitDate = #VisitDate ORDER BY HotelName"
Using cmdSQL = New SqlCommand(strSQL, conn)
cmdSQL.Parameters.Add("#VisitDate", SqlDbType.DateTime).Value = txtDate.Text
conn.Open()
GridView1.DataSource = cmdSQL.ExecuteReader
GridView1.DataBind()
End Using
End Using
End Sub
And we now have this:
I have a GridView holding my order table (these are inserted values from an insert statement). The order table appears on the doctor page, but I want to add to it so it can be approve/unapproved (if checked then update the 'Approve' column to approved, if not checked update the 'Approve' column to not approved. I need to add a CheckBox column to the GridView for this. This is part of my learning (not a live website).
How do I add a column of check boxes to a GridView that would set a row to approve/unapproved when checked?
This is my order table -(all the data is dummy data)
Grid:
<asp:GridView ID="GridViewdoc" runat="server" >
</asp:GridView>
Showing the data on the grid
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If Not IsPostBack Then
Dim conn As New System.Data.SqlClient.SqlConnection("Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\surgerydb.mdf;Integrated Security=True;Connect Timeout=30")
Dim cmd3string As String = " Select * From alltheview WHERE DoctorId = " & Session("DoctorId")
Dim dt As New System.Data.DataTable()
Dim da As New System.Data.SqlClient.SqlDataAdapter(cmd3string, conn)
conn.Open()
da.Fill(dt)
conn.Close()
GridViewdoc.DataSource = dt
GridViewdoc.DataBind()
End If
End Sub
My select statement that pulls the data:
Create View theallview
As
Select A.OrderID
,A.PatientId ,B.Forename,B.Surname ,A.MedicineId ,C.Name as MedicineName ,E.Name as DoctorName, A.PharmacyId ,D.pharmname ,A.DoctorId ,A.Dateordered, Approved
From order_pres A
Left Join Patient B on (A.PatientId = B.PatientId)
Left Join Medicine C on (A.MedicineId = C.MedicineId)
Left Join pharmacy D on (A.PharmacyId = D.PharmacyId)
Left Join Doctor E on (A.DoctorId = E.DoctorId)
How the order table now looks (this is all dummy data by the way:
A button . click event will then submit the checked values
If anyone needs more information regarding this question please let me know.
Try this. It's in C# but you should be able to work out the VB.Net pretty easily.
As an aside I would put your code to load your data in its own method. Once the approved is updated I would recommend reloading your grid view.
ASPX:
<asp:GridView ID="GridViewdoc" DataKeyNames="OrderId" AutoGenerateColumns="false" runat="server">
<Columns>
<asp:BoundField HeaderText ="Order Id" DataField="OrderId" />
<asp:BoundField HeaderText ="Patient Id" DataField="PatientId" />
<asp:BoundField HeaderText ="Medicine Id" DataField="MedicineId" />
<asp:BoundField HeaderText ="Pharmacy Id" DataField="PharmacyId" />
<asp:BoundField HeaderText ="Doctor Id" DataField="DoctorId" />
<asp:BoundField HeaderText ="Date Ordered" DataField="Dateordered" />
<asp:TemplateField HeaderText="Approve/Unapprove">
<ItemTemplate>
<asp:CheckBox ID="chkApproved" AutoPostBack="true" Checked='<%# Eval("Approved") == null || Eval("Approved") == DBNull.Value ? false : Eval("Approved") %>' runat="server" OnCheckedChanged="chkApproved_CheckedChanged" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code behind:
protected void chkApproved_CheckedChanged(object sender, EventArgs e)
{
CheckBox chkApproved = (CheckBox)sender;
GridViewRow gridViewRow = (GridViewRow)chkApproved.Parent.Parent;
int orderID = (int)GridViewdoc.DataKeys[gridViewRow.RowIndex].Value;
bool approved = chkApproved.Checked;
//Your update method
UpdateApproved(orderID, approved);
//Your data load method
LoadData();
}
When I call gvTags.DataSource = GetTags() and gvTags.DataBind() it shows that all the rows and columns have the appropriate data.
When the OnRowDataBound function is hit, the last columns in my first table row have no data (null values).
The only difference between this table and the others I've created is that I am showing/hiding columns based on a user selection higher in the page. But - the showing/hiding is done within the OnRowDataBound function, where the data is already missing. I have no idea what's going on, or where to even start for looking further.
UPDATE: It looks like the problem is caused by the last three lines of the RowDataBound function. When I remove those three rows, the data displays as it should. So - I need a way to show/hide those three columns based on if a user selects a checkbox elsewhere on the page (Include Removed Entries). If the checkbox is selected, the RemovedBy and RemovedDate columns are visible. If it is not selected, those hide but the Description column is visible.
<asp:GridView ID="gvTags" CssClass="table table-striped" runat="server" AutoGenerateColumns="false"
AllowSorting="false" GridLines="None" OnRowDataBound="gvTags_RowDataBound" DataKeyNames="TagID">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="btnRemove" runat="server" OnClick="btnRemove_Click">
Remove
</asp:LinkButton>
<asp:LinkButton ID="btnReapply" runat="server" OnClick="btnReapply_Click">
Re-Apply
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="TagID" HeaderText="ID" />
<asp:BoundField DataField="InstalledBy" HeaderText="Installed By" />
<asp:BoundField DataField="InstalledDate" HeaderText="Date Installed" />
<asp:BoundField DataField="RemovedBy" HeaderText="Removed By" />
<asp:BoundField DataField="RemovedDate" HeaderText="Date Removed" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:BoundField DataField="IsRemoved" Visible="false" />
</Columns>
</asp:GridView>
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If (Page.IsPostBack) Then
Exit Sub
End If
gvTags.DataSource = GetTags()
gvTags.DataBind()
'NOTE - Here, DataSource has complete data (all rows & columns that should have data, do have data)
If (gvTags.Rows.Count > 0) Then
gvTags.HeaderRow.TableSection = TableRowSection.TableHeader
End If
End Sub
Protected Sub gvTags_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
If (Not e.Row.RowType = DataControlRowType.DataRow) Then
Exit Sub
End If
e.Row.FindControl("btnRemove").Visible = Not Boolean.Parse(DataBinder.Eval(e.Row.DataItem, "IsRemoved").ToString)
e.Row.FindControl("btnReapply").Visible = Boolean.Parse(DataBinder.Eval(e.Row.DataItem, "IsRemoved").ToString)
'NOTE - Here, gvTags.Columns(columnIndex) is null for each of the below three columns (RemovedBy, RemovedDate, and Description)
gvTags.Columns(removedByColumnIndex).Visible = checkbox.Checked
gvTags.Columns(removedDateColumnIndex).Visible = checkbox.Checked
gvTags.Columns(descriptionColumnIndex).Visible = (Not checkbox.Checked)
End Sub
My suggestion: if you columns are to be shown/hidden depending on a CheckBox outside of the grid, you could do this in the markup:
<asp:CheckBox ID="chkBox" runat="server" AutoPostBack="true" ... />
And process the event in code-behind:
Private Sub chkBox_CheckedChanged(sender As Object, e As System.EventArgs) Handles chkBox.CheckedChanged
gvTags.Columns(removedByColumnIndex).Visible = chkBox.Checked
gvTags.Columns(removedDateColumnIndex).Visible = chkBox.Checked
gvTags.Columns(descriptionColumnIndex).Visible = (Not chkBox.Checked)
gvTags.DataSource = GetTags()
gvTags.DataBind()
End Sub