ASP.NET 3.5 & SQL 2008
I have this main table & a subtable. For each set of calculation, 1 record is added to the main table & several records to the subtotal.
Most of user entered data goes into the subtable & some into the main table.
Currently I have a web page set up with several textboxes to get the data from the user & the resulting calculations are displayed in a gridview.
The gridview shows all the records from the Main table.
Each time the user selects a record from the gridview, I want to invoke another page where the user can change the data for the selected record.
It will be nice if I can display the 1 record from the main tablle & all the related records form the subtable.I don't want to use free floating textboxes.
What controls will be the easiest to use for such an application?
you can use grid view in two pages it self...
Here you have to use session variables to store the value of gridview rows in first page then retrieve the session value and display the row in gridview control in the second page.
If you use gridview control to display the data which get from database in the first page ,as far as I know you can also just pass the unique value of record(such as id) to an other page ,then use the unique value as a condition to search the record in database and display the record by using gridview control in the second page.
Sample:
1.Code in the first page(.aspx):
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server"
onrowdatabound="GridView1_RowDataBound">
<Columns>
<asp:TemplateField>
<HeaderTemplate>PassValue</HeaderTemplate>
<ItemTemplate>
Pass
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
2.Code in the first page(.cs):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Bind();
}
}
public void Bind()
{
this.GridView1.DataSource = Get_source();
this.GridView1.DataBind();
}
// You can get the datatbale form your own database.
// I get the datasource like this in order to run the sample convenient.
public DataTable Get_source()
{
DataTable dt = new DataTable();
DataRow dr;
string XM = "true,false,false,true";
string XMM = "1,2,3,4";
string[] list1 = XM.Split(',');
string[] list2 = XMM.Split(',');
dt.Columns.Add(new DataColumn("ActiveStatus"));
dt.Columns.Add(new DataColumn("number"));
for (int i = 0; i < list1.Length; i++)
{
dr = dt.NewRow();
dr["ActiveStatus"] = list1[i];
dr["number"] = list2[i];
dt.Rows.Add(dr);
}
return dt;
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
int i = e.Row.RowIndex;
Session["row"+i] = e.Row;
}
3.Code in the second page(which named as OtherPage.aspx)
OtherPage.aspx:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
4.Code in the second page(.cs):
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["id"] != null)
{
string i = Request.QueryString["id"].ToString();
if (Session["row"+i] != null)
{
DataTable dt = new DataTable();
DataRow dr = dt.NewRow();
GridViewRow row = (GridViewRow)Session["row" + i];
for (int xm = 0; xm < row.Cells.Count; xm++)
{
dt.Columns.Add(new DataColumn());
dr[xm] = row.Cells[xm].Text;
}
dt.Rows.Add(dr);
this.GridView1.DataSource = dt;
this.GridView1.DataBind();
}
}
}
if you want to modify the data in second page, you can modify ... I hope it will helps you...
Related
I am creating a page with Dynamic controls(Setting ID of controls too) in a table based on a loop in Page Load event.
I need to access the values of those dynamic controls to save in database in button click event. Now, when I loop on the table, it is giving me 0 rows of the table. Secondly, I also try to use the Find_Control Property to find the controls on the page and it is giving me NOTHING for each Control.
Is there something, I am missing, like runat=server? Or there is another approach to access those control on button click event.
ASP.net Code is below
<%# Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Table ID="Table1" runat="server">
</asp:Table>
</div>
<asp:Button ID="Button1" runat="server" Text="Button" />
</form>
</body>
</html>
VB.net is Below (just looping 10 times, Odd rows with text boxes, even rows with Drop down list just as an example)
Option Strict On
Option Explicit On
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If IsPostBack = False Then
Dim li As Integer = 0, lj As Integer = 0
For li = 0 To 9
Dim loR As New TableRow
'-------------------------------
Dim loC1 As New TableCell
loC1.Text = "Caption " & CStr(li + 1).PadLeft(2, CChar("0")) & " :"
loR.Cells.Add(loC1)
'-------------------------------
Dim loC2 As New TableCell
Select Case (li + 1) Mod 2
Case 0
Dim loDD As New DropDownList
loDD.ID = "ctl" & CStr(li + 1).PadLeft(2, CChar("0"))
For lj = 0 To li + 3
loDD.Items.Add("List " & CStr(li + 1).PadLeft(2, CChar("0")) & " : Value " & CStr(lj + 1).PadLeft(2, CChar("0")))
Next
loC2.Controls.Add(loDD)
Case 1
Dim loT As New TextBox
loT.ID = "ctl" & CStr(li + 1).PadLeft(2, CChar("0"))
loC2.Controls.Add(loT)
End Select
loR.Cells.Add(loC2)
'-------------------------------
Table1.Rows.Add(loR)
Next
End If
End Sub
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim li As Integer = 0
For li = 0 To Table1.Rows.Count - 1
Response.Write("")
Next
End Sub
End Class
Hi for storing and retrieving values from the dynamically created control we need to follow 3 steps,
1) create dynamic controls as per our need
2) override viewstate's two methods that is LoadViewState and SaveViewState as per below my code snippet
protected void CreateDynamicTextBox()
{
Label lableVendorName = new Label();
lableVendorName.Text = "Vendor Name";
TextBox VendorName = new TextBox();
VendorName.ID = "VendorName";
PanelVendor.Controls.Add(lableVendorName);
PanelVendor.Controls.Add(VendorName);
}
protected override void LoadViewState(object savedState)
{
//if we can identify the custom view state as defined in the override for SaveViewState
if (savedState is object[] && ((object[])savedState).Length == 2 && ((object[])savedState)[0] is string[])
{
object[] newViewState = (object[])savedState;
string[] txtValues = (string[])(newViewState[0]);
if (txtValues.Length > 0)
{
CreateDynamicTextBox();
(PanelVendor.FindControl("VendorName") as TextBox).Text = txtValues[0];
}
//load the ViewState normally
base.LoadViewState(newViewState[1]);
}
else
{
base.LoadViewState(savedState);
}
}
protected override object SaveViewState()
{
object[] newViewState = new object[2];
List<string> txtValues = new List<string>();
if (PanelVendor.Controls.Count > 1)
{
txtValues.Add((PanelVendor.FindControl("VendorName") as TextBox).Text);
}
newViewState[0] = txtValues.ToArray();
newViewState[1] = base.SaveViewState();
return newViewState;
}
and now retrieve the value from that control as below
protected void btnItem_Click(object sender, EventArgs e)
{
string VendorName = (PanelVendor.FindControl("VendorName") as TextBox).Text;
}
hope this would help. happy coding :)
I use Asp.net web forms. I have a form with a table and a combo box. The table changes as the selected item of the combo box changes. The combo box is an asp.net control and I set autopostback = true. The table is also an asp.net control, and all table cells are created/rendered in the server side.
Users will input values in the table and submit it to the server.
The problem I find is that when a user changes the selected item of the combo box, the table changes and the web page rendered correctly. Then the user inputs some values and clicks submit. From the server side, the value I get is the default values of the table, not the user inputs. If the user submits again, the server side can get the user inputs.
Here is the code I write to reproduce this issue. I create a default web form project, add a new web from which inherits the site master. To reproduce, take following steps: 1. select one radio button 2. submit and you will see a text about your selection at the top of the page. 3. change the combo box selection 4. select another radio button 5. submit and you will find the bug. 6. redo 4 and 5, you will find the text correct.
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="PostBackUsingMasterPage.aspx.cs" Inherits="WebFormBug.PostBackUsingMasterPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<asp:DropDownList ID="comboBox" runat="server" AutoPostBack="true" OnSelectedIndexChanged="UpdateTable">
<asp:ListItem>Apple</asp:ListItem>
<asp:ListItem>Beet</asp:ListItem>
<asp:ListItem>Citron</asp:ListItem>
</asp:DropDownList>
<asp:Label ID="userInput" runat="server"></asp:Label>
<asp:Table runat="server" ID="testTable"> </asp:table>
<asp:Button ID="submit" runat="server" Text="Submit for validation" OnClick="SubmitButton_Click" />
</asp:Content>
The aspx.cs is like this
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace WebFormBug
{
public partial class PostBackUsingMasterPage : Page
{
private string _scope;
protected void Page_Load(object sender, EventArgs e)
{
_scope = comboBox.SelectedValue ?? "Apple";
PopUpTable(_scope);
}
private void PopUpTable(string item)
{
testTable.Rows.Clear();
var row = new TableRow();
row.Cells.Add(new TableCell {Text = item});
row.Cells.Add(AddRadioButtons(item));
testTable.Rows.Add(row);
}
private TableCell AddRadioButtons(string name)
{
var cell = new TableCell();
var radioButtons = new HtmlInputRadioButton[5];
for (var i = 0; i < 3; i++)
{
radioButtons[i] = new HtmlInputRadioButton { Name = name, Value = name + " " + i };
cell.Controls.Add(radioButtons[i]);
var label = new Label { Text = name + " " + i };
cell.Controls.Add(label);
}
return cell;
}
protected void UpdateTable(object sender, EventArgs e)
{
PopUpTable(comboBox.SelectedValue);
}
protected void SubmitButton_Click(object sender, EventArgs e)
{
int valueIndex = 1;
for (int i = 0; i < testTable.Rows.Count; i++)
{
var row = testTable.Rows[i];
string inputValue = null, inputName = null;
foreach (var ctrl in row.Cells[valueIndex].Controls)
{
if (ctrl is HtmlInputRadioButton)
{
var radioInput = (HtmlInputRadioButton) ctrl;
if (!radioInput.Checked) continue;
inputValue = radioInput.Value;
inputName = radioInput.Name;
break;
}
}
if (inputName != null && inputValue != null)
{
userInput.Text = inputName + " " + inputValue;
}
}
}
}
}
Preparation knowledge of ASP.NET WebForm: dynamically added table data is missing from form post asp.net table adding rows dynamically don't remain after postback. Your sample is the case - you are composing table data dynamically.
Solution for posting back dynamic table data is to re-create the form data in each post back in (and should ONLY in) page Load method. and your sample is doing this (the PopUpTable method is always called in Page_Load).
However, in your code Page_Load is not the only place doing table re-creation, but also in OnSelectedIndexChanged which results in cleaning your table data. Actually, you don't need register this event.
So, solution (sorry for attaching the code as image, but I found attaching large segment of code is very hard to format):
Remove OnSelectedIndexChanged
Change code behind as below:
Try this:
in page load:
if(!IsPostBack)
{
// set data to table
}
I'm assuming you have updatepanel with which the change of the selected index on the dropdown updates the radiobuttons. You could try changing the eventhandler for the dropdown to
protected void UpdateTable(object sender, EventArgs e)
{
if (!IsPostBack)
{
PopUpTable(comboBox.SelectedValue);
}
}
I have this in my page:
<asp:DropDownList ID="membros" runat="server">
</asp:DropDownList>
and in the code behind, I have this:
protected void Page_Load(object sender, EventArgs e)
{
members.Items.Clear();
members.Items.Add(new ListItem("Choose...", ""));
foreach (DataRow item in com.Execute("select * from tableMembers").Rows)
{
members.Items.Add(new ListItem(item["name"].ToString(), item["id_user"].ToString()));
contForeach++;
}
}
and when I try to get the value from SelectedIndex I can't because he always get me 0 independently of what index I choose.
put that in if(!IsPostBack) so that it can load the dropdown at first time the form loads,right now it is loading everytime the page loads so your missing your selection.
if(!IsPostBack)
{
members.Items.Clear();
members.Items.Add(new ListItem("Choose...", ""));
foreach (DataRow item in com.Execute("select * from tableMembers").Rows)
{
members.Items.Add(new ListItem(item["name"].ToString(), item["id_user"].ToString()));
contForeach++;
}
}
Apart from the IsPostBack mentioned, you should be doing it this way instead of looping through the data
Modify your markup
<asp:DropDownList ID="membros" runat="server" DataTextField="name" DataValueField="user_id">
From code behind, bind the data
if(!IsPostBack)
{
DataTable dt = com.Execute("select * from tableMembers");
members.DataSource = dt;
members.DataBind();
members.Items.Insert(0, new ListItem("Choose...", ""));
}
Bound controls like DropdownList, GridView, DataList etc have DataSource property that you can use to assign a collection to them and they would loop through it and extract the data.
I have a page with a table of stuff and I need to allow the user to select rows to process. I've figured out how to add a column of check boxes to the table but I can't seem to figure out how to test if they are checked when the form is submitted. If they were static elements, I'd be able to just check do this.theCheckBox but they are programaticly generated.
Also I'm not very happy with how I'm attaching my data to them (by stuffing it in there ID property).
I'm not sure if it's relevant but I'm looking at a bit of a catch-22 as I need to known which of the checkboxes that were created last time around were checked before I can re-run the code that created them.
Edit:
I've found an almost solution. By setting the AutoPostBack property and the CheckedChanged event:
checkbox.AutoPostBack = false;
checkbox.CheckedChanged += new EventHandler(checkbox_CheckedChanged);
I can get code to be called on a post back for any check box that has changed. However this has two problems:
The call back is processed after (or during, I'm not sure) Page_Load where I need to use this information
The call back is not called for check boxes that were checked when the page loaded and still are.
Edit 2:
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.
I'm going to assume you're using a DataList but this should work with and Control that can be templated. I'm also going to assume you're using DataBinding.
Code Front:
<asp:DataList ID="List" OnItemDataBound="List_ItemDataBound" runat="server">
<ItemTemplate>
<asp:CheckBox ID="DeleteMe" runat="server"/>
<a href="<%# DataBinder.Eval(Container, "DataItem.Url")%>" target="_blank">
<%# DataBinder.Eval(Container, "DataItem.Title")%></a>
</ItemTemplate>
</asp:DataList>
<asp:Button ID="DeleteListItem" runat="server" OnClick="DeleteListItem_Click" ></asp:Button>
Code Behind:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadList();
}
protected void DeleteListItem_Click(object sender, EventArgs e)
{
foreach (DataListItem li in List.Items)
{
CheckBox delMe = (CheckBox)li.FindControl("DeleteMe");
if (delMe != null && delMe.Checked)
//Do Something
}
}
LoadList();
}
protected void LoadList()
{
DataTable dt = //Something...
List.DataSource = dt;
List.DataBind();
}
protected void List_ItemDataBound(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
{
string id = DataBinder.Eval(e.Item.DataItem, "ID").ToString();
CheckBox delMe = (CheckBox)e.Item.FindControl("DeleteMe");
if (delMe != null)
delMe.Attributes.Add("value", id);
}
}
}
First, make sure that each Checkbox has an ID and that it's got the 'runat="server"' in the tag.
then use the FindControl() function to find it.
For example, if you're looping through all rows in a GridView..
foreach(GridViewRow r in Gridview1.Rows)
{
object cb = r.FindControl("MyCheckBoxId");
if(r != null)
{
CheckBox chk = (CheckBox)cb;
bool IsChecked = chk.Checked;
}
}
Postback data is restored between the InitComplete event and the PreLoad event. If your checkboxes are not created until later then the checkboxes will play "catch up" with their events and the data will be loaded into the control shortly after it is created.
If this is to late for you then you will have to do something like what you are already doing. That is you will have to access the post data before it is given to the control.
If you can save the UniqueId of each CheckBox that you create then can directly access the post data without having to given them a special prefix. You could do this by creating a list of strings which you save the ids in as you generate them and then saving them in the view state. Of course that requires the view state to be enabled and takes up more space in the viewstate.
foreach (string uniqueId in UniqueIds)
{
bool data = Convert.ToBoolean(Request.Form[uniqueId]);
//...
}
Your post is a little vague. It would help to see how you're adding controls to the table. Is it an ASP:Table or a regular HTML table (presumably with a runat="server" attribute since you've successfully added items to it)?
If you intend to let the user make a bunch of selections, then hit a "Submit" button, whereupon you'll process each row based on which row is checked, then you should not be handling the CheckChanged event. Otherwise, as you've noticed, you'll be causing a postback each time and it won't process any of the other checkboxes. So when you create the CheckBox do not set the eventhandler so it doesn't cause a postback.
In your submit button's eventhandler you would loop through each table row, cell, then determine whether the cell's children control contained a checkbox.
I would suggest not using a table. From what you're describing perhaps a GridView or DataList is a better option.
EDIT: here's a simple example to demonstrate. You should be able to get this working in a new project to test out.
Markup
<form id="form1" runat="server">
<div>
<table id="tbl" runat="server"></table>
<asp:Button ID="btnSubmit" runat="server" Text="Submit"
onclick="btnSubmit_Click" />
</div>
</form>
Code-behind
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var row = new HtmlTableRow();
var cell = new HtmlTableCell();
cell.InnerText = "Row: " + i.ToString();
row.Cells.Add(cell);
cell = new HtmlTableCell();
CheckBox chk = new CheckBox() { ID = "chk" + i.ToString() };
cell.Controls.Add(chk);
row.Cells.Add(cell);
tbl.Rows.Add(row);
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
foreach (HtmlTableRow row in tbl.Rows)
{
foreach (HtmlTableCell cell in row.Cells)
{
foreach (Control c in cell.Controls)
{
if (c is CheckBox)
{
// do your processing here
CheckBox chk = c as CheckBox;
if (chk.Checked)
{
Response.Write(chk.ID + " was checked <br />");
}
}
}
}
}
}
What about using the CheckBoxList control? I have no Visual Studio open now, but as far as I remember it is a DataBound control, providing DataSource and DataBind() where you can provide a list at runtime. When the page does a postback you can traverse the list by calling something like myCheckBoxList.Items and check whether the current item is selected by calling ListItem.Selected method. This should work.
Add them in an override of the CreateChildControls method of the Page. Be sure to give them an ID! This way they get added to the control tree at the correct time.
IMHO The best way would be to use DataBound Templated Control though, i.e. something like a ListView (in .NET 3.5). then in pageload after postback traverse all items in the databound control and use item.FindControl to get at the actual checkbox.
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.
I have the following really simple code
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server">
</asp:PlaceHolder>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</ContentTemplate>
</asp:UpdatePanel>
And the codebehind
protected void Button1_Click(object sender, EventArgs e)
{
Literal literal = new Literal();
literal.Text = DateTime.Now.ToString();
literal.ID = DateTime.Now.Ticks.ToString();
// These both work fine the first time the button is clicked
// but the second time nothing is added.
UpdatePanel1.ContentTemplateContainer.Controls.Add(literal);
PlaceHolder1.Controls.Add(literal);
}
My problem comes in that the Literal control is only ever added once. I've scoured google and blog sites (plus books) but without any luck. What am I missing?
In asp.net, the controls in the ASPX file are automatically generated on each postback. The controls you've created are not in the ASPX code so the framework does not create them for you. The first time you execute the Button1_Click method, you add one extra control to the page. The second time you execute the Button1_Click method, you're on another post back and that first extra button has been forgotten about. So the result of that postback is you get one extra button again.
This will create one extra control each time you click the button (although the timestamps will update each time you press the button because the controls are being re-created)
protected void Button1_Click(object sender, EventArgs e)
{
int count = 0;
if (ViewState["ButtonCount"] != null)
{
count = (int)ViewState["ButtonCount"];
}
count++;
ViewState["ButtonCount"] = count;
for (int i = 0; i < count; i++)
{
Literal literal = new Literal();
literal.Text = DateTime.Now.ToString();
literal.ID = DateTime.Now.Ticks.ToString();
UpdatePanel1.ContentTemplateContainer.Controls.Add(literal);
PlaceHolder1.Controls.Add(literal);
}
}
I agree to the answer above, However this approach will not save the state of the dynamic controls (or to be accurate, it will save the state but not load them back).
Load view state is called in Load event section of page life cycle,where it assigns back the control values saved in view state.
However if the controls are not created by this time, They can not be loaded with previous data so for the state to be maintained, the new controls must be recreated on or before load event.
protected void Page_Load(object sender, EventArgs e)
{
//PS: Below approach saves state as id is constant, it simply generates a new control with same id hence viewstate loads the value
if (IsPostBack)
{
int count = 0;
if (ViewState["ButtonCount"] != null)
{
count = (int)ViewState["ButtonCount"];
}
count++;
ViewState["ButtonCount"] = count;
for (int i = 0; i < count; i++)
{
TextBox literal = new TextBox();
//literal.Text = DateTime.Now.ToString();
literal.ID = "Textbox" + i.ToString();
//UpdatePanel1.ContentTemplateContainer.Controls.Add(literal);
PlaceHolder1.Controls.Add(literal);
}
}
}
Dynamically adding controls
View State and postback