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
}
Related
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 am creating a table of components, and need the ability to add items from a dropdown list to each item in the table. These lists are added programmatically using a foreach like this:
MyDatabase db = new MyDatabase();
if (db.ComponentTypes.Count() > 0)
{
foreach (ComponentType componentType in db.ComponentTypes)
{
// Header row components
TableRow componentRow = new TableRow();
TableCell componentTypeCell = new TableCell();
// Create Header Row
componentTypeCell.ColumnSpan = 5;
componentTypeCell.Text = componentType.Name;
componentTypeCell.Attributes.Add("style", "background: black; color: white; font-weight: bold;");
componentRow.Cells.Add(componentTypeCell);
tblRigActionTypesAndComponentTypes.Rows.Add(componentRow);
// Middle portion omitted for simplicity
//=================================================
// Relevant portion
// DDL Row Components
TableRow addActionRow = new TableRow();
TableCell rigActionTypeMenuCell = new TableCell();
TableCell addRigActionTypeButtonCell = new TableCell();
DropDownList ddlRigActionTypeMenu = new DropDownList();
Button addRigActionTypeButton = new Button();
// Populate dropdown with action types
Helper.PopulateDropdownWithActionTypes(ddlRigActionTypeMenu);
rigActionTypeMenuCell.Controls.Add(ddlRigActionTypeMenu);
addRigActionTypeButton.Text = "Add This Action";
addRigActionTypeButton.CommandName = "Add";
addRigActionTypeButton.CommandArgument = componentType.ID.ToString();
addRigActionTypeButtonCell.ColumnSpan = 4;
addRigActionTypeButtonCell.Controls.Add(addRigActionTypeButton);
addActionRow.Cells.Add(rigActionTypeMenuCell);
addActionRow.Cells.Add(addRigActionTypeButtonCell);
tblRigActionTypesAndComponentTypes.Rows.Add(addActionRow);
}
}
Button Handler
protected void ButtonHandler(object sender, EventArgs e)
{
Button button = (Button)sender;
MyDatabase db = new MyDatabase();
if (button.CommandName == "Add")
{
// How do I capture the selected value from the
// dropdown menu paired with the "add" button?
}
}
Capturing the component the button belongs to is easy using the CommandArgument property, but how can I get the corresponding DDL?
Update: Moe S' Method
I have been unable to get this to work. I have tried a few different ways to access the dropdown menu using button.NamingContainer, but keep hitting a Object reference not set to an instance of an object. error. My last attempt is below:
Control control = button.NamingContainer;
Control test = control.FindControl("ddlRigActionTypeMenu");
lblPageHeader.Text = test.UniqueID;
Update 2:
To shed some additional light on the above (non-working) code, the following DOES work:
Control control = button.NamingContainer;
lblPageHeader.Text = button.NamingContainer.UniqueID;
This changes the page header to dnn$ctr498$AssignRigActionTypesToComponentTypes
Solved
I am marking Moe as the accepted answer on this because he got me pointed in the right direction, but Parent was what ended up working for me, not NamingContainer. All of the same principles still apply though.
Solution:
DropDownList ddl = (DropDownList)((TableRow)((TableCell)button.Parent).Parent).Cells[0].Controls[0];
You should be able to use the following to access the table row:
TableRow tblRow = (TableRow) button.NamingContainer;
And then use the FindControl option to access the DropDownList
DropDownList ddlMenu = (DropDownList) tblRow.FindControl("ddlRigActionTypeMenu");
And then obviously SelectedValue to capture the value
I want to duplicate a table row on button click, specifically, a row with controls in (drop down list etc). The user will be inputting data and then sending it to a database when all rows are complete. The purpose is to reduce user time as many fields will be the same on each row possibly.
I have this code attempting to duplicate row controls, it doesnt copy but rather 'move' the control. Also, why doesn't multiple button clicks result in multiple rows..after one click it wont add any more than one additional. Thanks. EDIT: yes, is asp:table.
Current code:
protected void Button1_Click(object sender, EventArgs e)
{
TableRow tRow = new TableRow();
Table1.Rows.Add(tRow);
foreach (TableCell cell in Table1.Rows[0].Cells)
{
TableCell tCell = new TableCell();
foreach (Control ctrl in cell.Controls)
{
tCell.Controls.Add(ctrl);
}
//tCell.Text = cell.Text;
tRow.Cells.Add(tCell);
}
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;
}