How to get an unselected value from a gridView - c#

i need to be able to delete a record from a GridView, but in order to do so, i need to to a method on the .cs file. The thing is that i have other tables that have the ID of a train associated with it, so i need to display an error message if the delete command fails. My problem is that, i can only delete a record when that record is selected.
(GridView2.SelectedDataKey). I want to be able to delete it when it's not selected.
Something like GriView2.Datakey .....
aspx page:
<LinkButton ID="LinkButton3" runat="server" CausesValidation="False"
CommandName="" Text="DELETE" OnClick ="del" >
.cs code:
protected void del(object sender, EventArgs e)
{
string DeleteSql = "DELETE FROM [Trains] WHERE [ID_Train] = #ID_Train";
SqlCommand com = new SqlCommand(DeleteSql, Connection);
String key = GridView2.SelectedDataKey["ID_Train"].ToString();
try
{
com.Parameters.AddWithValue("#ID_Train", key);
cn.Open();
com.ExecuteNonQuery();
cn.Close();
Response.Redirect("Trains.aspx");
}
catch (Exception)
{
Label5.Text = "Error";
Label5.Visible = true;
}
}

Assuming you have a template construct similar to this:
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" CommandName="Delete" Text="Delete"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
AND you have a Delete command in your SqlDataSource, you can process the delete command and access the datakeys in the RowCommand Event like this (Note I converted this from VB so the C# might be off, but you get the idea):
private void GridView1_RowCommand(object sender, System.Web.UI.WebControls.GridViewCommandEventArgs e) Handles GridView1.RowCommand
{
if( e.CommandName == "Delete" ) {
string TrainID = GridView1.DataKeys(e.Row.RowIndex).Values("ID_Train");
integer rc = 0;
GridView1.DeleteParameters.Clear();
GridView1.DeleteParameters.Add("Train_ID", TrainId); // Adjust as necessary
try{
rc = GridView1.Delete();
if( rc == 1) {
// Deleted
} else {
// Not Deleted
}
}
catch() {
Label5.Text = "Error";
Label5.Visible = true;
}
}
}

Related

How do I add new DataSource to an already Databinded CheckBoxList

i'm building a web form that show Database's item(Tables, Rows, FK,...)
I have a CheckBoxList of Tables (chkListTable) which will show a new CheckBoxList of Rows (chkListRow) everytime I SelectedIndexChanged from chkListTable. The problem is i can show the items from chkListTable with 1 selected item. But i don't know how to show chkListRow if multiple item from chkListTable are selected.
Here are my codes:
aspx:
<div>
<asp:Label ID="Label2" runat="server" Text="Table: "></asp:Label>
<asp:CheckBoxList ID="chkListTable" runat="server"
DataTextField="name"
DataValueFeild="name"
AutoPostBack="true"
OnSelectedIndexChanged="chkListTable_SelectedIndexChanged">
</asp:CheckBoxList>
</div>
<div>
<asp:CheckBoxList ID="chkListRow" runat="server"
DataTextField="COLUMN_NAME"
DataValueField="COLUMN_NAME"
RepeatDirection="Horizontal">
</asp:CheckBoxList>
</div>
aspx.cs:
protected void chkListTable_SelectedIndexChanged(object sender, EventArgs e)
{
tableName.Clear();
foreach (ListItem item in chkListTable.Items)
{
if(item.Selected)
{
tableName.Add(item.Text.Trim());
}
}
for(int i = 0; i < tableName.Count; i++)
{
String query = "USE " + dbname +
" SELECT * FROM information_schema.columns" +
" WHERE table_name = '" + tableName[i] + "'" +
" AND COLUMN_NAME != 'rowguid'";
chkListRow.DataSource = Program.ExecSqlDataReader(query);
chkListRow.DataBind();
Program.conn.Close();
}
}
Program.cs:
public static bool Connect()
{
if (Program.conn != null && Program.conn.State == ConnectionState.Open)
Program.conn.Close();
try
{
Program.conn.ConnectionString = Program.constr;
Program.conn.Open();
return true;
}
catch (Exception e)
{
return false;
}
}
public static SqlDataReader ExecSqlDataReader(String query)
{
SqlDataReader myreader;
SqlCommand sqlcmd = new SqlCommand(query, Program.conn);
sqlcmd.CommandType = CommandType.Text;
if (Program.conn.State == ConnectionState.Closed) Program.conn.Open();
try
{
myreader = sqlcmd.ExecuteReader();
return myreader;
myreader.Close();
}
catch (SqlException ex)
{
Program.conn.Close();
return null;
}
}
I want my display to be like this:
[x]Table1 [x]Table2 [ ]Table3
[ ]Row1(Table1) [ ]Row2(Table1) [ ]Row3(Table1)
[ ]Row1(Table2) [ ]Row2(Table2)
Ok, this is a rather cute little problem.
So, if we select 1 table, then we need to have one "child" or so called ONE check box list.
but, if we select 2 tables, (or 5), then we need 2 (or 5) child check box lists.
In other words, we would not use the same check box list (child), or try to "mangle" that "N" number of check box lists we need.
So, this "child" sets (one for each checked table) is NOT known ahead of time.
So, we have "repeating" set of those child check box lists, right?
So, we can (and should) then use a "repeater". All a repeater does is "repeat" the whatever we want, and we "feed" the repeater the main list of tables.
So, our markup will now look like this:
<style>
.rBut input {margin-right: 5px; }
.rBut label {margin-right: 15px; }
</style>
<asp:Label ID="Label2" runat="server" Text="Table: "></asp:Label>
<div class="rBut">
<asp:CheckBoxList ID="chkListTable" runat="server"
DataTextField="TABLE_NAME"
DataValueFeild="TABLE_NAME"
AutoPostBack="true" RepeatDirection="Horizontal" OnSelectedIndexChanged="chkListTable_SelectedIndexChanged" >
</asp:CheckBoxList>
</div>
</div>
<div>
<asp:Repeater ID="Repeater1" runat="server" OnItemDataBound="Repeater1_ItemDataBound">
<ItemTemplate>
Table: <%# Eval("Table") %> -
<div class="rBut">
<asp:CheckBoxList ID="chkListRow" runat="server"
RepeatDirection="Horizontal">
</asp:CheckBoxList>
</div>
</ItemTemplate>
</asp:Repeater>
Note for the 2nd repeter, we do NOT set the Value and text columns - WE DO NOT know them yet. You did not mention which columns to display, but most of my tables always have a PK "ID" for the first column, so lets make that the value , and the display (DataTextField, the 2nd column in the given table).
So, now we build up a "list" of the first selections, (tables), and then pass that thing to the repeater, and it will repeat.
the code now looks like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadData();
}
void LoadData()
{
string strSQL = "SELECT TABLE_NAME FROM VideoGames.INFORMATION_SCHEMA.TABLES " +
"WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME";
chkListTable.DataSource = MyRst(strSQL);
chkListTable.DataBind();
}
protected void chkListTable_SelectedIndexChanged(object sender, EventArgs e)
{
DataTable MyTables = new DataTable();
MyTables.Columns.Add("Table", typeof(string));
foreach (ListItem OneTable in chkListTable.Items)
{
if (OneTable.Selected)
{
DataRow OneRow = MyTables.NewRow();
OneRow["Table"] = OneTable.Value;
MyTables.Rows.Add(OneRow);
}
}
// ok, we have a list of tables, send that to repeater
Repeater1.DataSource = MyTables;
Repeater1.DataBind();
}
public DataTable MyRst(string strSQL)
{
var rst = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.VideoGames))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rst.Load(cmdSQL.ExecuteReader());
}
}
return rst;
}
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item
| e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRowView rItem = e.Item.DataItem as DataRowView; // get binding data row
CheckBoxList chkListRow = e.Item.FindControl("chkListRow") as CheckBoxList;
string strSQL = "SELECT * FROM " + rItem["Table"].ToString();
DataTable MyTable = MyRst(strSQL);
chkListRow.DataValueField = MyTable.Columns[0].ColumnName;
chkListRow.DataTextField = MyTable.Columns[1].ColumnName;
chkListRow.DataSource = MyRst(strSQL);
chkListRow.DataBind();
}
}
So, now we see this:
If I click one, then I see this:
But, say I click 3, then I see this:
So, note how I feed the Repeater a table (could have been sql query, but in this case, we create table in code). Pass it to repeater.
For each table, the itemdatabound triggers. Because there are multiple copies of the tables, then we need to use find control.
And if you wanted to test/get all of the checked items, then we do a for each on the Repeater items - and again use find control to get each check box control.
But note how we only have two check box lists, but the 2nd one is inside of that repeater, and is data driven.
As a result, this will work for 1, or "N" tables.

Maintain sorting throughout paging in gridview in asp.net

How to maintain sorting throughout paging in gridview in asp.net.
Below is my code behind for binding grid, paging, and sorting
private string SortDirection
{
get { return ViewState["SortDirection"] != null ? ViewState["SortDirection"].ToString() : "ASC"; }
set { ViewState["SortDirection"] = value; }
}
private void BindGV(string sortExpression = null)
{
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection con = new SqlConnection(CS);
SqlCommand cmd = new SqlCommand("SPGetEmpDetailes", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter sda = new SqlDataAdapter(cmd);
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
if (sortExpression != null)
{
DataView dv = dt.AsDataView();
this.SortDirection = this.SortDirection == "ASC" ? "DESC" : "ASC";
dv.Sort = sortExpression + " " + this.SortDirection;
EmployeeGV.DataSource = dv;
}
else
{
EmployeeGV.DataSource = dt;
}
EmployeeGV.DataBind();
}
}
I am new to .NET and I want to maintain sorting throughout paging but I don't know how to do it.
protected void EmployeeGV_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
EmployeeGV.PageIndex = e.NewPageIndex;
BindGV();
}
protected void EmployeeGV_Sorting(object sender, GridViewSortEventArgs e)
{
this.BindGV(e.SortExpression);
}
Ok, so since we have a data pager here, then that means the data page routine will re-bind the GV. So, that means:
Loading up - first page load - we fill the grid
When we page - we fill the grid
When we sort - we fill the grid.
So, from above, we see at least 3 places. That suggests we need (and want) ONE routine to load up the grid - since we have several places where we will do this.
And I see you have one routine for this. We tweak it up a bit.
Next up:
The sort value we get/have does not persit, so once again we can use ViewState for that. (and again I see you attempting to use that).
So, lets lay this out.
First up, and NEVER forget this rule:
You want to load combo boxes, grids, listbox and ANY data on your FIRST page load, and ONLY your first page load, and ONLY first time. If you break this rule then you in effect cannot build a working web forms page - you just can't!!!!
And the reason is simple:
Any button click, any post-back, and any code operations on that page will ALWAYS and I repeat ALWAYS will trigger the page load event first and then YOUR event - (button click, paging or whatever). So, that means we ALWAYS will have our page load code stub and have a if (!IsPostBack) code stub - (darn near always follow this design pattern).
Ok, so lets code this up.
next up: I see no reason to put say a datatable in some using block - they safe dispose on their own. However, adopting using blocks for sql connections, and things like a sql command object - yes, do apply a using block.
Ok, I think we quite much cleared up the base issues.
So, lets setup the code, say like this:
So, say our simple markup - we dropped in a row select button.
<div style="padding:35px;width:55%">
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table"
AllowPaging="True" OnPageIndexChanging="GridView1_PageIndexChanging" PageSize="8" AllowSorting="True" OnSorting="GridView1_Sorting">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last" SortExpression="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" SortExpression="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
<asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />
<asp:CheckBoxField DataField="Active" HeaderText="Active" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" Text="View" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle CssClass="GridPager" />
</asp:GridView>
</div>
Ok, now our code, say this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState["MySort"] = "HotelName";
BindGV();
}
}
void BindGV()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * FROM MyHotels", conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
// apply optional sort
if (ViewState["MySort"] != null)
rstData.DefaultView.Sort = ViewState["MySort"] as string;
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGV();
}
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
ViewState["MySort"] = e.SortExpression;
BindGV();
}
And now we get this:
So, the trick or approach?
We have ONE view sate that holds our sort - when we want to sort, we simply put into this ViewState["MySort"] = some column to sort. After that, our bind + load routines are free to run - and we sort if set.
Thus, the pager routine, sort routine, and load routine -- they all call that one routine, and thus we don't have to write a lot of code.
And the view button? Well, if we click on that button (say we add a click event) like this:
Then the row button click can be this:
protected void Unnamed_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
Debug.Print("Row index click = " + gRow.RowIndex.ToString());
// get database (hidden pk row id
int? PKID = GridView1.DataKeys[gRow.RowIndex]["ID"] as int?;
Debug.Print("Database PK = " + PKID.ToString());
Debug.Print("Hotel name this row = " + gRow.Cells[2].Text);
}
And clicking, we get:

C# ASP.NET Add "remove button" to List in grid-view

I am making a checkout program using C# and ASP.NET (because it has to work for offline(as a backup checkout register)).
I got the list working into a GridView but i want to make per added list added to the GridView that it will automatically make a "remove button".
I have tried to find in on the internet but most are database related(removing an item in database) and not just removing a row in a GridView.
what i have:
public static List<KassaItem> KassaList = new List<KassaItem>();
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Focus();
}
public string Connection(string eannr)
{
// connection stuff here
}
public class KassaItem
{
public string EanNr { get; set; }
public string zoekName { get; set; }
public int Quantity { get; set; }
public double Price { get; set; }
public KassaItem(string eanNr,string zoekname, int quantity, double price)
{
EanNr = eanNr;
zoekName = zoekname;
Quantity = quantity;
Price = price;
}
}
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
double TotalPrice = 0;
string zoekfunctie = Connection(TextBox1.Text);
string[] zoekSplit = zoekfunctie.Split('-');
string zoekNaam = zoekSplit[1];
double Prijs = Convert.ToDouble(zoekSplit[0]);
List<KassaItem> KassaNew = new List<KassaItem>();
bool isNew = true;
if (TextBox1.Text != "")
{
foreach (var KassaItem in KassaList)
{
if (TextBox1.Text == KassaItem.EanNr)
{
KassaNew.Add(new KassaItem(KassaItem.EanNr, KassaItem.zoekName, KassaItem.Quantity + 1, KassaItem.Price + Prijs));
isNew = false;
}
else
{
KassaNew.Add(KassaItem);
}
}
if (isNew)
{
KassaNew.Add(new KassaItem(TextBox1.Text, zoekNaam, 1, Prijs));
}
KassaList = KassaNew;
GridView1.DataSource = KassaList;
GridView1.DataBind();
foreach(var item in KassaList)
{
TotalPrice += item.Price;
}
// here i want to make a button
foreach(var item in KassaList)
{
Button RemoveButton = new Button();
RemoveButton.Text = "remove product??";
RemoveButton.ID = "ButtonRemove_" + Item.EanNr;
}
}
TextBox1.Text = "";
TextBox1.Focus();
TotalItems.Text = TotalPrice.ToString();
}
What I want is basically:
how to make a "button" that is assigned to the row when, the row is created to delete the row when it is clicked.
P.S.
I'm also happy if i get a link to a documentation that i might not have seen/missed.
I wish you a happy new year in advance!
Ok, so we have a grid view. We want to drop in a simple plane jane asp.net button, and when we click on this, we delete that row. But we do NOT YET delete from the database. And we could of course have a button below the gv that says "save changes" and then the deletes would actually occur against the database.
So, lets do the first part. GV, load with data, add delete button.
So we have this markup:
<asp:GridView ID="GHotels" runat="server" CssClass="table"
width="50%" AutoGenerateColumns="False" DataKeyNames="ID" >
<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:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText = "Delete" ItemStyle-HorizontalAlign ="Center" >
<ItemTemplate>
<asp:Button ID="cmdDelete" runat="server" Text="Delete"
CssClass="btn"
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="cmdSave" runat="server" Text="Save Changes" CssClass="btn" />
<asp:Button ID="cmdUndo" runat="server" Text="Undo Changes" CssClass="btn"
style="margin-left:25px" />
I dropped in two buttons below the GV - we deal with those later.
So, now here is our code to load the GV - NOTE very careful how we persit the table that drives this table. So we can load the table from database, and now our operations can occur against that table that drives the gv - but we do NOT send the changes back to the database (changes in this case = deletes - but it could be edits or additions if we want - and that's easy too!).
Ok, so our code is this:
DataTable rstData = new DataTable(); // data to drive grid
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadData();
Session["rstData"] = rstData;
LoadGrid();
}
else
{
rstData = Session["rstData"] as DataTable;
}
}
void LoadData()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT * FROM tblHotels ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
}
void LoadGrid()
{
GHotels.DataSource = rstData;
GHotels.DataBind();
}
}
and now we have this:
So, now lets add the code behind event stub for the delete button:
We can't double click on the button to jump to the click code behind event stub, so we have to type into the markup the onclick= (note VERY close how you are THEN offered to create a click event for the button. You see this:
So, we choose create new event (don't look like much occures, but we can now flip to code behind - and you see our event stub created for us.
Ok, so all we have to do is delete (remove) the row from the gv. The code looks like this:
protected void cmdDelete_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int? pkID = (int?)GHotels.DataKeys[gRow.DataItemIndex]["ID"];
// find row and delete
DataRow[] DelRow = rstData.Select("ID = " + pkID);
DelRow[0].Delete();
LoadGrid();
}
Now, the delete code was a bit messy. In MOST cases, I could have done just this:
rstData.Rows[gRow.DataItemIndex].Delete();
However, when you delete from the table, we NOT YET done a rstData.AcceptChanges. So the row sync between table and Gv gets messed up.
Now I COULD DO a accept changes to the table, but if we do that, then we LOSE the deleted rows - and we NEED that for the single final save button that would actually commit and do the deletes. So we use find on the table by PK row id, and we eliminate this issue but MORE important PERSERVE the deleted rows in that data table. (by NOT doing accept changes). And we do that, since now we can send with ONE SINGLE update to the database, the deleted rows. However, as noted, the problem is that when you delete the rstData row, but not accept changes - it STILL exists, an thus when we feed to GV, those deleted rows don't show - but the data index is still set, but dataindex at that point does not match rows from the table.
So, now when we click on the delete button, the row will be deleted. But we not deleted the row from the database.
The undo command? Well, it would just run the same code we have on first page (postback = false) code, and thus we could un-do deletes with great ease.
As you can see - not a lot of code here.
And if you wish, I can post the actual delete code we would use for this to commit the deletes to the database. With above, it is only ONE update command to send the deletes to the database.
Thanks Albert but got a way answer to this.
C#:
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
List<KassaItem> KassaNew = new List<KassaItem>();
TableCell NR = GridView1.Rows[e.RowIndex].Cells[1];
string eanrNR = Convert.ToString(NR.Text);
string zoekfunctie = Connection(eanrNR);
string[] zoekSplit = zoekfunctie.Split('-');
string zoekNaam = zoekSplit[1];
double Prijs = Convert.ToDouble(zoekSplit[0]);
GridViewRow row = GridView1.Rows[e.RowIndex];
TableCell cell = GridView1.Rows[e.RowIndex].Cells[3];
int quantity = Convert.ToInt32(cell.Text);
int newquantity = quantity - 1;
if (newquantity == 0)
{
KassaList.RemoveAt(e.RowIndex);
}
else
{
foreach (var KassaItem in KassaList)
{
if (eanrNR == KassaItem.EanNr)
{
KassaNew.Add(new KassaItem(KassaItem.EanNr, KassaItem.zoekName, newquantity, KassaItem.Price - Prijs));
}
else
{
KassaNew.Add(KassaItem);
}
}
KassaList = KassaNew;
}
foreach (var item in KassaList)
{
TotalPrice += item.Price;
}
GridView1.DataSource = KassaList;
GridView1.DataBind();
TextBox1.Text = "";
TextBox1.Focus();
TotalItems.Text = TotalPrice.ToString();
}
protected void GridView1_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
}

Asp.net gridview with dropdown: Rowindex is always 0 in SelectedIndexChanged event

I have a problem that's been driving me nuts. Any help would be much appreciated. I have a grid view that displays what research items are assigned to what people. It should be possible to change the assignments using a dropdown. Here's the markup:
<asp:GridView ID="assignmentsGridView" runat="server" AutoGenerateColumns="false" CellPadding="2" Font-Size="Smaller"
OnRowDataBound="assignmentsGridView_RowDataBound" OnRowCommand="assignmentsGridView_RowCommand">
<Columns>
<asp:BoundField HeaderText="Ticker" DataField="Item" />
<asp:BoundField HeaderText="Name" DataField="Name" />
<asp:TemplateField HeaderText="Assignment" ItemStyle-HorizontalAlign="Center" ControlStyle-Font-Size="Small">
<ItemTemplate>
<asp:DropDownList ID="assignmentDropDown" runat="server" Visible="true"
OnSelectedIndexChanged="assignmentDropDown_SelectedIndexChanged" AutoPostBack="true" >
<asp:ListItem Text="Joe" Value="Joe"></asp:ListItem>
<asp:ListItem Text="Aron" Value="Aron"></asp:ListItem>
<asp:ListItem Text="Frank" Value="Frank"></asp:ListItem>
<asp:ListItem Text="Ross" Value="Ross"></asp:ListItem>
<asp:ListItem Text="Alex" Value="Alex"></asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Analyst Complete" DataField="AnalystComplete" ItemStyle-HorizontalAlign="Center" ControlStyle-Font-Size="Smaller"/>
<asp:TemplateField HeaderText="Mark Complete" ControlStyle-Font-Size="Smaller">
<ItemTemplate>
<asp:Button ID="completeButton" runat="server" Text="Incomplete" CommandArgument='<%# Eval("Item") %>'
CommandName="Complete" Font-Size="Smaller" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
I want to update the database when the user changes the SelectedIndex of the dropdown. I'm handling that here:
protected void assignmentDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
if (Session["IsCompany"] == null)
{
Session["IsCompany"] = assignmentsDropDown.SelectedValue == "Company";
}
bool? isCompany = Session["IsCompany"] as bool?;
int isCo = (isCompany == true) ? 1 : 0;
DropDownList ddl = sender as DropDownList;
GridViewRow gvr = ddl.NamingContainer as GridViewRow;
//ri is always 0!
int ri = gvr.RowIndex;
gvr = (GridViewRow)(((Control)sender).NamingContainer);
//ri is always 0!
ri = gvr.RowIndex;
gvr = ((GridViewRow)ddl.Parent.Parent);
//ri is always 0!
ri = gvr.RowIndex;
string selAnalyst = ddl.SelectedValue;
//selAnalsyt always = initial value!
selAnalyst = ddl.SelectedItem.Value;
//selAnalsyt always = initial value!
string ticker = gvr.Cells[0].Text;
//ticker always is first data row
}
As you can see in the comments, no matter what row I click, the GridViewRow associated with the sender argument is always the first row. Also, the selected value of the dropdown is always its initial value, not the value selected by the user that fired off the SelectedIndexChanged event.
I've searched for a solution to this problem online and found that it is often caused by (1) the databind event getting fired on postback and (2) duplicate rows in the source data. I have checked for duplicate data and solved that problem and put primary keys on the appropriate table. I'm also making sure not to rebind the data on postback. Here's the Page_Load method:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
}
else
{
string userName = Page.User.Identity.Name;
if (Session["UserMod"] == null)
{
this.userMod = new userModel(userName);
}
else
{
this.userMod = Session["UserMod"] as userModel;
}
if (!IsPostBack)
{
getPeopleList(userName);
getTickerList(userName);
if (this.userMod.IsResearchAdmin)
{
getAdminList();
}
else
{
assignmentsDropDown.Visible = false;
assignmentsGridView.Visible = false;
assignmentsButton.Visible = false;
assignmentsLabel.Visible = false;
addTextBox.Visible = false;
Image1.Visible = true;
analystDropdown.Visible = false;
}
}
}
}
I've got a RowDataBound method as well which sets the selected value of the dropdown for each row. When debugging, I have verified that the method is only running on the initial page load, not after the postback from SelectedIndexChanged. I was using an Update Panel as well, but removed that while trying to debug this.
I'm all outta ideas now, so I'm open to any suggestions. Thanks in advance
It looks like I've discovered an odd idiosyncrasy of asp.net. I had the following code in the RowDataBound method:
if (IsCompany)
{
e.Row.Cells.Remove(e.Row.Cells[1]);
}
I removed this code and the dropdown SelectedIndexChanged method now works correctly. All 3 methods of getting the parent GridViewRow of the dropdownlist are working after changing the RowDataBound method so that it does not remove any cells in the gridview.
I now believe that adding or removing cells during databinding causes unexpected problems with the SelectedIndexChanged event for dropdowns within GridViews. Here is the full code of the RowDataBoundMethod:
protected void assignmentsGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (Session["IsCompany"] == null)
{
Session["IsCompany"] = entityDropDown.SelectedValue == "Company";
}
bool? isCompany = Session["IsCompany"] as bool?;
bool IsCompany = (bool)isCompany;
TableCell cell;
if (e.Row.RowType == DataControlRowType.DataRow)
{
cell = e.Row.Cells[0];
HyperLink link = new HyperLink();
if (IsCompany)
{
link.NavigateUrl = "~/CompanyPage.aspx?Ticker=" + cell.Text;
}
else
{
link.NavigateUrl = "~/PeoplePage.aspx?PersonId=" + cell.Text;
}
link.Text = cell.Text;
cell.Controls.Clear();
cell.Controls.Add(link);
/* with this code included, I experience problems with the SelectedIndexChanged event of the dropdown
if (IsCompany)
{
e.Row.Cells.Remove(e.Row.Cells[1]);
}
*/
cell = e.Row.Cells[2];
var dd = e.Row.FindControl("assignmentDropDown") as DropDownList;
string assignment = ((DataRowView)e.Row.DataItem)["Assignment"].ToString();
foreach (ListItem li in dd.Items)
{
if (li.Value == assignment)
{
li.Selected = true;
break;
}
}
cell = e.Row.Cells[4];
if (cell.Text == "False")
{
//"completeButton"
var cb = e.Row.FindControl("completeButton") as Button;
cb.Enabled = false;
cb.Visible = false;
}
else
{
((Button)cell.FindControl("completeButton")).CommandArgument = e.Row.RowIndex.ToString();
}
}
}
If someone could confirm that removing cells inside rowdatabound causes problems with the selected index change event in asp.net I'd appreciate it.

Get Row Index on Asp.net Rowcommand event

I have an asp.net GridView:
<asp:TemplateField HeaderText="View Faktor" ShowHeader="False" Visible="True">
<ItemTemplate>
<asp:ImageButton ID="imgBtn1" CssClass="SelectRow" runat="server" CausesValidation="false"
CommandArgument='<%#(eval("mprID")) %>' CommandName="ViewFactors" ImageUrl="~/tadarokat/Images/factor.png"
Text="" />
</ItemTemplate>
</asp:TemplateField>
How Can I get rowIndex on row command event?
I want to highlight (select) target row when RowCommand fires.
this is answer for your question.
GridViewRow gvr = (GridViewRow)((ImageButton)e.CommandSource).NamingContainer;
int RowIndex = gvr.RowIndex;
ImageButton \ Button etc.
CommandArgument='<%# Container.DataItemIndex%>'
code-behind
protected void gvProductsList_RowCommand(object sender, GridViewCommandEventArgs e)
{
int index = e.CommandArgument;
}
Or, you can use a control class instead of their types:
GridViewRow row = (GridViewRow)(((Control)e.CommandSource).NamingContainer);
int RowIndex = row.RowIndex;
If you have a built-in command of GridView like insert, update or delete, on row command you can use the following code to get the index:
int index = Convert.ToInt32(e.CommandArgument);
In a custom command, you can set the command argument to yourRow.RowIndex.ToString() and then get it back in the RowCommand event handler. Unless, of course, you need the command argument for another purpose.
I was able to use #rahularyansharma's answer above in my own project, with one minor modification. I needed to get the value of particular cells on the row on which the user clicks a LinkButton. The second line can be modified to get the value of as many cells as you wish.
Here is my solution:
GridViewRow gvr = (GridViewRow)(((LinkButton)e.CommandSource).NamingContainer);
string typecore = gvr.Cells[3].Text.ToString().Trim();
protected void gvProductsList_RowCommand(object sender, GridViewCommandEventArgs e)
{
try
{
if (e.CommandName == "Delete")
{
GridViewRow gvr = (GridViewRow)(((ImageButton)e.CommandSource).NamingContainer);
int RemoveAt = gvr.RowIndex;
DataTable dt = new DataTable();
dt = (DataTable)ViewState["Products"];
dt.Rows.RemoveAt(RemoveAt);
dt.AcceptChanges();
ViewState["Products"] = dt;
}
}
catch (Exception ex)
{
throw;
}
}
protected void gvProductsList_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
try
{
gvProductsList.DataSource = ViewState["Products"];
gvProductsList.DataBind();
}
catch (Exception ex)
{
}
}

Categories

Resources