Gridview Delete Button - Object Reference Error - c#

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.

Related

How to get a column of checkboxes in Gridview?

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());
}
}
}

Is it possible to change gridviewid to to a gridview DataSouce after the gridview is loaded

Is it possible to change a gridviews DataSouceId to a gridview DataSouce after the gridview is loaded. I have a gridview with a DataSouceId that is equal to SqlDatasource. I have a button when pressed executes a some LINQ which I want to change what's in the gridview.
var sce = from pk in db.Tables
where pk.Score > 20
select new { pk.First_Name, pk.Last_Name, pk.Score };
GridView1.DataSourceID = "";
GridView1.DataSource = sce;
GridView1.DataBind();
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="First Name" DataSourceID="SqlDataSource2" AllowSorting="True">
<Columns>
<asp:BoundField DataField="First Name" HeaderText="First Name" ReadOnly="True" SortExpression="First Name" />
<asp:BoundField DataField="Last Name" HeaderText="Last Name" SortExpression="Last Name" />
<asp:BoundField DataField="Score" HeaderText="Score" SortExpression="Score" />
<asp:BoundField DataField="Nationality" HeaderText="Nationality" SortExpression="Nationality" />
</Columns>
</asp:GridView>
I tried to change to a empty string. This compiles but crashes when the button is clicked.
Is what I'm trying possible?
The problem is that the query defines columns with different names from what the GridView expects. For example, compare:
pk.First_Name
and
DataField="First Name"
These are different, theerefore GridView is not able to find the column in data source and throws exception.
The best option would be to use underscore in GridView as well. That might require some refactoring in your initial SQL, but it should e done anyway - having spaces in column names is very brittle and uncommon, better refrain from it altogether:
<asp:BoundField DataField="First_Name" ...
Or you can define a TemplateField that looks for column with either name and displays what was found. But that sounds like a terribly huge overhead.

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...

Selecting multiple rows in GridView with checkboxes: Program not pulling data

I have been coming across a strange bug/error in my code when attempting to select and pull data from multiple rows in GridView controls.
Background:
In short, I have a GridView item, populated with rows of different equipment items that are "checked in" and available to check out of our system. I want to have the user able to select multiple items and check them out at once, rather than doing it X times.
I am using a check box item on each row to do this, like so: http://i.imgur.com/fPYV2.png
There is a button at the bottom to check the equipment out of our database.
Code:
The code I am using to generate the ASPX page is:
<asp:GridView ID="grdEquipment" runat="server" AutoGenerateColumns="false" ShowHeaderWhenEmpty="true" CssClass="table table-bordered">
<Columns>
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox ID="chkMultiSelect_Out" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Equipment Name" DataField="Name" />
<asp:BoundField HeaderText="Category" DataField="Category.Name" />
</Columns>
<EmptyDataTemplate>
There is no equipment to display.
</EmptyDataTemplate>
</asp:GridView>
<div class="well">
<asp:Button ID="btnCheckOut" runat="server" CssClass="btn btn-small btn-inverse" Text="Check Out" OnClick="btnCheckOut_Click" />
</div>
This works and compiles with no problem.
The code running the button to check everything out is:
protected void btnCheckOut_Click(object sender, EventArgs e)
{
int checkoutNumber = 0;
//string id = (sender as Button).Attributes["data-id"].ToString();
SortedBindingList<Tracker.Business.Equipment> eqList = Tracker.Business.EquipmentList.FetchAll();
foreach (GridViewRow row in this.grdEquipment.Rows)
{
CheckBox chkMultiSelect_Out = (CheckBox)row.FindControl("chkMultiSelect_Out") ;
if (chkMultiSelect_Out.Checked)
{
checkoutNumber++;
Tracker.Business.Equipment equip = Tracker.Business.Equipment.GetByIdentification(Int32.Parse(row.ID.ToString()));
eqList.Add(equip);
}
}
//If checkoutNumber is 0, do nothing.
//If checkoutNumber is 1, launch popup for only 1 item
//If checkoutNumber is more than 1, launch popup for multiple items.
if (checkoutNumber == 0)
{
Response.Redirect("~/Equipment/CheckInOut/Default.aspx");
}
else if (checkoutNumber == 1)
{
}
else if (checkoutNumber > 1)
{
}
}
Now, OBVIOUSLY, this code isn't finished. I am debugging as I go to make my life easier.
The important code is the foreach loop where I check each row for the control item, and see if it is checked or not.
The Problem:
When I run the code, and test with some check boxes checked, it looks at all the rows, and ALL of them have "Checked = false" as an attribute. What's worse, I found out later that it doesn't even grab the data from the row, as shown in this screen shot here: http://i.imgur.com/clRuk.png
The text should be "Optimus Prime" or "Switch #1", however it is not! And both are checked when I run the code, yet the code sees only false checked items.
The code runs through the foreach loop for each row, i.e., 3 rows, it runs through the loop 3 times. So it sees each row, yet it is not pulling the data...
Where should I start looking?
I fixed my problem. As Tim Schmelter said, I was postbacking the page on every click, so I changed my code to databind the gridview table only on a page load, and not on a post back. Works now!

Checked checkbox in gridview, in c# codebehind checked property is false

I have custom upload control. The control has gridview with the uploaded documents and the first column has checkbox for selecting documents I want to delete by clicking Delete button. The upload control is contained in other UserControls almost everywhere in the application and works properly, except at one control. The problem is when I check some document for deleting, when the code executes the checked property of the checkbox is false.
<asp:GridView ID="gvFiles" runat="server" AutoGenerateColumns="false" ShowHeader="true"
CssClass="DataGrid" Width="100%" OnRowDataBound="gvFiles_RowDataBound">
<HeaderStyle HorizontalAlign="left" CssClass="UploadControlGridHeader" />
<RowStyle CssClass="dlcell" />
<EditRowStyle CssClass="dlsell" />
<Columns>
<asp:TemplateField HeaderText="Delete">
<ItemStyle Width="8%" HorizontalAlign="Center" />
<ItemTemplate>
<asp:CheckBox ID="chkFile" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and this is the delete button event
protected void btnDelete_Click(object sender, EventArgs e)
{
for (int i = 0; i < gvFiles.Rows.Count; i++)
{
CheckBox chk = (CheckBox)gvFiles.Rows[i].Cells[0].FindControl("chkFile");
if (chk.Checked)
{
// If file is marked/checked to be deleted
// then delete it.
DeleteFile(i);
}
}
Session[KEY_VIEWSTATEFILES] = this.FileList;
// Repopulate gridview
BindToGridView();
// Call command to keep same visible screen.
CustomCommandEventArgs command = new CustomCommandEventArgs(COMMAND_FILE_DELETED, null);
OnHappyCommand(command);
}
Not having your DeleteFile(i) code, I can't be sure, but I will take a guess that into that function you remove/delete the row. Am I right? If yes, then it change the index of all your rows, which make you think it is not checked when you check the row that you remember to have checked. You will have to use a backward for loop to fix this.

Categories