Findcontrol in Multiview nested in Formview - c#

I have a multiview inside a formview:
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:FormView ID="fvSpec" runat="server" DataKeyNames="ID" OnItemUpdating="fvSpec_ItemUpdating"
OnItemInserting="fvSpec_ItemInserting" OnModeChanging="fvSpec_ModeChanging">
<EditItemTemplate>
<asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
<asp:View ID="View1" runat="server">
<asp:DropDownList ID="ddlEditProdType" runat="server" SelectedValue='<%# Bind("CommonID") %>'></asp:DropDownList>
When switching to edit mode, the dropdownlist need to be bound:
protected void fvSpec_ModeChanging(object sender, FormViewModeEventArgs e)
{
int tableID = int.Parse(ddlItems.SelectedValue);
switch (e.NewMode)
{
case FormViewMode.Edit:
fvSpec.ChangeMode(FormViewMode.Edit);
FillEditLists();
BindFormView(tableID);
}
}
private void FillEditLists()
{
MultiView MultiView1 = (MultiView)fvSpec.FindControl("MultiView1");
View View1 = (View)MultiView1.FindControl("View1");
//ddlEditProdType COMES BACK NULL
DropDownList ddlEditProdType = (DropDownList)View1.FindControl("ddlEditProdType");
//bind ddl here
}
The formview control is found just fine.
The multiview control is found just fine.
The view control is found just fine.
But when the dropdownlist line is executed, it comes back NULL.
(object ref error)
Any input is appreciated.

Have you tried something like this:
private Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
Comes from this CodingHorror article. I'm kind of surprised we're on .NET 4.0 and Microsoft hasn't built something like this in yet...I use it a lot.

Found out why it was not finding the control.
The formview needed to be bound first, then find that certain control.
BindFormView(tableID);
FillEditLists();

Related

Get all TextBoxes in a page

Very simple but I can't figure out why it wont works.
I got five TextBox and one Button, click to count the number of TextBox.
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Testing._Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:Label ID="Label1" runat="server" Text="Test"></asp:Label> </br>
<asp:TextBox ID="TextBox1" runat="server" Width="40px"></asp:TextBox>
<asp:TextBox ID="TextBox2" runat="server" Width="40px"></asp:TextBox>
<asp:TextBox ID="TextBox3" runat="server" Width="40px"></asp:TextBox>
<asp:TextBox ID="TextBox4" runat="server" Width="40px"></asp:TextBox>
<asp:TextBox ID="TextBox5" runat="server" Width="40px"></asp:TextBox>
</br>
<asp:Button ID="Button1" runat="server" Text="Generate" OnClick="Button1_Click" />
</asp:Content>
Code behind
protected void Button1_Click(object sender, EventArgs e)
{
var List = this.Controls.OfType<TextBox>();
Label1.Text = List.Count().ToString();
}
But the result return me 0.
Since your TextBoxes are inside a ContentPlaceHolder so you need to replace the this keyword with your ContentPlaceHolder(MainContent). This should works as you want:
var List = (Page.Master.FindControl("MainContent") as ContentPlaceHolder)
.Controls.OfType<TextBox>();
Label1.Text = List.Count().ToString();
Most solutions will not work if text boxes are in tables or divs. The only way is to recursively look for them in all controls. Paste the following function in a class somewhere.
public static List<Control> GetAllControls(List<Control> controls, Type t, Control parent) //first call pass this.Page as the 'Parent' parameter
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == t)
controls.Add(c);
if (c.HasControls())
controls = GetAllControls(controls, t, c);
}
return controls;
}
Then from your asp page you call it as follows.
List<Control> list = new List<Control>();
list = GetAllControls(list, typeof(TextBox), this.Page);
This gets all controls of the type you passed (in my example it was TextBox)
Then you can iterate through the list of Textbox controls.
foreach (Control ctl in list)
{
if (ctl.GetType() == typeof(TextBox)) //this should always test true but you i left it here for clarity
{
//do something
((TextBox)ctl).Attributes.Add("onfocus", "this.select()");
}
}
Here is my implementation to add the select() attribute to all text boxes.
//my asp page
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
Utils.SetTextBoxFocusSelect(this.Page);
}
//in a utilities class
public class Utils
{
public static void SetTextBoxFocusSelect(Page page)
{
List<Control> list = new List<Control>();
list = GetAllControls(list, typeof(TextBox), page);
foreach (Control ctl in list)
{
if (ctl.GetType() == typeof(TextBox))
{
((TextBox)ctl).Attributes.Add("onfocus", "this.select()");
}
}
}
public static List<Control> GetAllControls(List<Control> controls, Type t, Control parent /* can be Page */)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == t)
controls.Add(c);
if (c.HasControls())
controls = GetAllControls(controls, t, c);
}
return controls;
}
}

ASP.NET find control by id

I am doing a simple web site using asp.net and i am having troubles finding my or objects by id in the code behind in c#. I have something like this:
<asp:ListView ID="InternamentosListView" runat="server"
DataSourceID="InternamentosBD">
<LayoutTemplate>
<table id="camas">
<asp:PlaceHolder runat="server" ID="TablePlaceHolder"></asp:PlaceHolder>
</table>
</LayoutTemplate>
the rest is irrelevant, and then i use this in the code behind:
Table table = (Table)FindControl("camas");
i also tried:
Table table = (Table)camas;
and
Control table= (Table)FindControl("camas");
and each one of this lines gives me Null. am i doing something wrong ?
EDIT: From your answers i did this:
<LayoutTemplate>
<table id="camas" runat="server">
</table>
</LayoutTemplate>
and tried all the things stated above. same result.
EDIT2: Whole Code:
C#
protected void Page_Load(object sender, EventArgs e)
{
Table table = (Table)FindControl("camas");
HiddenField NCamasHF = (HiddenField)FindControl("NCamas");
int NCamas = Convert.ToInt32(NCamasHF);
HiddenField NColunasHF = (HiddenField)FindControl("NColunas");
HiddenField CorMasc = (HiddenField)FindControl("CorMasc");
HiddenField CorFem = (HiddenField)FindControl("CorFem");
int NColunas = Convert.ToInt32(NColunasHF);
TableRow Firstrow = new TableRow();
table.Rows.Add(Firstrow);
for (int i = 1; i <= NCamas; i++)
{
if (i % NColunas == 0)
{
TableRow row = new TableRow();
table.Rows.Add(row);
TableCell cell = new TableCell();
cell.BackColor = System.Drawing.Color.LightGreen;
cell.CssClass = "celula";
row.Cells.Add(cell);
}
else
{
TableCell cell = new TableCell();
cell.BackColor = System.Drawing.Color.LightGreen;
cell.CssClass = "celula";
Firstrow.Cells.Add(cell);
}
}
}
asp.net
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:SqlDataSource
ID="ParametrosBD"
ConnectionString ="<%$ ConnectionStrings:principal %>"
ProviderName = "System.Data.SqlClient"
SelectCommand = "SELECT par_Id, par_NCamas, par_NColunas, par_CorMasculino, par_CorFeminino FROM Parametros WHERE par_Id=1"
runat="server">
</asp:SqlDataSource>
<asp:ListView ID="InternamentosListView" runat="server"
DataSourceID="InternamentosBD">
<LayoutTemplate>
<table id="camas" runat="server">
</table>
</LayoutTemplate>
<ItemTemplate>
<asp:HiddenField ID="NCamas" runat="server" Value='<%# Bind("par_NCamas") %>' />
<asp:HiddenField ID="NColunas" runat="server" Value='<%# Bind("par_NColunas") %>' />
<asp:HiddenField ID="CorMasc" runat="server" Value='<%# Bind("par_CorMasculino") %>' />
<asp:HiddenField ID="CorFem" runat="server" Value='<%# Bind("par_CorFeminino") %>' />
<tr id="cama"></tr>
</ItemTemplate>
</asp:ListView>
</asp:Content>
To expand on #Yura Zaletskyy's recursive find control method from MSDN -- you can also use the Page itself as the containing control to begin your recursive search for that elusive table control (which can be maddening, I know, I've been there.) Here is a bit more direction which may help.
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI;
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Table table = (Table)FindControlRecursive(Page, "camas");
if (table != null)
{
//Do awesome things.
}
}
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn = FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
}
As you already added runat="server" is step 1. The second step is to find control by id, but you need to do it recursively. For example consider following function:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
None of the existing answers mention the obvious built-in solution, FindControl.
Detailed here, FindControl is a function that takes as a string the id of a control that is a child of the calling Control, returning it if it exists or null if it doesn't.
The cool thing is that you can always call it from the Page control which will look through all controls in the page. See below for an example.
var myTextboxControl = (TextBox)Page.FindControl("myTextboxControlID);
if (myTextboxControl != null) {
myTextboxControl.Text = "Something";
}
Note that the returned object is a generic Control, so you'll need to cast it before you can use specific features like .Text above.
You need to have runat="server" for you to be able to see it in the codebehind.
e.g.
<table id="camas" runat="server">
Ok. You have the table within a list view template. You will never find the control directly on server side.
What you need to do is find control within the items of list.Items array or within the item data bound event.
Take a look at this one- how to find control in ItemTemplate of the ListView from code behind of the usercontrol page?
When you're using a master page, then this code should work for you
Note that you're looking for htmlTable and not asp:Table.
// add the namespace
using System.Web.UI.HtmlControls;
// change the name of the place holder to the one you're using
HtmlTable tbl = this.Master.FindControl("ContentPlaceHolder1").FindControl("camas") as HtmlTable;
Best,
Benny
You need to add runat server like previously stated, but the ID will change based on ListView row. You can add clientidmode="Static" to your table if you expect only 1 table and find using InternamentosListView.FindControl("camas"); otherwise, you will need to add ItemDataBound event.
<asp:ListView ID="InternamentosListView" runat="server" DataSourceID="InternamentosBD" OnItemDataBound="InternamentosListView_ItemDataBound">
<LayoutTemplate>
<table id="camas">
<asp:PlaceHolder runat="server" ID="TablePlaceHolder"></asp:PlaceHolder>
</table>
</LayoutTemplate>
You will need to introduce in code behind
InternamentosListView_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Table tdcamas = (Table)e.Item.FindControl("camas");
}
}

Invalid postback or callback argument (HiddenField and container Visible=false)

I've not found answers that matched my circumstances, so I'm posting an answered question hoping it will help others.
I was getting the error
Invalid postback or callback argument. Event validation is enabled
using in configuration or <%#
Page EnableEventValidation="true" %> in a page. For security
purposes, this feature verifies that arguments to postback or callback
events originate from the server control that originally rendered
them. If the data is valid and expected, use the
ClientScriptManager.RegisterForEventValidation method in order to
register the postback or callback data for validation.
at System.Web.UI.ClientScriptManager.ValidateEvent(String uniqueId, String argument)
at System.Web.UI.Control.ValidateEvent(String uniqueID, String eventArgument)
at System.Web.UI.WebControls.HiddenField.LoadPostData(String postDataKey, NameValueCollection postCollection)
at System.Web.UI.WebControls.HiddenField.System.Web.UI.IPostBackDataHandler.LoadPostData(String postDataKey, NameValueCollection postCollection)
at System.Web.UI.Page.ProcessPostData(NameValueCollection postData, Boolean fBeforeLoad)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
I've got a Databound ListView (with a few 100s rows), with buttons on each row. The buttons bring a popup. The popup has dropdownlists and other controls doing asynchronous postbacks. I need to make sure I do asynchronous postbacks to avoid refreshing my big table.
I get the error when I click the button on one row, then change a dropdownlist inside the popup which fires a postback (selected item changed). Boom.
Here's the markup for a reduced sample, without popup and javascript at all! It still exhibits the problem. Click twice on a button in a row to get the error.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestPopupAsynchPostback.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="scriptMgr" runat="server" ScriptMode="Debug" EnablePartialRendering="true"
EnableScriptGlobalization="true" EnableScriptLocalization="true" EnablePageMethods="true"/>
<asp:ObjectDataSource ID="ListDataSource" runat="server" SelectMethod="List" TypeName="TestPopupAsynchPostback.Default" />
<asp:Label runat="server" ID="PageLabel"></asp:Label>
<asp:ListView ID="EL" runat="server" DataSourceID="ListDataSource" OnItemDataBound="EntityList_OnItemDataBound">
<LayoutTemplate>
<table border="1">
<tr id="itemPlaceholder" runat="server" enableviewstate="false">
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr runat="server" id="DefaultRow" enableviewstate="false">
<td>
<asp:Label ID="Lbl" runat="server" EnableViewState="false" />
</td>
<td>
<button runat="server" type="button" id="ReportingButton" enableviewstate="false" onserverclick="ReportingButton_OnClick" causesvalidation="false">click</button>
</td>
</tr>
<%-- Fix part 1: Change SpecialRow visible = true--%>
<tr runat="server" id="SpecialRow" visible="false" enableviewstate="false">
<td>
<asp:Label ID="Lbl2" runat="server" EnableViewState="false" />
<asp:HiddenField runat="server" ID="fn_hid" />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</form>
</body>
</html>
Here's the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace TestPopupAsynchPostback
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ScriptManager sm = ScriptManager.GetCurrent(Page);
PageLabel.Text = DateTime.UtcNow.ToString() + " IsPostBack:" + IsPostBack + " IsInAsyncPostBack:" + (sm == null ? "" : sm.IsInAsyncPostBack.ToString());
}
protected override void Render(HtmlTextWriter writer)
{
ScriptManager sm = ScriptManager.GetCurrent(this.Page);
if (sm != null)
{
foreach (ListViewDataItem row in EL.Items)
{
HtmlButton reportingButton = row.FindControl("ReportingButton") as HtmlButton;
if (reportingButton != null)
sm.RegisterAsyncPostBackControl(reportingButton);
}
}
base.Render(writer);
}
public IList<string> List()
{
return (new string[] { "ONE", "TWO"}).ToList();
}
protected void ReportingButton_OnClick(object sender, EventArgs e)
{
//Do something useful here, for now, just a postback event
}
protected void EntityList_OnItemDataBound(object sender, ListViewItemEventArgs e)
{
Label lbl = e.Item.FindControl("Lbl") as Label;
Label lbl2 = e.Item.FindControl("Lbl2") as Label;
lbl.Text = lbl2.Text = e.Item.DataItem.ToString();
HtmlTableRow specialRow = e.Item.FindControl("SpecialRow") as HtmlTableRow;
if (e.Item.DataItemIndex%2 == 0)
{
HiddenField fn_hid = e.Item.FindControl("fn_hid") as HiddenField;
fn_hid.Value = "test1";
specialRow.Visible = true;
}
//Fix part 2: set SpecialRow Visible = false in code behind
//else
// specialRow.Visible = false;
}
}
}
I initially thought my javascript was at fault as I am modifying things with it. However, the process of creating a sample page has helped me find the problem.
It turns out it has nothing to do with the javascript or the popup. It's to do with a HiddenField contained inside a TR HtmlControl (asp.net server side control) with Visible=false IN THE MARKUP. I then use the code behind to set Visible=true when I need to in OnItemDataBound event.
This is what is causing the error for me: It seems that because the container (the TR SpecialRow) is Visible=false, I guess something is not rendered. I then come along in OnItemDataBound, decide that this row must be shown, and set Visible=true and set the value of my HiddenField. Boom.
If I remove the markup for the hidden field and don't set its value, no crash.
So it's not just the visibility of the TR on its own, it's the HiddenField too.
My fix: don't set Visible=false in the markup, and change OnItemDataBound to set Visible=false when required.
In other words: I was defaulting to hide things in markup and use code behind to show them. I changed this around and show things by default in markup and hide them using the code behind.
I've added the fix in the markup and code above.

Cannot find server control

I'm new at ASP.NET but something that continuously gives me trouble is finding nested server controls, especially when they are nested.
In this case, here is my registration page up until the server control I want:
<asp:CreateUserWizard runat="server" ID="RegisterUser" ViewStateMode="Disabled" OnCreatedUser="RegisterUser_CreatedUser">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="wizardStepPlaceholder" />
<asp:PlaceHolder runat="server" ID="navigationPlaceholder" />
</LayoutTemplate>
<WizardSteps>
<asp:CreateUserWizardStep runat="server" ID="RegisterUserWizardStep">
<ContentTemplate>
<fieldset>
<ol>
<li>
<asp:TextBox runat="server" ID="firstName" />
</li>
For readability sake, the only things I've removed are some HTML elements. I am trying to access'firstName'. I've tried all of the following with no luck, (TextBox first is always coming up null).
TextBox first = (TextBox)Page.Master.FindControl("MainContent").FindControl("firstName");
TextBox first = (TextBox)Page.FindControl("firstName");
TextBox first = (TextBox)RegisterUserWizardStep.FindControl("firstName");
TextBox first = (TextBox)RegisterUser.FindControl("firstName");
Would appreciate help, thanks!
Often times you may need to do a recursive control search. First, add this method to your page:
private Control FindControlRecursive(Control Root, string Id)
{
if (Root.ID == Id)
return Root;
foreach (Control Ctl in Root.Controls)
{
Control FoundCtl = FindControlRecursive(Ctl, Id);
if (FoundCtl != null)
return FoundCtl;
}
return null;
}
Now, to find the control, call:
TextBox firstName = (TextBox)FindControlRecursive(this, "firstName");
Try this piece of code:
TextBox first = (TextBox) RegisterUser.CreateUserStep.ContentTemplateContainer.FindControl("firstName");

How to Add UserControl Conditionally to Repeater Control?

I want to know how to Add a UserControl Conditionally to a Repeater Control. I have tried to add it to the placeholder which is in Repeater Control but unable to load the usercontrol. This following code doesn't work.
<asp:Repeater ID="ResultsRepeater" runat="server">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<div>
<asp:PlaceHolder ID="PlaceHolder1" runat="server">
</asp:PlaceHolder>
</div>
</ItemTemplate>
</asp:Repeater>
public void GetStatus(int i)
{
UserControl uc = new UserControl();
if(i==1)
{
uc = LoadControl("DraftList.ascx") as UserControl;
}
else if(i==2)
{
uc = LoadControl("FinalList.ascx") as UserControl;
}
PlaceHolder p1 = (PlaceHolder)ResultsRepeater.Items[0].FindControl("PlaceHolder1");
p1.Controls.Add(uc);
}
Is there some reason that you don't want to just handle all of this in the aspx? That would be the simplest and cleanest option:
<asp:Repeater runat="server" ID="ResultsRepeater">
<ItemTemplate>
<uc1:DraftList ID="DraftList1" runat="server" Visible='<%# ((int)Eval("Status") == 1)%>' />
<uc2:FinalList ID="FinalList1" runat="server" Visible='<%# ((int)Eval("Status") == 2)%>' />
</ItemTemplate>
</asp:Repeater>
If a control is not visible, (i.e., Visible=false) then no markup is rendered, so coding in this fashion would not create any more work for the server or the client browser, while having the benefit of being much easier to read and providing user control properties at design-time.
You would just need to make sure to register your controls at the top of the page:
<%# Register src="DraftList.ascx" tagname="DraftList" tagprefix="uc1" %>
<%# Register src="FinalList.ascx" tagname="FinalList" tagprefix="uc2" %>
Why don't you try adding it within the repeaters ItemDataBound event? I.e.,
<asp:Repeater ID="ResultsRepeater" OnItemDataBound="ResultsRepeater_ItemDataBound" runat="server">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<div>
<asp:PlaceHolder ID="PlaceHolder1" runat="server">
</asp:PlaceHolder>
</div>
</ItemTemplate>
</asp:Repeater>
and in the code behind
protected void ResultsRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Panel PlaceHolder1 = (Panel)e.Item.FindControl("PlaceHolder1");
// declare/obtain the value of i given the DataItem
// e.g.,
int i = ((int)e.Item.DataItem); // or however you're getting i
if (i == 1)
{
var uc = LoadControl("~/DraftList.ascx");
PlaceHolder1.Controls.Add(uc);
}
else if (i == 2)
{
var uc = LoadControl("~/FinalList.ascx");
PlaceHolder1.Controls.Add(uc);
}
}
}
Judging by your last comment (to the question) you might need to also make sure you've attached and bound your datasource to the repeater as well. I.e.,
ResultsRepeater.DataSource = dataSource; //whatever your datasource is e.g., datatable, IEnumerable list etc
ResultsRepeater.DataBind();

Categories

Resources