I am trying to make a dynamic table which has a couple of rows and the last row is a plus button, which will add a new row. (I will only describe the important information, to keep it simple.)
I thought of this way to accomplish it:
// .aspx code
<li><asp:LinkButton ID="LM_Show" runat="server" Text="Show list" OnClick="Action"/></li>
<asp:PlaceHolder ID="infoTable" runat="server"></asp:PlaceHolder>
//CreateTable function
public void Clicked(object sender, EventArgs e){
table();
}
public void table() {
//Do stuff...
//Screen variable is keeping track of which screen should be shown in .aspx
infoTable.Controls.Add(CreateView.createTable<Employee>(screen, this.Context, table));
}
//Create the actual table
public static Table createTable<T>(Screen screen, HttpContext context, Action method) where T : new() {
//New table and make it stretch
Table tb = new Table();
tb.Width = Unit.Percentage(100);
tb.Height = Unit.Percentage(100);
//Gather list from session
List<T> items = (List<T>)context.Session["list"];
//Create table content based on the list
for (int i = 1; i <= items.Count(); i++) {
TableRow tr = new TableRow();
//Foreach property create cells with content according to the property
//Add these cells to the row
tb.Rows.Add(tr);
}
//Create 1 final row which has a button to be able to add 1 row
TableRow tr2 = new TableRow();
TableCell tc = new TableCell();
tr.Cells.Add(tc);
//Create the Button
Button button = new Button();
button.Text = "+";
//!!This is not getting triggered!!//
button.Click += (s, e) => {
List<T> tempItems = (List<T>)context.Session["list"];
tempItems.Add(new T());
context.Session["list"] = tempItems;
//When a button is pressed, it gives a postback.
//The table has to be rebuild over again with the newly added item
method();
};
//!!This is not getting triggered!!//
//Add the button
tr2.Cells[0].Controls.Add(button);
tb.Rows.Add(tr2);
return tb;
}
Any comments about the code or how to accomplish it even better are also very welcome.
This could be achieved more easily if you used jQuery AJAX and communicate with a web service/method to get the data. However, it can be achieved in C# as well. Assuming that the given below part of the code is not working,
//Add the button
tr2.Cells[0].Controls.Add(button);
tb.Rows.Add(tr2);
You can try adding button to tc first, and then add tc to the row tr
tc.Controls.Add(button);
tr2.Cells.Add(tc);
tb.Rows.Add(tr2);
Good Luck!
Related
Hello i got this problem.
I have table that got rows filled with dynamically generated controls (TextBox) in first cell , button (MyCustomButton) in second and validation control in last cell (RegularExpressionValidator). The validation controller checks if data in the TextBox are correct. Function of the button is to remove row containing this button, textbox and validator.
My problem is when i click that button to remove row that it belongs to exception will pop up saying that "Unable to find control id 'MyTextBoxId' referenced by the 'ControlToValidate'".
Problem here is that validator cant find TextBox to validate because it was removed and this exception pop up. I tried to first remove this validator and after that rest of the row, clear incorrect data in TextBox, turn off validation of that TextBox but im still getting this exception even after the validator was removed.
Removing from table method
protected void DeleteMemberRow_Click(object sender, EventArgs e)
{
//Find row to remove
TableRow row = (TableRow)((MyCustomButton)sender).Parent.Parent;
//Custom list of controls - works fine
ControlsList.RemoveAll(x => x.id == row.ID.Replace("row", ""));
//MyTable is basic Table type
MyTable.Rows.Remove(row);
}
Adding table rows
Guid guid = Guid.NewGuid();
TextBox txt = new TextBox();
MyCustomButton btn = new MyCustomButton();
btn.Click += new System.EventHandler(DeleteMemberRow_Click);
btn.ID = "TeamMember" + guid + "btn";
txt.ID = "TeamMember" + guid;
RegularExpressionValidator validate = new RegularExpressionValidator();
validate.ValidationExpression = #"(\d{5}, ?)*\d{5}";
validate.ErrorMessage = "My error message";
validate.Attributes.Add("runat", "server");
validate.ControlToValidate = "TeamMember" + guid;
validate.Attributes.Add("Display", "none");
TableRow tRow = new TableRow();
tRow.ID = "Teammember" + guid + "row";
TableCell tCell2 = new TableCell();
TableCell tCell = new TableCell();
TableCell tCell1 = new TableCell();
tCell2.Controls.Add(validate);
tCell1.Controls.Add(btn);
tCell.Controls.Add(txt);
tRow.Cells.Add(tCell);
tRow.Cells.Add(tCell1);
tRow.Cells.Add(tCell2);
MyTable.Rows.Add(tRow);
Any help would be appreciated. Thank you
Ok its fixed there wasnt problem with code but with me. I didnt noticed that i got another validator checking same TextBox so after i deleted it the other one failed to find TextBox to validate.
I have 3 TextBoxes for user input.
I want the user to enter name, email and age and then clicking on a button to put it into a table.
I have created it using design mode, but I have issues with the table, how should I create the table?
how can I make it create a new row and take the information from the textboxes to the right cells?
This is the code I`m trying to do:
public partial class WebForm1 : System.Web.UI.Page
{
Table myTable;
TableCell[] td;
TableRow tr;
protected void Page_Load(object sender, EventArgs e)
{
myTable = new Table();
}
protected void btnAdd_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 1;i++ )
{
tr = new TableRow();
for(int j = 0; j <=3; j++)
{
td = new TableCell[3];
td[0].Text = txtName.Text;
td[1].Text = txtMail.Text;
td[2].Text = txtAge.Text;
tr.Cells.Add(td[j]);
}
}
myTable.Rows.Add(tr);
Panel1.Controls.Add(myTable);
}
Break down the problem into smaller pieces.
1) Creating the table
You said you already created "it". I don't know if you meant the text boxes or the table, but if you are already using design view, continue using it. There is a table control in the Toolbox you can drag onto the web page. Do that. Specify three columns and give them names.
2) Adding the inputs to the table
Don't worry about new rows yet. Just focus on getting the values in the text boxes into the first row of the table.
Double click the web page button. This should create code for a Click event. Here is where you add the code to copy the data from the boxes into the table. Something like :
`DataRow row = new DataRow();
row[0] = textboxName.Text;
row[1] = textboxEmail.Text;
row[2] = textboxAge.Text;
yourTable.Rows.Add(row);`
Obviously you want to replace the names here with whatever you named them.
I have the following code :
Label docsLabel = new Label();
docsLabel = (Label)tasksPlaceholder.FindControl("taskdocs_" + taskId);
int index = tasksPlaceholder.Controls.IndexOf(docsLabel);
The label is found within the placeholder, but when I call .IndexOf() it always returns -1.
How do I find the correct position of this control?
This is an important information in your comments:
the element I want to update is 3 levels down (TableRow -> TableCell ->Label)
Control.FindControl finds all control in this NamingContainer whereas ControlCollection.IndexOf finds only controls in this control. So if this control contains for example a table which contains rows and cells and every cell contains also controls, all of these controls will not be found by IndexOf, only the top-control is searched.
Control.FindControl will search all controls that belong to this NamingContainer(a control that implements INamingContainer). A table/row/cell does not implement it, that's why all of these controls are also searched with FindControl.
However, FindControl will not search through sub-NamingContainers (like a GridView in a GridViewRow).
This reproduces your issue:
protected void Page_Init(object sender, EventArgs e)
{
// TableRow -> TableCell ->Label
var table = new Table();
var row = new TableRow();
var cell = new TableCell();
var label = new Label();
label.ID = "taskdocs_1";
cell.Controls.Add(label);
row.Cells.Add(cell);
table.Rows.Add(row);
tasksPlaceholder.Controls.Add(table);
}
protected void Page_Load(object sender, EventArgs e)
{
Label docsLabel = (Label)tasksPlaceholder.FindControl("taskdocs_1");
int index = tasksPlaceholder.Controls.IndexOf(docsLabel);
// docsLabel != null and index = -1 --> quod erat demonstrandum
}
How do I find the correct position of this control?
If you want to find the row-number this label belongs to:
Label docsLabel = (Label)tasksPlaceholder.FindControl("taskdocs_1");
TableRow row = (TableRow)docsLabel.Parent;
Table table = (Table)row.Parent;
int rowNumber = table.Rows.GetRowIndex(row);
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 have one table control "table1"
And added controls to it in click event of one button as :
protected void Button2_Click(object sender, EventArgs e)
{
TableRow row;
TableCell cell;
for (int i = 0; i < 3; ++i)
{
TextBox txt = new TextBox();
txt.Text = i.ToString();
row = new TableRow();
cell = new TableCell();
cell.Controls.Add(txt);
row.Controls.Add(cell);
Table1.Controls.Add(row);
}
}
but i cant retrieve this controls in click event of another button. i think it is because of postback.
How can i prevent it?
When adding controls dynamically, if you want to access them on postback, you need to re-create them every time the page is loaded.
Adding them in the click handler of one button and expecting them to be there for the click handler of another button will not work, as they have not been re-created on the second postback.
You need to understand the asp.net page life cycle and change the logic of your page to fit in with it.