Pass an ID on button click - c#

I have this code below that create rows and cells for a list of products. There is a button that I want to use to get additional product information. Basically the user will search for something and it will display limited results. When the user clicks on the button it will call a method that will do something else.
How do I get that button to pass and ID or something to a method?
I tried the .Click but did not work to call the method. Currently the method only displays a messagebox.
for (rowCtr = 1; rowCtr <= rowCnt; rowCtr++)
{
// Create new row and add it to the table.
TableRow tRow = new TableRow();
Table1.Rows.Add(tRow);
for (cellCtr = 1; cellCtr <= cellCnt; cellCtr++)
{
// Create a new cell and add it to the row.
TableCell tCell = new TableCell();
if (rowCtr == 1 && cellCtr == 1)
{
Image ProductImage = new Image();
ProductImage.Height = 75;
ProductImage.Width = 75;
tCell.RowSpan = 5;
tCell.Controls.Add(ProductImage);
tRow.Cells.Add(tCell);
}
if (rowCtr == 1 && cellCtr == 2)
{
tCell.Text = "Title: Title of Product";
tRow.Cells.Add(tCell);
}
if (rowCtr == 2 && cellCtr == 2)
{
tCell.Text = "Weight (lbs): 54";
tRow.Cells.Add(tCell);
}
if (rowCtr == 4 && cellCtr == 2)
{
Button getOfferButton = new Button();
getOfferButton.Width = 100;
getOfferButton.Text = "Get Offer";
getOfferButton.Click += new EventHandler(getOffer);
tCell.Controls.Add(getOfferButton);
tRow.Cells.Add(tCell);
}
}
}

I think you should be using <asp:GridView /> control instead of generating this with markup. One way to handle this is use the OnCommand event and pass the ID as argument
getOfferButton.Click += new CommandEventHandler(RowButton_OnCommand);
getOfferButton.CommandArgument = "123";
and then the Handler
protected void RowButton_OnCommand(object sender, CommandEventArgs e)
{
string id = e.CommandArgument.ToString(); //could convert this to integer
}

First you need to return object list from your code behind , second you need to import ur BLL or BO what ever is name:
'<asp:Repeater ID="rptGrpAcc" runat="server" OnItemDataBound="rptAccident_ItemDataBound">
<ItemTemplate>
<tr style="cursor: pointer" onclick="SelectGrpAcc(this,<%#Eval("ID"%>);">
</ItemTemplate>
</asp:Repeter>'
<asp:HiddenField ID="hdnRowNum" runat="server" />
<asp:Button ID="btnShowRptDtl" runat="server" Text="ShowRptDtl" Style="display: none"
OnClick="btnShowRptDtl_Click" CausesValidation="false"/>
' <script type="text/javascript">
/*---------------for selecting row of repeator--------*/
function SelectGrpAcc(obj, ID) {
document.getElementById("<%=hdnRowNum.ClientID %>").value = ID;
document.getElementById("<%=btnShowRptDtl.ClientID %>").click();
}'
'protected void btnShowRptDtl_Click(object sender, EventArgs e)
{
btnAdd.Text= "update";
int Sn = ClsConvertTo.Int32(hdnRowNum.Value); // this is your ID in code behind
// add logic here
}'

Related

how to get the value of dynamically populated controls

I have a div Conatining a Panel in aspx page
<div id="divNameofParticipants" runat="server">
<asp:Panel ID="panelNameofParticipants" runat="server">
</asp:Panel>
</div>
I am populating the panel dynamically from codebehind with the following code:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
panelNameofParticipants.Controls.Add(tableparticipantName);
}
}
catch(Exception ex)
{
}
}
Now I want to access the value of these dynamically generated textbox in the codebehind.for which i my code is as under:
public void CreateControls()
{
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
}
}
catch (Exception ex)
{
}
}
but I am not able to get the values in codebehind.
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
the above code is not able to find the control and its always giving null.what am i doing wrong.i recreated the controls in page load also since postback is stateless but still no success.
Thanks in Advance
You need to create dynamic controls in PreInit not in OnLoad. See documentation here: https://msdn.microsoft.com/en-us/library/ms178472.aspx
Re/Creating the controls on page load will cause the ViewState not to be bound to the controls because viewstate binding happens before OnLoad.
As mentioned by other people. To create dynamic controls your need to do this for every postback and at the right time. To render dynamic controls, use the Preinit event. I suggest that your have a look at https://msdn.microsoft.com/en-us/library/ms178472(v=vs.80).aspx to learn more about this.
MSDN - PreInit:
Use this event for the following:
Check the IsPostBack property to determine whether this is the first time the page is being processed.
Create or re-create dynamic controls.
Set a master page dynamically.
Set the Theme property dynamically.
Read or set profile property values.
NoteNote If the request is a postback, the values of the controls have not yet been restored from view state. If you set a control property at this stage, its value might be overwritten in the next event.
The next interesting event is Preload that states:
MSDN - PreLoad
Use this event if you need to perform processing on your page or control before the Load event.
After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance.
Meaning that in the next event Load (Page_Load) the viewstate should be loaded, so here you should effectively be able to check your values.
You also need to make sure that view state is enabled and the easiest is probably in the page level directive:
<%#Page EnableViewState="True" %>
Take a look at the article https://msdn.microsoft.com/en-us/library/ms972976.aspx2 that goes more into the depth of all this
Note
If your problem is that you need to create controls dynamically on a button click, and there will be many controls created, you should probably turn to jQuery ajax and use the attribute [WebMethod] on a public function in your code behind. Creating dynamic controls and maintaining the ViewState is quite costly, so i really recommend this, for a better user experience.
If you use a DataPresentation control like asp:GridView it will be much easier.
Markup
<asp:GridView ID="ParticipantsGrid" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateField HeaderText="Participants">
<ItemTemplate>
<asp:TextBox ID="txtNameofParticipant" runat="server"
Text='<%# Container.DataItem %>'>
</asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code-behind
protected void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
try
{
var selectedParticipantCount = Convert.ToInt32(drpNoofparticipants.SelectedValue);
var items = Enumerable.Repeat(string.Empty, selectedParticipantCount).ToList();
ParticipantsGrid.DataSource = items;
ParticipantsGrid.DataBind();
}
catch (Exception ex)
{ }
}
public void CreateControls()
{
try
{
var participants = ParticipantsGrid.Rows
.Cast<GridViewRow>()
.Select(row => ((TextBox)row.FindControl("txtNameofParticipant")).Text)
.ToList();
}
catch (Exception ex)
{ }
}
I think you are doing that correctly. In asp.net webforms, there are lots of times that you will experienced strange things that are happening. Especially with the presence of ViewState. First we need to troubleshoot our code and try different tests and approaches. That will be my advise for you, pleas use this code and debug if what is really happening:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
TableRow row = new TableRow();
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "NameTest";
txtNameofParticipant.Text = "This is a textbox";
cell.ID = "cellTest";
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
tableparticipantName.Rows.Add(row);
panelNameofParticipants.Controls.Add(tableparticipantName);
}
catch(Exception ex)
{ // please put a breakpoint here, so that we'll know if something occurs,
}
}
public void CreateControls()
{
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
//if (ViewState["numberofparticipants"] != null)
//{
string findcontrol = "NameTest";
TextBox txtParticipantName = (TextBox)panelNameofParticipants.Controls["NameText"];
//check if get what we want.
listParticipantName.Add(txtParticipantName.Text);
//}
}
catch (Exception ex)
{// please put a breakpoint here, so that we'll know if something occurs,
}
}
Dynamic controls need to be recreated on every post-back. The easiest way to achieve that in your code would be to cache the control structure and repopulate it. This how it can be done:
void btnSubmitCountParticipant_Click(object sender, EventArgs e)
{
StringBuilder sbparticipantName=new StringBuilder();
Panel p1 = new Panel();
try
{
int numberofparticipants = Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
p1.Controls.Add(tableparticipantName);
}
Cache["TempPanel"] = p1;
panelNameofParticipants.Controls.Add(p1);
}
catch(Exception ex)
{
}
}
Look for the p1 panel in the above code to see the changes. Now the code for the CreateControls function need to be changed as following:
public void CreateControls()
{
try
{
Panel p1= (Panel)Cache["TempPanel"];
panelNameofParticipants.Controls.Add(p1);
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)panelNameofParticipants.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
}
}
catch (Exception ex)
{
}
}
Hope this helps.
Dynamically created controls would disappear when page posts back, This article here explains why it happens.
So in order to make dynamic controls be recognized by asp.net page, you need to recreate it in preinit event handler, but I think it is difficult as this dynamic controls in your case rely on some other web form element such as a dropdownlist drpNoofparticipants and also ViewState is not available at this stage.
My suggestion is that we do it in a different way, each post back is actually a form post, so instead of finding the dynamic text box, you could directly get the value via Request.Form collection. Here is the code snippet:
var list = Request.Form.AllKeys.Where(x => x.Contains("txtNameofParticipant"));
foreach (var item in list)
{
var value = Request.Form[item];
}
In this way, you could get the value and since you don't need to rely on ASP.NET engine to retrieve the value from dynamically created control, you could postpone the recreating the table in page_load event handler.
Hope it helps.
Put your btnSubmitCountParticipant_Click method data into other function with name of your choice. Call that function in method btnSubmitCountParticipant_Click and in method CreateControls(). Also cut paste the below code in btnSubmitCountParticipant_Click method
int numberofparticipants =
Convert.ToInt32(drpNoofparticipants.SelectedValue);
ViewState["numberofparticipants"] = numberofparticipants;
. This is working in my machine. Hope this helps
Yes,Dynamic controls are lost in postback,So we need save dynamic control values in Viewsate and again generate dynamic control.I added code here, this working fine
//Create Button Dynamic Control
protected void btnDyCreateControl_Click(object sender, EventArgs e)
{
try
{
int numberofparticipants = 5;
ViewState["numberofparticipants"] = numberofparticipants;
int test = (int)ViewState["numberofparticipants"];
int rowcount = 1;
int columnCount = numberofparticipants;
CreateDynamicTable(rowcount, columnCount);
}
catch (Exception ex)
{
}
}
//submit values
protected void btnSave_Click(object sender, EventArgs e)
{
try
{
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
int i = 0;
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox)
{
string findcontrol = "txtNameofParticipant" + i;
TextBox txtParticipantName = (TextBox)cell.Controls[0].FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
i++;
}
}
}
}
}
}
catch (Exception ex)
{
}
}
//Save ViewState
protected override object SaveViewState()
{
object[] newViewState = new object[3];
List<string> txtValues = new List<string>();
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox)
{
txtValues.Add(((TextBox)cell.Controls[0]).Text);
}
}
}
}
}
newViewState[0] = txtValues.ToArray();
newViewState[1] = base.SaveViewState();
if (ViewState["numberofparticipants"] != null)
newViewState[2] = (int)ViewState["numberofparticipants"];
else
newViewState[2] = 0;
return newViewState;
}
//Load ViewState
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 == 3 && ((object[])savedState)[0] is string[])
{
object[] newViewState = (object[])savedState;
string[] txtValues = (string[])(newViewState[0]);
if (txtValues.Length > 0)
{
//re-load tables
CreateDynamicTable(1, Convert.ToInt32(newViewState[2]));
int i = 0;
foreach (Control c in panelNameofParticipants.Controls)
{
if (c is Table)
{
foreach (TableRow row in c.Controls)
{
foreach (TableCell cell in row.Controls)
{
if (cell.Controls[0] is TextBox && i < txtValues.Length)
{
((TextBox)cell.Controls[0]).Text = txtValues[i++].ToString();
}
}
}
}
}
}
//load the ViewState normally
base.LoadViewState(newViewState[1]);
}
else
{
base.LoadViewState(savedState);
}
}
//Create Dynamic Control
public void CreateDynamicTable(int rowcount, int columnCount)
{
Table tableparticipantName = new Table();
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
cell.ID = "cell" + Convert.ToString(j);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
tableparticipantName.EnableViewState = true;
ViewState["tableparticipantName"] = true;
}
panelNameofParticipants.Controls.Add(tableparticipantName);
}
Reference Link,MSDN Link, Hope it helps.
When creating your textboxes make sure you set the ClientIDMode
I recently worked on something similar. I dynamically created checkboxes in GridView rows and it worked for me.
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
txtNameOfParticipant.ClientIDMode = System.Web.UI.ClientIDMode.Static;
Dynamically added controls have to be created again on postback, otherwise the state of the dynamically added controls will get lost (if the controls lost how can one preserve their old state) . Now the question. When is the viewstated loaded? It is loaded in between the two events Page_Init and Load.
Following code samples are a bit modification of your code for your understanding
The aspx markup is same as yours, but with some extra controls
<form id="form1" runat="server">
<asp:DropDownList ID="drpNoofparticipants" runat="server" >
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
<asp:ListItem>6</asp:ListItem>
<asp:ListItem>7</asp:ListItem>
<asp:ListItem>8</asp:ListItem>
<asp:ListItem>9</asp:ListItem>
<asp:ListItem>10</asp:ListItem>
</asp:DropDownList>
<br /><asp:Button ID="btnCreateTextBoxes" runat="server" OnClick="btnSubmitCountParticipant_Click" Text="Create TextBoxes" />
<div id="divNameofParticipants" runat="server">
<asp:Panel ID="panelNameofParticipants" runat="server">
</asp:Panel>
</div>
<div>
<asp:Button ID="btnSubmitParticipants" runat="server" Text="Submit the Participants" OnClick="BtnSubmitParticipantsClicked" />
</div>
</form>
On click event of the btnCreateTextBoxes button i am creating the controls using the following code
private void CreateTheControlsAgain(int numberofparticipants)
{
try
{
ViewState["numberofparticipants"] = Convert.ToString(numberofparticipants);
Table tableparticipantName = new Table();
int rowcount = 1;
int columnCount = numberofparticipants;
for (int i = 0; i < rowcount; i++)
{
for (int j = 0; j < columnCount; j++)
{
TableRow row = new TableRow();
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(j);
cell.ID = "cell" + Convert.ToString(j);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
tableparticipantName.Rows.Add(row);
}
}
panelNameofParticipants.Controls.Add(tableparticipantName);
}
catch (Exception ex)
{
}
}
As mentioned above to maintain the control state, i included the re-creation of the controls in the page Load event based on the viewstate["numberofparticipants"]
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["numberofparticipants"] != null)
{
CreateTheControlsAgain(Convert.ToInt32(ViewState["numberofparticipants"]));
CreateControls();
}
}
On click event of btnSubmitParticipants button i wrote the following event and writing the participants names to the console
try
{
//String test1 = test.Value;
List<string> listParticipantName = new List<string>();
if (ViewState["numberofparticipants"] != null)
{
int numberofparticipants = Convert.ToInt32(ViewState["numberofparticipants"]);
for (int i = 0; i < numberofparticipants; i++)
{
string findcontrol = "txtNameofParticipant" + i;
var txtParticipantName = panelNameofParticipants.FindControl(string.Format("txtNameofParticipant{0}", i)) as TextBox;
listParticipantName.Add(txtParticipantName.Text);
}
}
foreach (var item in listParticipantName)
{
Response.Write(string.Format("{0}<br/>", item));
}
}
catch (Exception ex)
{
}
Hope this helps
I'm not sure if this is desired, but you're adding multiple tables to your panel, but I think you only want/need one table. So this line should be outside the outer for loop like so:
for (int i = 0; i < rowcount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < columnCount; j++)
{
TableCell cell = new TableCell();
TextBox txtNameofParticipant = new TextBox();
txtNameofParticipant.ID = "txtNameofParticipant" + Convert.ToString(i);
cell.ID = "cell" + Convert.ToString(i);
cell.Controls.Add(txtNameofParticipant);
row.Cells.Add(cell);
}
tableparticipantName.Rows.Add(row);
}
panelNameofParticipants.Controls.Add(tableparticipantName);
Additionally, FindControl will only search the direct children of the container, in this case it can only find the table, else it'll return null. so you need to search children of the panel to find the control, e.g. your table:
foreach (TableRow row in tableparticipantName.Rows)
{
TextBox txtParticipantName = (TextBox)row.FindControl(findcontrol);
listParticipantName.Add(txtParticipantName.Text);
}
If that doesn't work then doing it recursively might work better:
ASP.NET Is there a better way to find controls that are within other controls?

.Net Wizard user inputs being overwritten

I have been searching quite a bit, but unable to find something that addresses the issue I am seeing. I am sure I am missing something simple, but I have been fighting it too long, and really need to figure out what is going on. I have an existing (working) user control that I am rebuilding. It is a multi-step wizard, with each step being a type of "form" created from tables. I have successfully converted 3 of the 4 steps to divs to make them dynamic (using Bootstrap 3), but this one step, step 2, is not working like the rest. The user's input is being lost. The original code (table based) works properly. It is a simple table declared on the .ascx side:
<asp:WizardStep ID="childInformationStep" runat="server" Title="">
<%-- Some more stuff...--%>
<asp:Table cellpadding="2" class="annualSurveyTable" cellspacing="0" border="0" ID="tblChildInfo" runat="server">
</asp:Table>
<asp:WizardStep>
On the c# side, during Page_Load, a method is called to cycle through all the children of a family and dynamically build rows with pre-populated input cells for each child's First/Last Name, B-day, gender and grade. It looks like this:
private void AddChildEdit(Person child, int index)
{
TableRow row = new TableRow();
TableCell cell = new TableCell();
row.ID = "trChildFirstName_" + index;
cell.ID = "tcChildFirstName_" + index;
cell.VerticalAlign = VerticalAlign.Middle;
cell.HorizontalAlign = HorizontalAlign.Right;
cell.Wrap = false;
cell.CssClass = "registrationLabel";
cell.Text = "Child's First Name";
row.Cells.Add(cell);
cell = new TableCell();
TextBox tb = new TextBox();
tb.ID = "tbChildFirstName_" + index;
tb.Text = child.FirstName;
tb.Enabled = false;
cell.Controls.Add(tb);
row.Cells.Add(cell);
tblChildInfo.Rows.AddAt(tblChildInfo.Rows.Count, row);
// snip (more of same for last name)
row = new TableRow();
cell = new TableCell();
row.ID = "trChildBirthday_" + index;
cell.ID = "tcChildBirthday_" + index;
cell.VerticalAlign = VerticalAlign.Middle;
cell.HorizontalAlign = HorizontalAlign.Right;
cell.Wrap = false;
cell.CssClass = "registrationLabel Birthday";
cell.Text = "Child's Birth Date";
row.Cells.Add(cell);
cell = new TableCell();
DateTextBox dtb = new DateTextBox();
dtb.ID = "tbChildBirthday_" + index;
dtb.CssClass = "registrationItem Birthday";
if (child.BirthDate != DateTime.MinValue && child.BirthDate != DateTime.Parse("1/1/1900"))
dtb.Text = child.BirthDate.ToShortDateString();
cell.Controls.Add(dtb);
row.Cells.Add(cell);
tblChildInfo.Rows.AddAt(tblChildInfo.Rows.Count, row);
row = new TableRow();
cell = new TableCell();
row.ID = "trChildGender_" + index;
cell.ID = "tcChildGender_" + index;
cell.VerticalAlign = VerticalAlign.Middle;
cell.HorizontalAlign = HorizontalAlign.Right;
cell.Wrap = false;
cell.CssClass = "registrationLabel";
cell.Text = "Child's Gender";
row.Cells.Add(cell);
cell = new TableCell();
DropDownList ddlGender = new DropDownList();
ListItem l = new ListItem("", "", true);
l.Selected = true;
ddlGender.Items.Add(l);
l = new ListItem("Male", "0", true);
ddlGender.Items.Add(l);
l = new ListItem("Female", "1", true);
ddlGender.Items.Add(l);
ddlGender.ID = "ddlChildGender_" + index;
// snip (there is one more row added for grade
}
And the save method looks like it cycles through the table looking for the inputs related to the children it is looping through, and pulling in the text value, which should include any changes the user has made. It works as desired, and looks like this (BTW, I didn't write it, it looks like it could be cleaned up quite a bit :D)
private void SaveChildValues()
{
string userID = CurrentUser.Identity.Name + " - Annual Survey";
if (userID == " - Annual Survey")
userID = "Annual Survey";
int i = 0;
foreach (Person child in childrenList)
{
TableCell selectedCell = null;
foreach (TableRow row in tblChildInfo.Rows)
{
if (row.ID == "trChildBirthday_" + i)
{
foreach (TableCell cell in row.Cells)
{
if (cell.ID == "tcChildBirthday_" + i)
{
selectedCell = cell;
DateTextBox box = (DateTextBox)selectedCell.FindControl("tbChildBirthday_" + i);
if (box.Text.Trim() != string.Empty)
try { child.BirthDate = DateTime.Parse(box.Text); }
catch { }
i++;
break;
}
}
break;
}
}
}
i = 0;
foreach (Person child in childrenList)
{
TableCell selectedCell = null;
foreach (TableRow row in tblChildInfo.Rows)
{
if (row.ID == "trChildGender_" + i)
{
foreach (TableCell cell in row.Cells)
{
if (cell.ID == "tcChildGender_" + i)
{
selectedCell = cell;
DropDownList ddl = (DropDownList)selectedCell.FindControl("ddlChildGender_" + i);
if (ddl.SelectedValue != string.Empty)
try { child.Gender = (Gender)Enum.Parse(typeof(Gender), ddl.SelectedValue); }
catch { }
i++;
break;
}
}
break;
}
}
}
i = 0;
foreach (Person child in childrenList)
{
TableCell selectedCell = null;
foreach (TableRow row in tblChildInfo.Rows)
{
if (row.ID == "trChildGrade_" + i)
{
foreach (TableCell cell in row.Cells)
{
if (cell.ID == "tcChildGrade_" + i)
{
selectedCell = cell;
DropDownList ddl = (DropDownList)selectedCell.FindControl("ddlChildGrade_" + i);
if (ddl.SelectedValue != string.Empty)
try { child.GraduationDate = Person.CalculateGraduationYear(Int32.Parse(ddl.SelectedValue), CurrentOrganization.GradePromotionDate); }
catch { }
i++;
break;
}
}
break;
}
}
}
}
Now, here are the changes that I have made to that section. The page loads, and runs through all the motions, yet when the save happens, it is pulling in the original DB value from the child record again instead of the user's input. I simply changed the table to an ASP Panel in the .ascx file:
<asp:WizardStep ID="childInformationStep" runat="server" Title="">
<%-- Some more stuff...--%>
<asp:Panel ID="tblChildInfo" runat="server" ClientIDMode="Static">
</asp:Panel>
<asp:WizardStep>
I have changed the dynamic row creation to dynamic divs, laid out for bootstrap 3:
private void AddChildEdit(Person child, int index)
{
Panel childRow = new Panel();
childRow.ID = "ChildRow_" + index;
childRow.CssClass = "form-horizontal";
LiteralControl childTitle = new LiteralControl();
childTitle.Text = string.Format("<h4>Child {0}:</h4>", (index + 1).ToString());
childRow.Controls.Add(childTitle);
Panel formGroup = new Panel();
formGroup.ID = "trChildFirstName_" + index;
formGroup.CssClass = "form-group";
childRow.Controls.Add(formGroup);
Panel inputContainer = new Panel();
inputContainer.CssClass = "col-sm-8";
formGroup.Controls.Add(inputContainer);
TextBox tb = new TextBox();
tb.ID = "tbChildFirstName_" + index;
tb.Text = child.FirstName;
tb.Enabled = false;
inputContainer.Controls.Add(tb);
Label inputLabel = new Label();
inputLabel.ID = "tcChildFirstName_" + index;
inputLabel.CssClass = "col-sm-3 control-label registrationLabel";
inputLabel.Text = "First Name";
inputLabel.AssociatedControlID = tb.ID;
formGroup.Controls.AddAt(0, inputLabel);
tblChildInfo.Controls.Add(childRow);
// snip (more code for adding Last Name row
formGroup = new Panel();
formGroup.ID = "trChildBirthday_" + index;
formGroup.CssClass = "form-group";
inputContainer = new Panel();
inputContainer.ID = "tcChildBirthday_" + index;
inputContainer.CssClass = "col-sm-8";
formGroup.Controls.Add(inputContainer);
TextBox dtb = new TextBox();
dtb.ID = "tbChildBirthday_" + index;
dtb.CssClass = "form-control survey-control date-mask registrationItem";
dtb.Attributes.Add("placeholder", "MM/DD/YYYY");
if (child.BirthDate != DateTime.MinValue && child.BirthDate != DateTime.Parse("1/1/1900"))
dtb.Text = child.BirthDate.ToString("MM/dd/yyyy");
inputContainer.Controls.Add(dtb);
inputLabel = new Label();
inputLabel.CssClass = "col-sm-3 control-label";
inputLabel.Text = "BirthDate";
inputLabel.AssociatedControlID = dtb.ID;
formGroup.Controls.AddAt(0, inputLabel);
childRow.Controls.Add(formGroup);
// snip (more of the same, adding two more rows for gender and grade)
}
And I simplified the save method to:
private void SaveChildValues()
{
string userID = CurrentUser.Identity.Name + " - Annual Survey";
if (userID == " - Annual Survey")
userID = "Annual Survey";
int i = 0;
foreach (Person child in childrenList)
{
try
{
TextBox box = (TextBox)tblChildInfo.FindControl("tbChildBirthday_" + i);
if (box.Text.Trim() != string.Empty)
child.BirthDate = DateTime.Parse(box.Text);
}
catch { }
try
{
DropDownList ddl = (DropDownList)tblChildInfo.FindControl("ddlChildGender_" + i);
if (ddl.SelectedValue != string.Empty)
child.Gender = (Gender)Enum.Parse(typeof(Gender), ddl.SelectedValue);
}
catch {}
try
{
DropDownList ddl = (DropDownList)tblChildInfo.FindControl("ddlChildGrade_" + i);
if (ddl.SelectedValue != string.Empty)
child.GraduationDate = Person.CalculateGraduationYear(Int32.Parse(ddl.SelectedValue), CurrentOrganization.GradePromotionDate);
}
catch { }
i++;
}
As far as I understand it, my code does not change any fundamental behavior, other than it is using div elements to build out the dynamic content rather then adding rows to a table. What am I missing that is causing my updated code to lose the users' input?
NOTE: this is step two, where the information is rendered, captured for the child info. The save method is not executed until step 4, so the input data should be persisting through two more steps, and remain in tact. I have tried using debugger, but can never see the users input. I don't know if I am looking for it at the wrong breakpoints, but I can't seem to find where the user input is coming back with the post, and when it SHOULD be getting written to the inputs. Any help would be greatly appreciated.
You could try moving the dynamic creation of the fields into the Page_Init section rather than the Page_Load.

ASP.NET loop through datatable column and write a checkbox for each item

I'm brand new to ASP.NET with intermediate C# level, and previously I wrote a PHP project. I'm now stuck trying to get a similar effect in ASP.NET.
What I'm using:
The project is C# ASP.NET Empty web application. I'm using Visual Studio 2010 with SP1.
MSSQL 2008 R2
What I want to do is add HTML code using a foreach into the ASP file, specific content area.
This is what I would do in php:
foreach ($library as $book)
{
print "<a href=\"bookpage.php?id=".$book[book_id]"";
print "<h3>".$book[book_author]."</h3>";
print " <p>".$book[book_blurb]."</p>";
}
This is what I've tried in ASP:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" Runat="Server">
<asp:RadioButtonList ID="RadioButtonPizzaList" runat="server">
<asp:ListItem Text="Margerita" />
<asp:ListItem Text="Hawaain" />
<asp:ListItem Text="Meat Supreme" />
</asp:RadioButtonList>
</asp:Content>
But instead of hardcoding, I want to add a listitem for each pizza thats been retrieved from the database, the names of pizza are stored in an array. How would I use a loop and add an HTML line like what I did in above PHP example?
I assume that your pizza list has two columns that Id and PizzaName.
And you have a method that getting pizza list from db as named GetList in Pizza class.
Firstly you should add two attributes in aspx side to your radio button list control.
They are DataValueField and DataTextField.
These attributes required for data binding.
Aspx Side
<asp:RadioButtonList ID="RadioButtonPizzaList" DataValueField="Id" DataTextField="PizzaName" runat="server">
</asp:RadioButtonList>
Code behind Side
private void FillPizzaList()
{
DataTable dtList = Pizza.GetList();
this.RadioButtonPizzaList.DataSource = dtList;
this.RadioButtonPizzaList.DataBind();
}
If you want to get selected item value you can get with this code
this.RadioButtonPizzaList.SelectedValue
Note: If you fill radiobutton list in page load event, do not forget check is postback.
if ( !IsPostBack )
this.FillPizzaList();
In the end, the answer to my problem was using c# to create the table and add rows, and in the cells add the content. One of the questions around here while I was looking around was kind of answered, but not fully. So if you see this you can adapt it.
I've changed from using checkboxes to simply adding text to the cell area, but to answer my original question, one can do what I did with the textboxes and choose which cells to add the checkboxes, or simply do without the table and loop checkboxes into your wanted area.
At the end I add the table to a div that is ready made in the asp code
public partial class _Default : System.Web.UI.Page
{
Database doDatabase = new Database();//custom class for querying a database
ArrayList textboxNames = new ArrayList();//just to make life easier
ArrayList pizzaNames = new ArrayList();//just to make life easier
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
}
this.FillPizzaList();
}
private void FillPizzaList()
{
int count = 1;
string query = "select FoodID, FoodName, 'R' + Convert(char(10), FoodPrice)[Food Price], rtrim(FoodDesc)[FoodDesc] from tblFood";
doDatabase.Do_SQLQuery(query);
Table tablePizza = new Table();
TableRow tr = new TableRow();
TableCell tc = new TableCell();
for (int c = 0; c < 4; c++)
{
tc = new TableCell();
if (c == 0)
{
tc.Width = new Unit("15%");
tc.Text = "<h3>Pizza Name</h3>";
}
else if (c == 1)
{
tc.Text = "<h3>Pizza Description</h3>";
}
else if (c == 2)
{
tc.HorizontalAlign = HorizontalAlign.Center;
tc.Width = new Unit("15%");
tc.Text = "<h3>Pizza Price</h3>";
}
else if (c == 3)
{
tc.Width = new Unit("12%");
tc.Text = "<h3>Pizza Quantity</h3>";
}
tr.Cells.Add(tc);
}
tablePizza.Rows.Add(tr);
foreach (DataRow dr in doDatabase.dataTbl.Rows)
{
tr = new TableRow();
for (int c = 0; c < 4; c++)
{
tc = new TableCell();
if (c == 0)
{
pizzaNames.Add(dr["FoodName"].ToString());
tc.Text = dr["FoodName"].ToString();
}
else if (c == 1)
{
tc.Text = dr["FoodDesc"].ToString();
}
else if (c == 2)
{
tc.HorizontalAlign = HorizontalAlign.Center;
tc.Text = dr["Food Price"].ToString();
}
else if (c == 3)
{
TextBox MyTextBox = new TextBox();
MyTextBox.ID = "Quantity" + count;
textboxNames.Add("Quantity" + count);
MyTextBox.Text = "0";
tc.Controls.Add(MyTextBox);
count++;
}
tr.Cells.Add(tc);
}
tablePizza.Rows.Add(tr);
}
pizzaMenu.Controls.Add(tablePizza);//add table to div
}

find selected item in custom paging

I wrote custom paging to a repeater. Here below is code. When I clicked a LinkButton, How to set selected it or how to set css class?
Firstly I tried to set css class in lbl_Click function like this lnk.CssClass = "classname"; On every attempt, It sets css class all clicked linkbutton.
private void CreatePagingControl()
{
for (int i = 0; i < pages; i++)
{
LinkButton lnk = new LinkButton();
lnk.Click += new EventHandler(lbl_Click);
lnk.ID = "lnkPage" + (i + 1).ToString();
lnk.Text = (i + 1).ToString();
plcPaging.Controls.Add(lnk);
Label spacer = new Label();
spacer.Text = " | ";
plcPaging.Controls.Add(spacer);
}
}
void lbl_Click(object sender, EventArgs e)
{
LinkButton lnk = sender as LinkButton;
int currentPage = int.Parse(lnk.Text);
int take = currentPage * 15;
int skip = currentPage == 1 ? 0 : take - 15;
FetchData(take, skip);
}
aspx
<asp:PlaceHolder ID="plcPaging" runat="server" />
Try this
Change you aspx to this
<div id="pageingDiv">
<asp:PlaceHolder ID="plcPaging" runat="server" />
<asp:HiddenField id="currentId" runatserver" value="1" />
</div>
Call this Jquery script to $(document).ready()
$("#pageingDiv a").removeClass();
var hvId = $('[id$=currentId]').val();
$("#lnkPage" _ hvId).AddClass('ClassName');
Then in your codebehind add css
void lbl_Click(object sender, EventArgs e)
{
LinkButton lnk = sender as LinkButton;
int currentPage = int.Parse(lnk.Text);
int take = currentPage * 15;
int skip = currentPage == 1 ? 0 : take - 15;
FetchData(take, skip);
//add css here
currentId.Value=currentPage.ToString();
}
Maybe you can try something like this, which should also work when CssClass contains more than one class.
// removing a cssClass
protected String removeCssClass(string cssClass, string toRemove)
{
return String.Join(" ", cssClass.Split(' ').Except(new[] {toRemove}).ToArray());
}
void lbl_Click(object sender, EventArgs e)
{
// resetting all selected items
plcPaging.Controls
.OfType<Label>()
.Where(l => l.ID.StartsWith("lnkPage") && l.CssClass.Split(' ').Contains("selectedClassName")).ToList()
.ForEach(l => l.CssClass = removeCssClass(l.CssClass, "classname"));
LinkButton lnk = sender as LinkButton;
// setting selected item
lnk.CssClass = "classname";
int currentPage = int.Parse(lnk.Text);
int take = currentPage * 15;
int skip = currentPage == 1 ? 0 : take - 15;
FetchData(take, skip);
}
Hope this will help

ASP.NET dynamically-generated TableRows won't persist between postbacks

In the ASPX
<asp:Table ID="superTable" runat="server" Width="100%">
<%--populate me on the fly!--%>
</asp:Table>
<asp:Button ID="btnAddRow" runat="server" CausesValidation="false" Text="Add Row" onclick="btnAddRow_Click" Width="90%"/>
<asp:Button ID="btnRemoveRow" runat="server" CausesValidation="false" Text="Remove Last Row" onclick="btnRemoveRow_Click" Width="90%"/>
<asp:Button ID="btnSubmit" runat="server" Text="1" onclick="btnSubmit_Click" Width="90%"/>
Relevant bits of CodeBehind
protected void Page_Load(object sender, EventArgs e)
{if (!IsPostBack){ writeHeader(); makeMeARow(); }}
protected void btnAddRow_Click(object sender, EventArgs e)
{
if (int.Parse(btnSubmit.Text) <= 20)
{ int b = superTable.Rows.Count+1;
writeHeader();
btnSubmit.Text = (int.Parse(btnSubmit.Text) + 1).ToString();
for (int a = 1; a <= int.Parse(btnSubmit.Text); a++)
{ makeMeARow(); }
}
else{/*tell user they can't do that! Max of 20 rows as noted by form requirements */}
}
private void writeHeader()
{
//= == create row == =//
TableHeaderRow tempHeaderRow = new TableHeaderRow();//make row
//= == create cells == =//
TableHeaderCell tempHeaderCell01 = new TableHeaderCell();
TableHeaderCell tempHeaderCell02 = new TableHeaderCell();
TableHeaderCell tempHeaderCell03 = new TableHeaderCell();
tempHeaderCell01.Text = "Call Number"; tempHeaderCell01.Width = Unit.Percentage(33);
tempHeaderCell02.Text = "Author"; tempHeaderCell02.Width = Unit.Percentage(33);
tempHeaderCell03.Text = "Title"; tempHeaderCell03.Width = Unit.Percentage(33);
//= == add TableCells to TableRow == =//
tempHeaderRow.Cells.Add(tempHeaderCell01);
tempHeaderRow.Cells.Add(tempHeaderCell02);
tempHeaderRow.Cells.Add(tempHeaderCell03);
//superTable.Rows.AddAt(superTable.Rows.Count, tempRow);
superTable.Rows.Add(tempHeaderRow);
}
protected void btnRemoveRow_Click(object sender, EventArgs e)
{ int b = superTable.Rows.Count - 1;
writeHeader();
btnSubmit.Text = (int.Parse(btnSubmit.Text) - 1).ToString();
for (int a = 1; a <= int.Parse(btnSubmit.Text); a++)
{makeMeARow();}
}
private void makeMeARow()
{
//= == maybe off by one? == =//
string rowCount = superTable.Rows.Count.ToString("00");
//= == create row == =//
TableRow tempRow = new TableRow();//make row
//= == create cells == =//
TableCell tempCell01 = new TableCell();
TableCell tempCell02 = new TableCell();
TableCell tempCell03 = new TableCell();
//= == create TextBoxes == =//
TextBox tempTextBox01 = new TextBox();
TextBox tempTextBox02 = new TextBox();
TextBox tempTextBox03 = new TextBox();
//= == change the ID of TableRow == =//
tempRow.ID = "tableRow_" + rowCount;
//= == change the IDs of TableCells == =//
tempCell01.ID = "tableCell_" + rowCount + "_01";
tempCell02.ID = "tableCell_" + rowCount + "_02";
tempCell03.ID = "tableCell_" + rowCount + "_03";
//= == change the IDs of TextBoxes == =//
tempTextBox01.ID = "txtCallNumber_" + rowCount;
tempTextBox02.ID = "txtAuthor_" + rowCount;
tempTextBox03.ID = "txtTitle_" + rowCount;
//= == change TextBox widths to 90%;
tempTextBox01.Width = Unit.Percentage(90);
tempTextBox02.Width = Unit.Percentage(90);
tempTextBox03.Width = Unit.Percentage(90);
//= == add TextBoxes to TableCells == =//
tempCell01.Controls.Add(tempTextBox01);
tempCell02.Controls.Add(tempTextBox02);
tempCell03.Controls.Add(tempTextBox03);
//= == add TableCells to TableRow == =//
tempRow.Cells.Add(tempCell01);
tempRow.Cells.Add(tempCell02);
tempRow.Cells.Add(tempCell03);
//add TableRow to superTable
//superTable.Rows.AddAt(superTable.Rows.Count, tempRow);
superTable.Rows.Add(tempRow);
}
Okay, so, my problem;
-when I hit either the "Add Row" or "Remove Row" button, the data in the cells don't persist between postbacks. The relevant rows and cells hold the same IDs, but don't persist data. Why not?
Dynamic controls must be re-added to the form on each postback. Typically this is done during the Init phase of the page's lifecycle. The controls that you have added dynamically DO actually have ViewState. When the appropriate control is re-added to the control tree using the exact same ID it had before, it should reappear with the values that were persisted in ViewState.
Check out this article for simple tips on using dynamic controls or you can check out this tutorial from 4 Guys from Rolla for a more in-depth look.

Categories