How to get a column of checkboxes in Gridview? - c#

One note is that I'm doing this all in Visual Studio 2013 Express for Web.
I have a GridView, and its data is being generated by a SqlDataSource. Currently, when I test the page. A table like below gets generated:
CustomerID CustomerName CustomerAddress
----------- ------------ ----------------
1 Bob Address
2 John Address
3 Smith Address
However, I really want this:
CustomerID CustomerName CustomerAddress
----------- ------------ ----------------
[] Bob Address
[] John Address
[] Smith Address
I want the CustomerID field to be "hidden field" and have a checkbox in its place. Then, I want the value of the checkboxes to be the CustomerID for that row. However, I can't for the life of me get the checkboxes in there, and it's still just displaying the CustomerID itself. In the end, I want to create a button that will delete the rows of whatever is checked, and have it reflected in the database via a DELETE FROM TABLE. This is why I want the checkboxes to also "have" a value of whatever the CustomerID is for that particular row.
Here's the code:
<asp:GridView ID="GridView1" runat="server" AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="CustomerID" DataSourceID="RowsInGroup" >
<Columns>
<asp:BoundField DataField="CustomerID" HeaderText="CustomerID" ReadOnly="True" SortExpression="CustomerID" />
<asp:BoundField DataField="CustomerName" HeaderText="CustomerName" SortExpression="CustomerName" />
<asp:BoundField DataField="CustomerAddress" HeaderText="CustomerAddress" SortExpression="CustomerAddress" />
<asp:TemplateField></asp:TemplateField>
</Columns>
</asp:GridView>
If there's a better Data object to use in the Toolbox, I'm all ears.
Thanks for any advice you can provide!

You already have half of your problem solved. By using DataKeyNames="CustomerID", you have no need for a hidden field to hold this value.
First, create your check box column. There are multiple ways to accomplish this. Here is one:
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkDelete" runat="server" />
</ItemTemplate>
</asp:TemplateField>
Then in whatever event handles the delete, just iterate each row in the GridView and find the checkbox in each row. If it is checked, use the DataKey for that row to get the CustomerID.
protected void btnDelete_Click(object sender, EventArgs e)
{
List<string> customersToDelete = new List<string>();
foreach(GridViewRow row in GridView1.Rows)
{
CheckBox chkDelete = (CheckBox)row.FindControl("chkDelete");
if(chkDelete.Checked)
{
DataKey key = GridView1.DataKeys[row.DataItemIndex];
customersToDelete.Add(key.Value.ToString());
}
}
}

Related

Gridview Delete Button - Object Reference Error

I am trying to develop a web page in asp .NET with a GridView. I want the GridView to have an edit and delete button. Following is my markup for GridView:
<asp:GridView ID="EduGrid" CssClass="edugrid table table-striped table-hover table-borderless" runat="server" AutoGenerateColumns="False" GridLines="None" OnRowDeleted="EduGrid_RowDeleted" OnRowDeleting="EduGrid_RowDeleting">
<Columns>
<asp:CommandField SelectText="" ShowSelectButton="True" ControlStyle-CssClass="btn btn-dark btn-sm fa fa-edit" />
<asp:CommandField DeleteText="" ShowDeleteButton="true" ControlStyle-CssClass="btn btn-dark btn-sm fa fa-trash" />
<asp:BoundField DataField="degree" HeaderText="Degree" SortExpression="degree" />
<asp:BoundField DataField="major" HeaderText="Major" SortExpression="major" />
<asp:BoundField DataField="year" HeaderText="Passing Year" SortExpression="year" />
<asp:BoundField DataField="total marks" HeaderText="Total Marks" SortExpression="total marks" />
<asp:BoundField DataField="obt marks" HeaderText="Obtained Marks" SortExpression="obt marks" />
<asp:BoundField DataField="division" HeaderText="Division" SortExpression="division" />
<asp:BoundField DataField="board" HeaderText="Board/University" SortExpression="board" />
</Columns>
<HeaderStyle CssClass="thead-dark" Font-Names="Calibri" Font-Size="Larger" />
<RowStyle Font-Names="calibri" Font-Size="Medium" />
</asp:GridView>
I also have a label on the page "CandId" that contains ID to get records in GridView and delete the records from DataBase. But, as there are more than 1 records for each candidate, I want to delete the records on the base of 2 parameters i.e. ID & Degree (the first column). It deletes on the base of ID only but deletes all the records of that specific ID, but I only want to delete one specific entry. If I provide the degree column as a parameter, it gives an object reference exception. Following is my backend code in c#:
protected void EduGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
GridViewRow row = EduGrid.SelectedRow;
conn = new SqlConnection(connString);
string query = "DELETE FROM [Edu Info] WHERE cnic = #cnic AND degree = #degree";
sqlcmd = new SqlCommand(query, conn);
sqlcmd.Parameters.AddWithValue("#cnic", lblID.Text);
sqlcmd.Parameters.AddWithValue("#degree", row.Cells[1].Text);
try
{
conn.Open();
sqlcmd.ExecuteNonQuery();
lblInfo.Text = "Record deleted successfully.";
lblInfo.Visible = true;
Get_Edu();
}
catch (Exception ex)
{
lblInfo.Text = "An error occurred. Please contact the administrator.";
lblInfo.Visible = true;
}
finally
{
conn.Close();
conn.Dispose();
SqlConnection.ClearAllPools();
}
}
I think the problem is that the method OnRowDeleting is not suitable to work with GridView.SelectedRow and that .SelectedRow can only work in the OnSelectedIndexChanged method. If that is the case and I am right, what should I do to fix it? Can anyone please provide a solution?
Thanks.
Ok, lot of issues here. And I not clear why your data source does NOT include the primary key of the database. And as a result, you should not need to use both cnic and degree to identify a row. I cannot EVER recall having rows in a grid in which I don't have a PK ID. (so, very strange on your part).
However, do keep in mind that the delete event does NOT change the selected index. In fact if you drop in say a button, use commandName/CommandText, you find that the row command fires FIRST (selected index not yet changed), and THEN the selected index event fires.
In your example - index will not change.
However, in that event, you can get the row clicked on with with this:
e.RowIndex
So, you can now go like this:
GridViewRow row = EduGrid.Rows[e.RowIndex];
So, the rest of what you have thus should be ok. So, just keep in mind that for command buttons, and most buttons in the grid, the selected index even (if it were to fire) in fact fires AFTER the button event stub. In some cases then you could in theory move the code to the selected index changed event - but that does not even fire in this case. So selected index is NOT available. Use e.RowIndex to get that row.
Also, if you include the PK from the datbase, then you don't need to use the two columns to identify that row in the datbase, but ONLY the PK row id. And you do NOT have to have the PK even display in the grid.
That's what DataKeyNames = "the PK row column name" is for"
eg:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" >
So, DataKeyNames = "PK FROM database"
Thus in above RowDeleting event, then
e.RowIndex - gets you row index clicked on
e.Keys["ID"] - gets you database PK
So, note how you can (should) try and set the DataKeyNames = "primary key" from database. then you can get with e.Keys["ID"] the PK row id.

Change the name of column in Gridview on runtime when sorting of gridview is enabled

I want to change the name of one of the column of gridview on runtime.The Gridview has also sorting enabled in it.
the gridview looks like:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true" OnRowCreated="myGrid_Rowcreate"
ShowFooter="false"
AllowSorting="true"
OnSorting="GridView1_Sorting"
EnableViewState="false"
ShowHeaderWhenEmpty="True"
AllowPaging="false">
<AlternatingRowStyle CssClass="altrowstyle" />
<HeaderStyle CssClass="headerstyle" />
<RowStyle CssClass="rowstyle" />
<RowStyle Wrap="false" />
<HeaderStyle Wrap="false" />
</asp:GridView>
and my onRowCreate function looks like:
protected void myGrid_RowDataBound(object sender, GridViewRowEventArgs e)
{
GridViewRow gvr = e.Row;
if (gvr.RowType == DataControlRowType.Header)
{
gvr.Cells[1].Text = "test1";
}
}
the column name does change to test1 but the sorting feature goes off.what should I do that will change the coulmn name as well as the sorting feature also stays?there is no problem in my sorting.Only when I write the above code,the sorting option for that column goes off.
Please help!
thank you.
The problem is because Sorting sets a LinkButton control in the GridView columns name, and setting the name in the way you do it, disable that control. So, you must set the name of the column with a code like:
((LinkButton)e.Row.Cells[1].Controls[0]).Text = "Test1";
I'm guessing you're trying to use AutoGenerateColumns?
If this is the case why can't you change the column name in the datasource (using "AS" if the datasource is SQL)?
Otherwise, your altering the cell text is short-circuiting the ASP.NET functionality which generates the sorting javascript postback.
Alternatively you would do this if you don't use AutoGenerateColumns:
myGrid.Columns[1].HeaderText = "test1";
I think your problem comes from the postback sent when you order your grid. You don't want to rebind your grid after ordering it.
In your pageLoad you should add :
If(!PostBack)
// the code to bind data to your grid
This way you prevent the grid to be reloaded and the information you set in myGrid_RowDataBound for the name your column should stay the same...

DataKeyNames for different tables with different primary key names

I'm wondering if there is a way to use datakeynames to access the primary keys of different tables if those primary key names are different in those tables.
For example, I have a table called Table1 and Table2 where the primary keys are Table1ID and Table2ID respectively.
My ultimate goal is to combine the contents of both tables into 1 gridview, and to generate a hyperlink for both every row. So my c# looks something like this:
protected void Page_Load(object sender, EventArgs e)
{
SqlCommand command = new SqlCommand("(SELECT Table1ID, Title FROM Table1" +
"UNION ALL SELECT Table2ID, Title FROM Table2)", connection);
GridView1.DataBind();
}
Then in my Gridview_Databound method, I go through and set the hyperlinks for each row individually. The problem is how do I access Table1ID and Table2ID? In my .aspx code, can I set that to be a BoundField because the DataField won't be consistent? I can't just do the following because then it neglects Table2ID right?
<asp:BoundField DataField="Table1ID"/>
Maybe I need to use a templateField or something?
You can use templatefield as below code
<asp:TemplateField HeaderText="Action">
<ItemTemplate>
<asp:LinkButton runat="server" ID="lnkEdit" ToolTip="Click to edit" CommandName="EditContent" CommandArgument='<%# Eval("Table1ID") %>' />
</ItemTemplate>
</asp:TemplateField>
Now on your OnRowCommand="GridView1_RowCommand" you will get the pk id of this tables.
In GridView control you can have a TemplateField containing Hyperlink (see the link to my article for details of this solution).
Also, it might be less labor-intensive if you use SqlDataSource assigning your select statement to its property and passing it to GridView.DataSource property. It will take just several lines of code and do the job for you. Regards, AB

Adding cells in GridView

How can i add cell in specific column for each row in gridview,i want to use RowCreated Events.
i have gridview that has 3 columns (ProductName, Price, Count) i get (ProductName, Price) from database, and i want to add value for (count), ex: (Kitkat, 2$ ) i want to add number 5 to (count) column, i want to handle this operation when each row created.
Thanks
Since you haven't shown your markup, I'm going to assume (based on your comment) that the first two columns are <BoundFields>. If that's the case, I would add a third column as a <TemplateField>, place a Label in it, and use the RowDataBound event to add the correct number to the Label.
Here is what the markup would look like:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
onrowdatabound="GridView1_RowDataBound">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product Name" />
<asp:BoundField DataField="Price" HeaderText="Price" />
<asp:TemplateField HeaderText="Count">
<ItemTemplate>
<asp:Label ID="countLbl" runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And the code-behind:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
Label countLbl = (Label)e.Row.FindControl("countLbl");
//Your logic for what number to use should go here, I'm just defaulting to 5.
countLbl.Text = "5";
}

ASP.NET 2.0 with conditional radio button in a GridView

I'm looking to have a GridView displaying "Events" from a database, and then in the last column I want to do another database lookup (for each row) to see if that event is full or not.
If it's not full, display a radio button. If it is full display text "Full".
I think this is best done with OnRowDataBound event but could use a little help.
I can see how to change an asp label text but not display a radio button.
register.aspx:
<asp:GridView ID="GridView2" runat="server" DataSourceID="SqlDataSource2" AutoGenerateColumns=false CellPadding="5" HeaderStyle-BackColor="DarkGray" Width="450px" OnRowDataBound="GridView2_OnRowDataBound">
<Columns>
<asp:BoundField DataField="sessionDate" HeaderText="Date" DataFormatString="{0:D}" />
<asp:BoundField DataField="sessionTime" HeaderText="Time" />
<asp:BoundField DataField="site" HeaderText="Site" />
<asp:BoundField DataField="room" HeaderText="Room" DataFormatString="{0:D}" />
<asp:TemplateField HeaderText="">
<ItemTemplate>
<input type="radio" name="rdoSessionID" value='<%# Eval("ses_ID") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<HeaderStyle BackColor="#99CCFF" />
</asp:GridView>
<br />
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:sqlConnection2 %>"
SelectCommand="SELECT * FROM dbo.sessions WHERE site LIKE #ses_site AND sessionDate = #ses_date ORDER BY sessionDate">
<SelectParameters>
<asp:SessionParameter name="ses_site" SessionField="ses_site" Type="String" />
<asp:SessionParameter name="ses_date" SessionField="ses_date" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
There are a few ways you can do this.
You can do this on the OnRowDataBound , If the Item you're dealing with is a DataItem, grab the value you need from the current row, and do a quick DB Lookup
When you get your resultset back from the database, do another lookup on each event. This way when your data leaves your data layer / methods you already have everything you need.
Modify your SQL query to return this information with the list of events. You already know the Events, you could just have a sub query to query to see if it's full or not (or the # of people registered, and the Max # allowed so you can show that). This way you only have to do 1 hit against the database and it will save you a bunch of processing. (My favorite)
You would still need to overload the OnRowDataBound event for all 3 of those solutions. You'd hide the radio button if it was full, and ensure it was visible if the event was not full. The difference is where do you get your data.
Do you want to do:
1 Good hit (list of events)
X amounts of small hits for each event
or
1 Good hit for list of events + if each event is full or not.
If you want to entertain #3, post your SQL Query and we can go from there.
Quick example of the OnRowDataBound event.
protected void MyGridView_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
CheckBox cbox = (CheckBox)e.Row.FindControl("chkBox");
// Do some funky logic
cbox.Visible = Event.HasRoom; //Boolean Propery
// Or
cbox.Visible = Convert.ToBoolean(DataBinder.Eval(e.Row.DataItem, "HasRoom").ToString());
}
}
Update 1:
For your GridView, You can use
<ItemTemplate>
<asp:CheckBox runat="server" ID="Checkbox1" ToolTip=" Sign up For event " text="Event stuff" />
</ItemTemplate>
If you want to use a template, don't use a regular control, use a control.
For the query you could do the following. I'm not 100% sure on your table structure so I did it the way I normally would:
Table: Session (sess_id(PK), SessionDate, SessionTime, Site, Room)
Table: Registrants (RegistrantID (PK), sess_id (FK to Session), UserID (FK to users that are registered)
SELECT SessionDate, SessionTime, Site, Room, ses_ID,
(SELECT Count(ses_ID) FROM Registrants R WHERE R.Ses_ID= S.ses_Id) as [Registrants]
FROM dbo.Sessions s
Use the OnRowDataBound event, like this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
RadioButton radio = e.Row.FindControl("RadioButton1") as RadioButton;
if (radio != null)
{
radio.Visible = SomeCheckThatReturnsBoolean((int)GridView1.DataKeys[e.Row.RowIndex]["SomeID"]);
}
}
If possible though, you should return the data with the GridView results, and store the value in a data key, so you can do something like this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
RadioButton radio = e.Row.FindControl("RadioButton1") as RadioButton;
if (radio != null)
{
radio.Visible = (bool)GridView1.DataKeys[e.Row.RowIndex]["SomeBooleanValue"];
}
}
EDIT: Based on your comment to #Ryan, I think you should be able to include that flag in your query. I don't know your database at all, but you can try using a derived table or subquery to get the registrant session counts. Here's a rough example to work off of:
SELECT ID,
ISNULL(Registrants.RegistrantCount, 0) RegistrantCount
...
FROM Table1 t1
LEFT OUTER JOIN (
SELECT ForeignKeyID,
COUNT(RegistrantID) RegistrantCount
FROM Registrants
GROUP BY ForeignKeyID
) Registrants
ON Registrants.ForeignKeyID = t1.ID

Categories