I'm trying to make a page that loads an undefined number of rows into a table, each row containing two columns, one column a string, the other a TextBox. When I click a submit button, i want to be able to retrieve the values entered into each TextBox.
The first half i can do, obviously in the real code this would be in a foreach loop and the textbox ID assigned a unique value i could reproduce later to call it with.
TableCell myCell = new TableCell();
myCell.Text = "StudentID";
TableCell nextCell = new TableCell();
TextBox mytext = new TextBox();
mytext.ID = "TxtBox1";
nextCell.Controls.Add(mytext);
TableRow myRow = new TableRow();
myRow.Cells.Add(myCell);
myRow.Cells.Add(nextCell);
TableStuUploads.Rows.Add(myRow);
When i click my submit button, i try to run this code(just temp code ATM, proof of concept stuff):
TextBox tmptext = (TextBox)FindControl("TxtBox1");
Label1.Text = tmptext.Text;
But that sets tmptext as null, and i get a null pointer exception on the next line. So then i tried
TextBox tmptext = (TextBox)TableStuUploads.FindControl("TxtBox1");
Label1.Text = tmptext.Text;
Same error. Then i tried
foreach (Control x in TableStuUploads.Controls)
{
if (x.GetType().ToString().Equals("System.Web.UI.WebControls.TextBox"))
{
Label1.Text = ((TextBox)x).Text;
}
}
But when debugging this, I see TableStuUploads.Controls has a count of zero.
How am I supposed to address these dynamically created controls? I have searched around, and the answer i got lead me to the three solutions i have already tried. Where am i going wrong?
You are probably not recreating your dynamic controls on post back. This is required to both find each control and retrieve each control's value.
In addition, it would probably be best to use a repeater to create these controls, It is easier to maintain and debug.
Edit
You need to recreate your table on each postback(page_load) like #ShaiCohen says in his answer then you need something ilke:
foreach (TableRow item in TableStuUploads.Rows)
{
TextBox tmptext = (TextBox)item.FindControl("TxtBox1");
Label1.Text = tmptext.Text;
}
Related
I am new to asp.net and c# and have to deal with the event handling for a dynamically created table.
The Background:
I have a method in my code-behind CSS file and there, I create dynamically a table to show users in the first column and for every user, I have a checkbox in the second column. My table is displayed well, so the creation is not the problem.
My problem is, that I need event-handling. The goal is, to "mark" users by clicking their appropriate checkbox and then to do, whatever I should do with such users.
I have added the code for event handling with "+=" and I have also tried to use an event handler object. But the problem is: my event-handling code is never reached (i checked this by running and debugging the code).
I'm totally new to asp.net and c# and HTML, javascript and so on. So i don't know much about page-life-cycle and things like that. And it will last time, to get in, cause im a student and I'm just working 2 days a week. If anybody can explain me, how I can do event handling in c# and asp.net for dynamically created elements, that would help me much. Thanks for every help :)
foreach (ListItem li in list_deletedUsers.Items)
{
String s = li.Text;
//Creating the basic Elements
TableRow myrow = new TableRow();
TableCell cellone = new TableCell();
TableCell celltwo = new TableCell();
CheckBox cb = new CheckBox();
//cb.CheckedChanged += cb_CheckedChanged;
//cb.CheckedChanged += this.cb_CheckedChanged;
//cb.CheckedChanged += new
System.EventHandler(cb_CheckedChanged);
//Setting the values and adding cells to row
cellone.Text = s;
myrow.Cells.Add(cellone);
celltwo.Controls.Add(cb);
celltwo.ApplyStyle(tableStyle);
myrow.Cells.Add(celltwo);
//Third Column Cell for further use
//TableCell cellthree = new TableCell();
//cellthree.BackColor = System.Drawing.Color.AliceBlue;
//Adding to Row
//myrow.Cells.Add(cellthree);
//Adding to TableBert
TableBert.Rows.Add(myrow);
}
Here in this methode i create the table:
public void rbe_changed(object sender, EventArgs e)
{
//do some stuff
//look for the user list
//create the table like i show above
}
Good afternoon.
I am attempting to create a DropDownList populated in 2 ways:
The options are from an Enumeration, listing each possible option.
The selected value is from a database table.
The DropDownList's SelectedIndexChanged takes care of updating the database for me.
Note that the control is working already, but I think I'm not understanding the lifecycle correctly and would like some clarification.
On PageInit, I generate a Table with Headers and one of the cells having my DropDownList.
//Essentially an ArrayList with "row" information (it uses a Field class with
//several parameters, each corresponding to 1 column in the table
GenericFieldsCollection data = new GenericFieldsCollection();
data.FillFieldList();
Table TableToEdit = new Table();
TableHeaderCell Header_FieldName = new TableHeaderCell();
(...)
foreach (GenericField row in data.FieldList)
{
(...)
//The cell with the DDL:
TableCell Column_GraphName = new TableCell();
Column_GraphName.Controls.Add(GenerateGraphSelectionDropdown());
(...)
}
Obviously, if I do not generate it here, the Click events won't work.
This procedure only queries the database with a Select, no updates are made.
After the first (!PostBack) call, subsequent calls can be triggered by changing the DropDownList's index (it has AutoPostBack=true).
So, my PageInit runs again, as expected. And, after Load Number 15 in here happens (
OnXXX (control event)), and my database gets updated.
Later in the Cycle (OnPreRender), I tried clearing the Div anchor I'm using for this table of all controls, and call my GenerateFieldCollectionTable() again. Since my update query in my DropDownList's SelectedIndexChanged event has already executed, I expected to see the updated values. However, doing this will show the old value, and wreck the behaviour (I won't be able to change anything and there is no change in the database).
After a bit of testing, I found out that Page_PreRender having this line is the issue:
------> //TableToEditGraphFields.Controls.Clear();
GenerateFieldCollectionTable();
Clearing the controls of TableToEditGraphFields, an action that happens AFTER CLICK EVENTS (since
Control.OnPreRender is #22 and OnSelectedIndexChanged is #15), means that on the next PostBack the Controls won't trigger click events, as they've been cleared. Is there a part of the cycle where I can process the Click event, rebuild the controls, and still have them clickable on the next PostBack?
Thank you in advance.
EDIT: Code that generates the table:
GenericFieldsCollection data = new GenericFieldsCollection();
data.FillFieldList();
Table TableToEdit = new Table();
TableToEdit.CssClass = "table";
TableRow Headers = new TableRow();
TableHeaderCell Header_FieldName = new TableHeaderCell();
Header_FieldName.Text = "Field Name";
Headers.Cells.Add(Header_FieldName);
//Repeat for each header
TableToEdit.Rows.Add(Headers);
foreach (GenericField row in data.FieldList)
{
TableRow currentRow = new TableRow();
//Repeat for each column; example with the "Order" column
TableCell Column_Order = new TableCell();
TextBox editableOrder = new TextBox();
editableOrder.Attributes.Add("FieldName", row.Name);
editableOrder.Attributes.Add("FieldTable", row.Table);
editableOrder.AutoPostBack = true;
editableOrder.TextChanged += EditableOrder_TextChanged;
editableOrder.Text = row.Order.ToString();
Column_Order.Controls.Add(editableOrder);
currentRow.Cells.Add(Column_Order);
TableToEdit.Rows.Add(currentRow);
}
TableToEditGraphFields.Controls.Add(TableToEdit);
" I'm not understanding the lifecycle correctly and would like some clarification."
How does the life cycle work?
As you can see in the picture below. This is how the life cycle is working.
All the information is from codedisplay There is a lot more information for you to read.
I have a asp.net page that has two controls on it, A placeholder and a submit button. During the Page_Load I create a checklist of tasks dynamically. Each row consists of a description, link to a tutorial, and a checkbox. All of the information in the row is kept in a database. If the database says the task has been checked, the code sets the checked property to true. The problem I'm having is that when the submit button is clicked I cannot find what the value is of the checked property for all of the checkboxes on the page(about 23 total).
Here is the code to create the checkboxes...
checkbox = new CheckBox();
phChecklist.Controls.Add(checkbox);
if (item.Attributes.Contains("ree_completed"))
checkbox.Checked = (bool)item.Attributes["ree_completed"];
checkbox.EnableViewState = true;
checkbox.ClientIDMode = System.Web.UI.ClientIDMode.Static;
checkboxId = "checkbox" + (string)item.Attributes["ree_sectionnumber"].ToString() + (string)item.Attributes["ree_sequencenumber"].ToString();
checkbox.ID = checkboxId;
Here is the code to try and find the value of the checkbox...
foreach (Entity item in checklistCollection.Entities)
{
checkboxId = "checkbox" + (string)item.Attributes["ree_sectionnumber"].ToString() + (string)item.Attributes["ree_sequencenumber"].ToString();
itemChecked = (bool)ViewState[checkboxId];
if (itemChecked == "true")
** update database **
//CheckBox checkbox = (CheckBox)phchecklist.FindControl(checkboxId);
}
I think I've read every post on this subject and have tried most of them. I have also read about the ViewState but the suggestions that I have read about have not worked. As you can see I also tried finding the checkbox in the controls collection by the id and that also did not work.
I do not recreate the checkboxes when posting back to the page. Some posts mention that you have to recreate the controls but when I tried to do that I received an error message saying it was a duplicate id. The other reason I would prefer not to have to recreate the page is the performance hit. The database is in a Microsoft Dynamic CRM database that is remote.
How do I retain the value of checked property across a post back?
UPDATE: I changed my logic around and fixed the duplicate id error. The page will now recreate all of the controls during the post back. I still cannot find the value of any of the checkbox controls when the submit button is clicked.
Gary
You need to provide an ID for the checkbox control when you create it. Since you are creating multiple checkboxes; one for each row in the database ... you need to add the unique row identifier to the ID. So you need to build the checkbox ID from the row ID (usually the IDENTITY). Example: ">
Then on postback while you are post-processing each row in the table, you can query the request for that specific key value pair. Something similar to this:
foreach (DataRow dr in dataTable.Rows)
Response["chk_" + dr("ID")];
Use Page Init as opossed to page_Load event. As per https://msdn.microsoft.com/en-us/library/ms178472.aspx "Use this event to read or initialize control properties"
In case for that to work you need to add an event handler to dynamically added controls
in your case
checkbox = new CheckBox();
phChecklist.Controls.Add(checkbox);
checkbox.CheckedChanged += checkBox_CheckedChanged;
and then what you need to do in the method
private void CheckBox_CheckedChanged(object sender, System.EventArgs e)
{
...
}
As I commented, you certainly need to create your controls during the Init event, Load is too late. Check out the asp.Net page life's cycle for details.
On a side note, you might want to let ASP.NET handle the controls's ID. You say you need to keep some value in order to get back to the database, you can wrap your checkboxes in a table. You will then be able to iterate through your controls without having to guess their names.
For instance, if your row has a HiddenField followed by your actual CheckBox:
//Please call me during Init
protected void createTheControls() {
TableRow tr;
TableCell td;
CheckBox cb;
HiddenField section, sequence;
foreach (Object item in someList)
{
section = new HiddenField();
section.Value = item.Attributes["ree_sectionnumber"].ToString();
sequence = new HiddenField;
sequence.Value = item.Attributes["ree_sequencenumber"].ToString();
cb = new CheckBox();
cb.ID = String.Concat("checkbox", (String)sequence.Value);
if (item.Attributes.Contains("ree_completed"))
cb.Checked = (bool)item.Attributes["ree_completed"];
td = new TableCell();
td.Controls.Add(section);
td.Controls.Add(sequence);
td.Controls.Add(cb);
tr = new TableRow();
tr.Cells.Add(td);
}
}
protected void readTheControls()
{
foreach (TableRow tr in myTable.Rows)
{
HiddenField section = (HiddenField)tr.Cells[0].Controls[0];
HiddenField sequence = (HiddenField)tr.Cells[0].Controls[1];
CheckBox cb = (CheckBox)tr.Cells[0].Controls[2];
}
}
I notice you try to use the ViewState explicitly, which I believe is not going to be of any help here. The solution above may look cumbersome, but it will ensure you dont need to mess with ID's when fetching the controls.
Here's a thorough explanation from MSDN, and here are a few short and sweet SO answers explaining ViewState's purpose.
I have a datagridview that I populate from a loop.
If I do the following:
public Form(Dictionary<String, String> headers)
{
InitializeComponent();
foreach (var key in headers.Keys) {
datagridview1.Columns.Add(key, key);
}
}
I get a header row containing text.
How do I add buttons and text to the header row of datagridview1 instead of just text?
I am using winforms and .net 4.5. (I guess I could switch to 4.5.1 if it makes things easier).
As far as I know this is not possible. Here are ways you could get close:
Set the first row's Frozen property and put the buttons in the first row.
Add a right-click menu to expose the functions you want to add.
May thats helping you
DataGridViewButtonColumn btn = new DataGridViewButtonColumn();
dataGridView1.Columns.Add(btn);
btn.HeaderText = "YourText";
btn.Text = "YourText";
btn.Name = "DGVbutton1";
btn.UseColumnTextForButtonValue = true;
I have a DetailsView control on my page used to edit various fields of a record, which works well in this respect.
I am looking for a way to add one column (and if that works, why not more) to the right, which will be absolutely read-only, to show the same fields of another record for comparison purposes.
I am aware there is no obvious way to do such thing out of the box with DetailsView. I have looked into other controls (transposed GridView, someone recommended FormView, ListView), but nothing satisfies. I have some very special data binding setup using the DetailsView and I can't get out of it without losing some features.
Anyone on how to "hack in" additional columns (for display only) on a DetailsView ?
The solution I have now, is to use a second DetailsView, with Visible set to False in my aspx.
In the code, I make sure to DataBind the hidden DetailsView that hosts the data for my third column first, then the initial DetailsView named ItemDetails.
And in the item created event, I pass to a third column the html rendering of my hidden controls (in the last code block) :
protected void ItemDetails_ItemCreated(object sender, EventArgs e)
{
if (dataItem2 != null) //compare enabled
{
var headerRow = ((DetailsView)sender).HeaderRow;
var headerL = new Label();
headerL.Text = header2;
headerL.Style.Add("font-weight", "bold");
var headerCell = new TableCell();
headerCell.Controls.Add(headerL);
headerCell.Style.Add("text-align", "right");
headerRow.Cells.Add(headerCell);
if (string.IsNullOrEmpty(header1) && string.IsNullOrEmpty(header2)) ((DetailsView)sender).HeaderRow.Visible = false;
}
else
{
((DetailsView)sender).HeaderRow.Visible = false;
}
foreach (DetailsViewRow r in ItemDetails.Rows)
{
if (r.RowType == DataControlRowType.DataRow)
{
// Assume the first cell is a header cell
var dataCell = (DataControlFieldCell)r.Cells[0];
string dataFieldName = null;
if (dataCell.ContainingField is CustomBoundField) dataFieldName = ((CustomBoundField)dataCell.ContainingField).GetDataFieldName();
else if (dataCell.ContainingField is BoundField) dataFieldName = ((BoundField)dataCell.ContainingField).DataField;
if (dataItem2 != null) //compare enabled
{
if (!string.IsNullOrEmpty(dataFieldName)) //it's a field, copy boundField from hidden DetailsView
{
var ct = new TableCell();
var text = new StringWriter();
var html = new HtmlTextWriter(text);
dict[dataFieldName].RenderControl(html);
ct.Text = text.ToString().Replace("<td>", String.Empty).Replace("</td>", String.Empty);
r.Cells.Add(ct);
}
}
}
}
}