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);
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 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!
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 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 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);
}
}
}
}
}