I have an ASP TemplateField inside a data populated GridView as follows:
<asp:GridView ID="gvReport" runat="server" AutoGenerateColumns="False" ShowHeaderWhenEmpty="true" OnRowDataBound="gvReport_RowDataBound" CssClass="table table-bordered">
<Columns>
<asp:BoundField DataField="Delete" HeaderText="Delete" SortExpression="Delete" />
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:HyperLink runat="server" NavigateUrl='<%# "Edit?from=Delete&ID=" + Eval("ID") %>' ImageUrl="Assets/SmallDelete.png" SortExpression="PathToFile" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
In other words, at the end of each row, there is a delete 'button'. I can tell whether a particular row/record has been deleted by checking the true/false value of the BoundField Delete in my code behind as follows:
if (e.Row.RowType == DataControlRowType.DataRow)
{
TableCell statusCell = e.Row.Cells[0];
if (statusCell.Text == "True")
{
//change ImageUrl of Hyperlink for this row
}
}
Right now, my icon is red for delete, but in the case that a record has already been deleted,
I would like to change the image to a different icon (a blue one).
How can I change this ImageUrl if statusCell evaluates to true?
ok, so we will need two parts here:
We need (want) to persist the data table that drives the grid).
The REASON for this?
We kill about 3 birds with one stone.
We use the data table store/save/know the deleted state
We use that delete information to toggle our image
(and thus don't have to store the state in the grid view).
We ALSO then case use that table state to update the database in ONE shot, and thus don't have to code out a bunch of database operations (delete rows). We just send the table back to the database.
In fact in this example, I can add about 7 lines of code, and if you tab around, edit the data? It ALSO will be sent back to the database. And this means no messy edit templates and all that jazz (which is panful anyway).
Ok, so, here is our grid - just some hotels - and our delete button with image:
markup:
<div style="width:40%;margin-left:20px">
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table table-hover borderhide" >
<Columns>
<asp:TemplateField HeaderText="HotelName" >
<ItemTemplate><asp:TextBox id="HotelName" runat="server" Text='<%# Eval("HotelName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="FirstName" SortExpression="ORDER BY FirstName" >
<ItemTemplate><asp:TextBox id="FirstName" runat="server" Text='<%# Eval("FirstName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="LastName" >
<ItemTemplate><asp:TextBox id="LastName" runat="server" Text='<%# Eval("LastName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="City" >
<ItemTemplate><asp:TextBox id="City" runat="server" Text='<%# Eval("City") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:ImageButton ID="cmdDelete" runat="server" Height="20px" Style="margin-left:10px"
ImageUrl="~/Content/cknosel.png"
Width="24px" OnClick="cmdDelete_Click"></asp:ImageButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="cmdDeleteAll" runat="server" Text="Delete selected" Width="122px"
OnClientClick="return confirm('Delete all selected?')" />
</div>
Ok, and the code to load up the grid - NOTE WE persist the data table!!!!!
Code:
DataTable rstTable = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
else
rstTable = (DataTable)ViewState["rstTable"];
}
void LoadGrid()
{
// load up our grid
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels ORDER BY HotelName ",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
rstTable.Clear();
rstTable.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstTable;
GridView1.DataBind();
}
ViewState["rstTable"] = rstTable;
}
Ok, so far, real plain jane. The result is this:
Ok, so now all we need is to add the delete button click event.
That code looks like this:
protected void cmdDelete_Click(object sender, ImageClickEventArgs e)
{
ImageButton btn = (ImageButton)sender;
GridViewRow rRow = (GridViewRow)btn.Parent.Parent;
DataRow OneRow = rstTable.Rows[rRow.DataItemIndex];
// toggle data
if (OneRow.RowState == DataRowState.Deleted)
{
// undelete
OneRow.RejectChanges();
btn.ImageUrl = "/Content/cknosel.png";
}
else
{
// delete
OneRow.Delete();
btn.ImageUrl = "/Content/ckdelete.png";
}
}
Again, really simple. But NOTE how we use the data table row state (deleted or not).
And this ALSO toggles the image for the delete button.
So, we don't have to care, worry, or store/save the sate in the grid.
So, if I click on a few rows to delete, I now get this:
So far - very little code!!!
Ok, so now the last part. the overall delete selected button. Well, since that is dangerous - note how I tossed in a OnClientClick event to "confirm the delete. if you hit cancel, then of course the server side event does not run.
But, as noted since we persisted the table? Then we can send the table deletes right back to the server without a loop. The delete code thus looks like this:
protected void cmdDeleteAll_Click(object sender, EventArgs e)
{
// send updates (deletes) back to database.
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels WHERE ID = 0",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
SqlDataAdapter daUpdate = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder daUpdateBuild = new SqlCommandBuilder(daUpdate);
daUpdate.Update(rstTable);
}
LoadGrid(); // refesh display
}
So, note how easy it is to update (delete) from the database. If you look close, I used text boxes for that grid - not labels. The reason of course is that if the user tabs around in the grid (and edits - very much like excel), then I can with a few extra lines of code send those changes back to the database. In fact the above delete button code will be 100% identical here (it will send table changes back with the above code. So, if we add rows, edit rows then the above is NOT really a delete command, but is in fact our save edits/deletes/adds command!
Related
I am working on a project in c# asp.net.
I created a gridview and connected it to a database. My code is like that;
<asp:GridView ID="GridView1" runat="server"
class="table table-bordered table table-hover " AutoGenerateColumns="false" HeaderStyle-Height="40px" OnRowCommand="GridView1_RowCommand1" >
<Columns>
<asp:TemplateField HeaderText="Numune Parçası" >
<ItemTemplate>
<asp:Literal ID="Literal22" runat="server" Text='<%# Eval("Açıklama") %>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Analiz">
<ItemTemplate>
<asp:Literal ID="Literal112" runat="server" Text='<%# Eval("Analiz") %>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Tartım" ItemStyle-Width="10%">
<ItemTemplate>
<asp:TextBox id="txttartim" runat="server" class="form-control" ></asp:TextBox>
</ItemTemplate>
<ItemStyle Width="10%"></ItemStyle>
</asp:TemplateField>
<asp:TemplateField HeaderText="Birim">
<ItemTemplate>
<asp:DropDownList ID="birimlist" class="form-control" runat="server" >
<asp:ListItem>g</asp:ListItem>
<asp:ListItem>mg</asp:ListItem>
<asp:ListItem>ml</asp:ListItem>
<asp:ListItem>cm2</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Kaydet">
<ItemTemplate>
<asp:LinkButton ID="Btn_Indir" runat="server"
class="btn btn-primary btn-sm" Text="Kaydet" CommandName="Open" CommandArgument='<%# Eval("ID") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<HeaderStyle BackColor="#D14124" CssClass="gridheader" ForeColor="White" HorizontalAlign="Left" />
</asp:GridView>
And the code behind is like that
protected void GridView1_RowCommand1(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Open")
{
int RowIndex = ((GridViewRow)((LinkButton)e.CommandSource).NamingContainer).RowIndex;
string name = ((TextBox)GridView1.Rows[RowIndex].FindControl("txttartim")).Text;
DropDownList bir = (DropDownList)GridView1.Rows[RowIndex].FindControl("birimlist");
string birim = bir.SelectedItem.Value;
}
}
But my problem is that i can not get the value of textbox and dropdownlist in which selected row.
The strings name and birim is null.
Give this a go:
if (e.CommandName == "Open")
{
GridViewRow selectedRow = ((GridViewRow)((LinkButton)e.CommandSource).NamingContainer);
string name = ((TextBox)selectedRow.FindControl("txttartim")).Text;
DropDownList bir = (DropDownList)selectedRow.FindControl("birimlist");
string birim = bir.SelectedItem.Value;
}
Ok, I often just drop in a plane jane asp.net button, and I don't bother with using the GV built in commands (such as command).
However, you have a command, so you can trigger the "row" command as you have.
And the "row command" fires before the selected index fires, and in fact fires before the selected index triggers.
I note that you passing the "ID". It not really necessary, since a GV has what is called DataKeys feature. That feature lets you get the row (database) PK id, but REALLY nice is you don't have to expose, show, or even use a hidden field, or anything else to get that data key. (this is great for several reasons - one being that you don't have to hide or shove away, or even use the command argument to get that database PK row. The other HUGE issue is then your database pk values are NEVER exposed client side - so nice for security).
Ok, So, make sure your row event is being triggered, and you should be able to use this, first the markup for the Linkbutton:
<asp:TemplateField HeaderText="View" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:LinkButton ID="cmdView" runat="server" Text="View" CommandName="Open" cssclass="btn btn-info" />
</ItemTemplate>
</asp:TemplateField>
Note how I do NOT worry or pass the "ID". As noted, datakeys takes care of this, and in the GV def, we have this:
<asp:GridView ID="GVHotels" runat="server" class="table"
AutoGenerateColumns="false" DataKeyNames="ID"
bla bla bla
So, now our code is this:
protected void GVHotels_RowCommand(object sender, GridViewCommandEventArgs e)
{
LinkButton lBtn = e.CommandSource as LinkButton;
GridViewRow gRow = lBtn.NamingContainer as GridViewRow;
int PKID = (int)GVHotels.DataKeys[gRow.RowIndex]["ID"];
Debug.Print("Row index click = " + gRow.RowIndex);
Debug.Print("Database PK id = " + PKID);
// get combo box for this row
DropDownList cbo = gRow.FindControl("cboRating") as DropDownList;
Debug.Print("Combo box value = " + cbo.SelectedItem.Value);
Debug.Print("Combo box Text = " + cbo.SelectedItem.Text);
Output:
Row index click = 7
Database PK id = 68
Combo box value = 2
Combo box Text = Good
So, use datakeys - you thus DO NOT have have to put in extra attributes here and there and everywhere to get the PK row value.
In fact, really, using row command is often MORE of a pain, and you can DUMP the command name to trigger that button click. And REALLY nice then is you get a separate nice command "code stub" for each button (or link button) in the GV. (no need to try and shove all that code into the row command, and then a bunch of if/then or "case" statements to run code for a given command.
So, I in fact suggest you use this:
<asp:TemplateField HeaderText="View" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:LinkButton ID="cmdView" runat="server" Text="View"
OnClick="cmdView_Click1" cssclass="btn btn-info" />
</ItemTemplate>
</asp:TemplateField>
So, all we did was add a plane jane good old regular click event. It will not fire row command, and in fact will not trigger "selected index changed" for the GV, but in most cases I don't need them. So, now, if you 2 or 5 buttons, you just think of them as plane jane regular buttons. (just type in onclick in teh markup, and choose "create new event" that intel-sense pops up). So, now our code for the button is seperate, and not part of the row command. Our code thus becomes this:
protected void cmdView_Click1(object sender, EventArgs e)
{
LinkButton lBtn = sender as LinkButton;
GridViewRow gRow = lBtn.NamingContainer as GridViewRow;
int PKID = (int)GVHotels.DataKeys[gRow.RowIndex]["ID"];
Debug.Print("Row index click = " + gRow.RowIndex);
Debug.Print("Database PK id = " + PKID);
// get combo box for this row
DropDownList cbo = gRow.FindControl("cboRating") as DropDownList;
Debug.Print("Combo box value = " + cbo.SelectedItem.Value);
Debug.Print("Combo box Text = " + cbo.SelectedItem.Text);
}
So, I tend to not bother with row command - better to just have that plane jane click event. Note the use of naming container - but the rest of the code is the same. I would ONLY use "commandName" to trigger the row event, since THEN the gv index changed event fires - and that useful for highlighting the whole row - but if you don't care, then don't even bother with CommandName - just use regular click events, and as noted, this is even better, since then each button gets it 100% own nice little code stub like any other button click tends to have.
So I grabbed a dropdownlist but your code could be say this:
TextBox tBox = gRow.FindControl("txttartim") as TextBox;
debug.Print (tBox.Text);
I solve the problem.
When i write the code in the Page_load section, it works! It is not null anymore.
if (!Page.IsPostBack)
{
load();
..
}
Assignment Instructions:
Develop a simple shopping cart application that uses session objects to store the information. You will have to create an ASP.NET project that contains two web forms. The first web form, ShoppingCart.aspx, allows the user to select items using a set of checkboxes. This page shows also the number of items in the cart.
The second web form, Display.aspx, displays the number of selected items in a table and allows the user to go back to the first web form and continue shopping.
I am using a CheckBoxList to list my products, and I built a table with IDs in the cells to receive the data the user checks off.
(I considered the idea of using CheckBox vs. CheckBoxList, but this would not be the appropriate solution and doesn't easily allow counting total items selected)
I have tried using cookies and session state variables, but I can't figure out how to associate them to the table.
I can transfer test data from labels and textboxes, anything with an ID, but not my CheckBoxList items.
How do I reference which checkboxes are selected on page 1, that will update the desired table cells on page 2, after clicking the 'checkout' button without unique IDs on the list items?
Example: If... 1lb Dark Roast is selected on page 1 ...
(identified as 'p001' for product 1 on next page)
<asp:ListItem Value="15.99">1lb Dark Roast</asp:ListItem>
On page 2 (Display.aspx) the Amount column should update to '1' beside that product
<asp:TableCell ID="p001_amt" runat="server"></asp:TableCell>
If an item is checked on page 1, this should be the result... (along with the other products).
Product
Price
Amount
1lb dark Roast
$15.99
1
1lb Med Roast
$15.99
0
1 = checked, 0 = unchecked (assignment desired outcome shows this table)
Any help would be appreciated. Thanks.
Here are snippets from my code, if more is needed I can add a link to the full pages.
Page 1 (ShoppingCart.aspx)
ShoppingCart.aspx
------------------
<asp:Label ID="TotalCart_lbl" runat="server" Text="Total Items in your Cart: "></asp:Label>
<br />
<asp:CheckBoxList ID="Shop_ckbx" runat="server">
<asp:ListItem Value="15.99">1lb Dark Roast</asp:ListItem>
<asp:ListItem Value="15.99">1lb Medium Roast</asp:ListItem>
<asp:ListItem Value="12.99">1lb Decaf Roast</asp:ListItem>
<asp:ListItem Value="16.99">1lb Cold Brew</asp:ListItem>
<asp:ListItem Value="10.99">1 box Tea</asp:ListItem>
<asp:ListItem Value="35.99">French Press</asp:ListItem>
<asp:ListItem Value="8.99">CCM Mug</asp:ListItem>
</asp:CheckBoxList>
<br />
<asp:Button ID="UpdateCart_btn" runat="server" Text="Update Your cart" OnClick="UpdateCart_btn_Click"/>
<br />
<asp:Label ID="Contents_lbl" runat="server" Text="Update To See Cart Contents"></asp:Label>
<br />
<asp:Label ID="Cost_lbl" runat="server" Text="Total: "></asp:Label>
<br />
<asp:Button ID="Checkout_btn" runat="server" PostBackUrl="~/Display.aspx" Text="Go to Checkout" />
ShoppingCart.aspx.cs
---------------------
protected void UpdateCart_btn_Click(object sender, EventArgs e)
{
int total = 0; //Total item count
Contents_lbl.Text = "Your Cart Contains: <br/>- - - - - - - - - - - - - - - -";
foreach (ListItem listItem in Shop_ckbx.Items)
{
if (listItem.Selected == true)
{
//Add Text to label
Contents_lbl.Text += "<br/>• " + listItem.Text;
//Count Items in Cart
total += 1;
}
}
Contents_lbl.Text += "<br/>";
for (int i = 0; i < Shop_ckbx.Items.Count; i++)
{
if (Shop_ckbx.Items[i].Selected)
{
cost += Convert.ToDouble(Shop_ckbx.Items[i].Value);
Cost_lbl.Text = "Total: $ " + cost.ToString();
}
}
//Update total items
TotalCart_lbl.Text = "Total Items in Your Cart: " + total;
}
protected void Checkout_btn_Click(object sender, EventArgs e)
{
How do I reference the items in the checklist on page 1
to update the table on page 2 without unique IDs on each list item?
}
Page 2 code (Display.aspx)
Display.aspx
------------------
<asp:Table ID="Display_tbl" runat="server">
<asp:TableRow runat="server" ID="Header_row" Font-Bold="True" Font-Size="Larger" BackColor="#FFCC99">
<asp:TableCell ID="Product_cell" runat="server" Width="250">Product</asp:TableCell>
<asp:TableCell ID="Price_cell" runat="server" Width="100">Price</asp:TableCell>
<asp:TableCell ID="Amount_cell" runat="server" Width="100">Amount</asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p001">
<asp:TableCell ID="p001_prod" runat="server">1lb Dark Roast</asp:TableCell>
<asp:TableCell ID="p001_price" runat="server">$ 15.99</asp:TableCell>
<asp:TableCell ID="p001_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p002" Width="500px">
<asp:TableCell ID="p002_prod" runat="server">1lb Med Roast</asp:TableCell>
<asp:TableCell ID="p002_price" runat="server">$ 15.99</asp:TableCell>
<asp:TableCell ID="p002_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p003">
<asp:TableCell ID="p003_prod" runat="server">1lb Decaf</asp:TableCell>
<asp:TableCell ID="p003_price" runat="server">$ 12.99</asp:TableCell>
<asp:TableCell ID="p003_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p004">
<asp:TableCell ID="p004_prod" runat="server">1lb Cold Brew</asp:TableCell>
<asp:TableCell ID="p004_price" runat="server">$ 16.99</asp:TableCell>
<asp:TableCell ID="p004_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p005">
<asp:TableCell ID="p005_prod" runat="server">1 box Tea (50 bags)</asp:TableCell>
<asp:TableCell ID="p005_price" runat="server">$ 10.99</asp:TableCell>
<asp:TableCell ID="p005_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p006">
<asp:TableCell ID="p006_prod" runat="server">French Press</asp:TableCell>
<asp:TableCell ID="p006_price" runat="server">$ 35.99</asp:TableCell>
<asp:TableCell ID="p006_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="p007">
<asp:TableCell ID="p007_prod" runat="server">CCM Coffee Mug</asp:TableCell>
<asp:TableCell ID="p007_price" runat="server">$ 8.99</asp:TableCell>
<asp:TableCell ID="p007_amt" runat="server"></asp:TableCell>
</asp:TableRow>
<asp:TableRow runat="server" ID="Total_row">
<asp:TableCell ID="Total_prod" runat="server">Total</asp:TableCell>
<asp:TableCell ID="Total_price" runat="server"></asp:TableCell>
<asp:TableCell ID="Total_amt" runat="server"></asp:TableCell>
</asp:TableRow>
</asp:Table>
<asp:LinkButton ID="Back_linkbtn" runat="server" OnClick="Back_linkbtn_Click">Go Back to the Shop</asp:LinkButton>
Display.aspx.cs
-------------------
protected void Page_Load(object sender, EventArgs e)
{
ShoppingCart prevPage = PreviousPage as ShoppingCart;
if (prevPage != null)
{
NOT SURE HOW TO LINK TO MY CHECKLIST ITEMS
TO UPDATE 'AMOUNT' TABLE CELLS
Also,
CHECKBOXES ON PREVIOUS PAGE NEED TO STAY SELECTED
in order to CONTINUE SHOPPING.
}
}
protected void Back_linkbtn_Click(object sender, EventArgs e)
{
Response.Redirect("ShoppingCart.aspx");
}
Ok, so the problem we have here is of course matching up the “thing” you selected on one page, and then figuring this out on the next web page.
To be fair, the problem here is that no doubt you have to "make up" the data here. And that REALLY hurts this whole process, since you having to type in the data two times (once on this page, and then on the target page). Worse, you have to match up by "text". One little space, period etc. and your whole system will fall down.
I going to suggest we NOT worry and NOT have to match up the values. We can in one shot get rid of a HUGE problem, and ALSO reduce the markup, the code and all your life hassles (well, ok, just the coding part - can't fix everything!!!).
So, I going to suggest that in place of the check box "list", we create a table and use that for BOTH pages. This will then not only keep us out of rooms with padded walls, but we can deal with a "row" of something that you select, and just not give one hoot as to the spelling or even what we place in that row. And by doing this, you could with almost no code changes change the "list" from our table to a database - and the whole she-bang would work!!
So, lets use (create) a table, and not only is this easy, but I think far more clean to setup our data.
So, we will have this at the start of the page (a table and some total values for the WHOLE page class.
public class MyFirstOrderPage : System.Web.UI.Page
{
private DataTable MyTable = new DataTable();
private decimal MyTotal = 0;
private int MyTotalQty = 0;
Ok, now lets do the sub in this page to load up the above table.
public void CreateData()
{
MyTable.Columns.Add("Product", typeof(string));
MyTable.Columns.Add("Price", typeof(decimal));
MyTable.Columns.Add("Qty", typeof(int));
MyTable.Columns.Add("Amount", typeof(decimal));
MyTable.Columns.Add("Purchased", typeof(bool));
MyTable.Rows.Add({"11b Dark Roast",15.99,1,0,false});
MyTable.Rows.Add({"11b Medium Roast",15.99,1,0,false});
MyTable.Rows.Add({"1b Decaf Roast",12.99,1,0,false});
MyTable.Rows.Add({"11b Cold Brew",16.99,1,0,false});
MyTable.Rows.Add({"1 box Tea Brew",10.99,1,0,false});
MyTable.Rows.Add({"French Press",35.99,1,0,false});
MyTable.Rows.Add({"CCM Mug",8.99,1,0,false});
}
Now not only can you "easy" read the above, but you can change/view and see the columns and then the data follows - so you can see we decided to have a Qty and also a column for the purchased. this will save BUCKETS of code, and BUCKETS of markup. And we can use the above to BOTH pages - no having to have two copies of this list!!!!
Ok, so now we have our table. next up, lets display that table. And we can use VERY much the same grid in our order page as we can in the check out page (saves even more time, more code and even MORE markup!!!).
So, lets use a gridview. We could use a listview and I would consider that if you wanted a cute picture and some more product description. But, gridview is fine.
So, here is the markup for the gridview:
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server" ShowFooter="true"
Font-Bold="True" Font-Size="Larger" BackColor="#FFCC99" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="Product" HeaderText="Product" Headerstyle-width="250px" />
<asp:BoundField DataField="Price" DataFormatString="{0:C2}" HeaderText="Price" Headerstyle-width="80px" />
<asp:TemplateField HeaderText="Qty" >
<ItemTemplate>
<asp:TextBox ID="Qty" runat="server" Width="60px"
Text ='<%# Eval("Qty") %>'
MyRow = '<%# Container.DataItemIndex %>'
AutoPostBack="true" OnTextChanged="Qty_TextChanged"
/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Amount">
<ItemTemplate>
<asp:TextBox ID="Amount" runat="server" width="80px"
Text ='<%# FormatCurrency(Eval("Qty") * Eval("Price")) %>' Enabled="false" DataFormatString="{0:C2}" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Purchase" ItemStyle-HorizontalAlign="Center">
<ItemTemplate >
<asp:CheckBox ID="CheckBox1" runat="server" width="110px"
Checked ='<%# Eval("Purchased") %>'
MyRow = '<%# Container.DataItemIndex %>'
AutoPostBack="true"
OnCheckedChanged="CheckBox1_CheckedChanged1"
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
</asp:GridView>
<asp:HiddenField ID="qtycheck" runat="server" ClientIDMode="Static" />
<br />
<asp:Button ID="Checkout_btn" runat="server" Text="Proceed to Checkout"
style="width:140px;height:60px;background-color:#FFCC99;border-radius:25px"
OnClientClick="hasorder();return false;"/>
<script>
function hasorder() {
var CheckQty = document.getElementById("qtycheck")
if (CheckQty.value > 0) {
window.location.href = "/CheckOut.aspx"
}
else {
alert("Please select at least one Purchase before proceding to check out");
return false;
}
}
</script>
Now that is a bit of markup, but it is STILL much less then say your 2nd page table, and better you see how our 2nd page now is VERY little markup!!
Ok, so we have the above. The only real issue is that when you check a box, we need to total things up. And when you edit or change the qty, then SAME thing - update the totals.
So, the code we have for each part is again rather limited. I'll post in all in one shot:
protected void Page_Load(object sender, System.EventArgs e)
{
if (System.Web.UI.Page.IsPostBack == false)
{
// ok first page load, now check if reutrn from checkout
if (System.Web.UI.Page.Session["MyTable"] == null)
{
CreateData(); // first time here - create the data
System.Web.UI.Page.Session["MyTable"] = MyTable; // save the data table
}
else
// we have data - (so this is user change of mind from checkout page)
MyTable = System.Web.UI.Page.Session["MyTable"];
// ok, now load our data into the grid
LoadData(); // load the data
}
else
// this is a post back due to general changes and selections for this
// page under normal and mutliple operations and choices by the user.
// we dont' need to re-display the grid - it persists automatic
// however the datatable variable does NOT persist - so get from session
MyTable = System.Web.UI.Page.Session["MyTable"];
}
public void LoadData()
{
MyTotal = 0;
MyTotalQty = 0;
qtycheck.Value = 0;
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
TextBox MyAmount = e.Row.FindControl("Amount");
DataRow MyRow = MyTable.Rows(e.Row.RowIndex);
MyRow("Amount") = MyRow("Qty") * MyRow("Price");
if (MyRow("Purchased") == true)
{
// total up the qty purcchased and the amount
MyTotal += MyRow("Amount");
MyTotalQty += MyRow("Qty");
}
}
else if (e.Row.RowType == DataControlRowType.Footer)
{
// this is our bottom last footer.
e.Row.Cells(3).Text = Strings.FormatCurrency(MyTotal);
e.Row.Cells(2).Text = MyTotalQty;
e.Row.Cells(1).Text = "Totals:";
qtycheck.Value = MyTotalQty; // a hidden control - to check for qty > 0 on checkout
}
}
protected void CheckBox1_CheckedChanged1(object sender, EventArgs e)
{
// user click on purchase a row - udpate totals.
CheckBox cBox = sender;
int RowID = cBox.Attributes("MyRow");
MyTable.Rows(RowID).Item("Purchased") = cBox.Checked;
// data table has been changed - so we call the load data routine to udpate
// our grid
LoadData();
}
protected void Qty_TextChanged(object sender, EventArgs e)
{
// user changed the qry in a row
TextBox tBox = sender;
int RowID = tBox.Attributes("MyRow");
MyTable.Rows(RowID).Item("Qty") = tBox.Text;
// data table has been changed - so we call the load data routine to udpate
// our grid
LoadData();
}
}
Now that is a "bit" of code, but again, not really much.
So, you now get this after running:
So, notice what we do WHEN you check a box!
We simple do this:
protected void CheckBox1_CheckedChanged1(object sender, EventArgs e)
{
// user click on purchase a row - udpate totals.
CheckBox cBox = sender;
int RowID = cBox.Attributes("MyRow");
MyTable.Rows(RowID).Item("Purchased") = cBox.Checked;
// data table has been changed - so we call the load data routine to udpate
// our grid
LoadData();
}
So, now NO MATCHING problem really exists, does it? You click on a row, and we simply change the Purchased setting (a true/false flag). And that is it - done! no matching required. When you thus check a box, we change the table value, and then simply say to the code will you please re-display the data!!! So NOW we are SAVING all that matching work!!!
Now, the check out code becomes a walk in the park.
when you click on the button to check out, we jump to the checkout page.
The markup is now dead simple - since it much the same as the first page - but no ability to change things. Hence this:
<asp:GridView ID="GridView2" runat="server" ShowFooter="true"
Font-Bold="True" Font-Size="Larger" BackColor="#FFCC99"
AutoGenerateColumns="False"
>
<Columns>
<asp:BoundField DataField="Product" HeaderText="Product" Headerstyle-width="250px" />
<asp:BoundField DataField="Price" DataFormatString="{0:C2}" HeaderText="Price" Headerstyle-width="80px" />
<asp:BoundField DataField="Qty" HeaderText="Qty" Headerstyle-width="80px" />
<asp:BoundField DataField="Amount" HeaderText="Amount" Headerstyle-width="80px" />
</Columns>
<FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
</asp:GridView>
<br />
<asp:Button ID="ChangeOrder_btn" runat="server" PostBackUrl="~/SimpleButton.aspx" Text="Change order"
style="width:140px;height:60px;background-color:#FFCC99;border-radius:25px"
/>
<br />
</div>
And the page looks like:
So, you NOW see why we using a table - since the SAME data now drives both pages, and our problem becomes smaller and smaller - not larger and larger!!
And our code for that checkout page, it is this:
class CheckOut : System.Web.UI.Page
{
private decimal MyTotal = 0;
private int MyTotalQty = 0;
protected void Page_Load(object sender, System.EventArgs e)
{
DataTable MyTable = System.Web.UI.Page.Session["MyTable"];
DataView dv = new DataView(MyTable); // we use data view in palce of data table - it can fitler!!
dv.RowFilter = "Purchased = True";
GridView2.DataSource = dv;
GridView2.DataBind();
}
protected void GridView2_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
MyTotal += e.Row.Cells(3).Text;
MyTotalQty += e.Row.Cells(2).Text;
}
else if (e.Row.RowType == DataControlRowType.Footer)
{
e.Row.Cells(3).Text = Strings.FormatCurrency(MyTotal);
e.Row.Cells(2).Text = MyTotalQty;
e.Row.Cells(1).Text = "Totals:";
}
}
}
So, by build a better design and mouse trap with the first page, and a better approach, then the next page becomes VERY simple, since we re-use the data, re-use the table and in fact darn near re-used the grid too!!!
And to display the order we ONLY have to get the table, and filter the results by purchased.
And the button to go back and change your mind? Well, it just jumps back to the that order page.
So while we did have to build up a bit more "markup" in the first page, we reduced by huge amounts the markup in the 2nd page.
I have a gridview that has a quantity and calculated price right next to it. When I change the quantity inside the Gridview, the price changes right away. I want to show total price outside the gridview . I am showing the total price outside the gridview, but the price is not changing dynamically when the quantity and price changes for individual items. Below is the image of what I want. I want to $560 to be displayed and calculated right away as soon as price changes.
Below is my gridview code:
<asp:GridView ID="grdShoppingCart" runat="server" AutoGenerateColumns="false" class="ui-responsive table-stroke ss-table ui-search-result-table" GridLines="None" OnRowDataBound="grdShoppingCart_RowDataBound" >
<Columns>
<asp:BoundField DataField="item" HeaderText="Item" HeaderStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Left" HeaderStyle-HorizontalAlign="Left" ItemStyle-Width="250px" ControlStyle-CssClass="ss-row" />
<asp:TemplateField HeaderText="Description" HeaderStyle-Font-Bold="true" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left" ItemStyle-Width="300px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:Label ID="lblDesc" runat="server" Text='<%# Eval("child") %>' /><br />
Requested By: <asp:Label ID="lblDesc1" runat="server" Text='<%# Eval("RequestedBy") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Quantity" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:TextBox Width="45px" TextMode="Number" ID="txtQuantity" Text='<%#Eval("Quantity") %>' runat="server" AutoPostBack="true" OnTextChanged="txtQuantity_TextChanged" ></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" HeaderStyle-HorizontalAlign="right" ItemStyle-HorizontalAlign="Right" ItemStyle-Width="100px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:Label ID="lblPrice" runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" ItemStyle-Width="150px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:ImageButton CommandArgument='<%# Eval("recordId") %>' ID="imgbtnDelete" runat="server" ImageUrl="~/Images/delete1.png" CommandName="Delete" ToolTip="Click To Dletee" AlternateText="Click To Dletee"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
his is what I tried to do in my code behind to show the total:
I declared this variable inside my class and put the value of price text box in total variable in rowDatabound
public string total;
protected void grdShoppingCart_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Label lbl = (e.Row.FindControl("lblPrice") as Label);
TextBox tbq = (e.Row.FindControl("txtQuantity") as TextBox);
if(tbq.Text !="")
{
lbl.Text = (Convert.ToInt32(tbq.Text) * 28).ToString();
total= lbl.Text.ToString();
}
}
}
and in the aspx page, I wrote this:
Total: <%=total %>
below is the code that changes the price and quantity:
protected void txtQuantity_TextChanged(object sender, EventArgs e)
{
TextBox changeTextbox = sender as TextBox;
GridViewRow row = (GridViewRow)changeTextbox.NamingContainer;
TextBox tbq = (row.FindControl("txtQuantity") as TextBox);
Label lblPrice = (row.FindControl("lblPrice") as Label);
lblPrice.Text = (Convert.ToInt32(tbq.Text) * 28).ToString();
}
any help will be highly apprecaited.
Your code is close. just remember that a data grid will persist in a round trip (post back), and MORE important that if you ONLY data bind the grid on the FIRST postback = false?
Then the grid will display but the row data bind event does not re-fire (and thus no total)
So, we assume that on page back we re-bind.
Now you have this code:
total= lbl.Text.ToString();
You need
total = total + lbl.Text.ToString()
Or, of course this:
total += lbl.Text.ToString()
Or more so, you want a final total, then
total += lbl.Text * tbq.Text
but, what you have should work.
Edit:
I now see the problem is that the total price is not updating right away. But we should at least GET the final total working upon page re-fresh.
If you want the total to update as you edit? Then most easy would be in your text changed event to re-calc the total. So say code like this:
So, you can have your existing code first, and THEN add this loop in the same text changed event:
protected void TextBoxE_TextChanged(object sender, EventArgs e)
{
// total the grid
sTotal = 0;
GridViewRow gvr;
foreach (var gvr in GridView1.Rows)
{
TextBox txtV = gvr.FindControl("lblPrice");
if (txtV.Text != "")
sTotal += lblprice.Text;
}
}
So in your text changed, you can (and need to) re-loop the rows after you updated the one row. So the above loop needs to be run. Keep in mind that the row data bound event fires for EACH row on a databind. If you on post back do a gridview databind, then you will of course loose the values in the grid. However, my bets is that you NOT doing a databind, and thus a simple loop to re-total the amount should work just fine.
The MAJOR issue here is that you left out the WHOPPER of a issue. What is that? Why of course you left out where you total things up in the first place. Since that part of yours was wrong (or at least looks to be), then my post FIRST addresed that issue and you need the +=. If your total on intitail load works, then you shoud have posted that code and how it works - since what you posted will not work.
So now that we cleared up why my post addressed the lack of total on first render.
Once you get that working (and you should have posted how that works), then and ONLY then can we address the text changed event. As above shows, it is a easy matter to re-loop and total up all rows since your changes will persist and be in that data - so right after your text changed - run/add/do the above loop to re-total.
I'm displaying a datatable that selects a number of elements from the database (IncomingsId, Type, Cost, Frequency) and I'm not sure how to delete a row when a button is clicked.
I've tried many solutions so far but nothing is working.
Here is the button I have within my Grid view
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:button id="DeleteRowButton" text="Delete Record" onclick="DeleteRowButton_Click" runat="server"/>
</ItemTemplate>
</asp:TemplateField>
And here is the code behind this page were I am creating the datatable.
SqlConnection con;
public _Default()
{
con = new SqlConnection(#"MySQLConnection");
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DisplayRecord();
}
}
public DataTable DisplayRecord()
{
string userId = (HttpContext.Current.User.Identity.GetUserId());
SqlDataAdapter Adp = new SqlDataAdapter("select * from Incomings where AspNetUsersId = '" + userId +"'", con);
DataTable Dt = new DataTable();
Dt.AcceptChanges();
Adp.Fill(Dt);
grid1.DataSource = Dt;
grid1.DataBind();
return Dt;
}
public void DeleteRowButton_Click(object sender, EventArgs e)
{
}
Cheers in advance for any help. I'm sure it's a simple resolution
You need a way for your code to know which ID to delete. Here is how I would normally do it:
Replace your button with an ItemTemplate, and add the button in here instead:
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Button Text="Delete" runat="server" CommandArgument='<%# Eval("userId") %>' CommandName="Delete" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
When you have the button this way, you now have access to a CommandArgument and CommandName attributes. Notice the argument I am passing is Eval("userId"), the command name (as you will see later) is used to recognize what action you want to execute (this could be anything, is just a name).
Replace the CommandArgument with whatever value(s) you want to pass to the server, using the name that came from the database/datasource (I am guessing it would be userId).
Then, to capture this you need to implement the RowCommand event of the GridView, and intercept the correct CommandName:
public void GridView1_RowCommand(Object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
string userId = e.CommandArgument.ToString();
//do something with the id
}
}
Here you have to make sure the CommandName in your button, matches to whatever action you want to execute in the RowCommand. This should do the trick.
Don't forget to bind the event to your GridView:
<asp:GridView OnRowCommand="GridView1_RowCommand" ID="GridView1" runat="server">
...
</asp:GridView>
just use this for the front-end code
<asp:GridView ID="grid1" OnRowDeleting="OnRowDeleting" DataKeyNames="deleteR" runat="server" AutoGenerateColumns="False" CellPadding="4" GridLines="None" style="width:100%" >
<columns>
<asp:TemplateField HeaderText="Delete Row">
<ItemTemplate>
<asp:Button ID="btnDelete" runat="server" class="btn btn-primary" Text="Delete" CommandName="Delete" OnRowDataBound="OnRowDataBound" />
</ItemTemplate>
</asp:TemplateField>
And have this in behind it:
protected void OnRowDeleting(object sender, GridViewDeleteEventArgs e)
{
int deleteR= Convert.ToInt32(grid1.DataKeys[e.RowIndex].Values[0]);
//have a sql statement here that add's the parameter ID for the row you want to delete, something like
SqlCommand com = new SqlCommand ("Delete FROM yourdatabase Where yourID = #yourID"
com.Parameters.AddWithValue("#yourID", yourID)
}
What you're looking for is indeed a simple solution.
You can use a foreach loop to search all DataRows within the DataTable for some kind of ID.Here's an example of what I mean:
String s = "/* IncomingsId for what you want to delete */";
foreach (DataRow r in dt.Rows) {
if (r["IncomingsId"].ToString().Equals(s)) {
dt.Rows.Remove(r);
}
}
Just a quick update for anyone that's helped me, thanks for your advice.
Asp.Net has it's own built in AutoGenerateDeleteButton and I set that to true and ran a bit of code behind it.
A simple solution that really shouldn't have taken me all day to complete!
I want to fetch the DataKey of the Selected Row of GridView on button click.
I would Personal do it in a Template Field Like so:
<asp:TemplateField>
<ItemTemplate>
//EDIT: after a comment it is suggested that you pass the RowIndex as the command argument which would provide access to the entire row
<asp:LinkButton ID="btnCopy" runat="server"CausesValidation="False"CommandName="MyCommandButton"CommandArgument='<%# Eval("MyDataKeyOrWhateverIWanteverIWantFromTheBindingSource")%>'>
</ItemTemplate>
</asp:TemplateField>
CodeBehind
protect void MyCommandButton(Object sender,CommandArgument e)
{
int DataKeyOrPK=int32.Parse(e.CommandArgument.ToString());
}
You other option would be:
<asp:gridview id="myGrid" runat="server"
width=100% datakeynames="Myid"
autogeneratecolumns=false
onSelectedIndexChanged="MyEvent">
<asp:templatefield headertext="Choose your dream home">
<itemtemplate>
<asp:linkbutton runat="server" commandname="select" text='<%# Eval ( "Whatever" ) %>' />
</itemtemplate>
</asp:templatefield>
Note the commandname="select" above.
Data-bound controls recognize certain command names and automatically raise and handle the appropriate events for the control. The following command names are recognized:
Cancel, Delete, Edit, Insert, New, Page, Select, Sort and Update. Reference
Codebehind
private void MyEvent(Object sender, EventArgs e)
{
string id = myGrid.SelectedDataKey.Value.ToString();
}
Use the SelectedDataKey property of the GridView:
DataKey currentKey = myGridView.SelectedDataKey;