How to find nested controls within nested user controls - asp.net

I have a user control (Control1) which has a placeholder that may contain several additional user controls (of the same type - see below) which are added dynamically. How do I navigate the user control hierarchy to find the values of the nested sets of controls when the button located in Control 1 is clicked?
Control 1:
<%# Control Language="C#" AutoEventWireup="True" CodeBehind="Control1.ascx.cs" Inherits="Control1" %>
<%# Reference Control="Control2.ascx" %>
<div id="div1">
<div id="divPh"><asp:PlaceHolder ID="phControls" runat="server" /></div>
<div id="divContinue"><asp:Button ID="btnContinue" Text="Continue" OnClick="submit_Click" runat="server" /></div>
</div>
Code behind for Control1.aspx:
protected void submit_Click(object sender, EventArgs e)
{
// iterate through list of divControl2 controls and find out which radio button is selected
// for example, there may be 3 divControl2's which are added to the placeHolder on Control1, rdoBth1 might be selected on 2 of the controls
// and rdoBtn2 might be selected on 1 - how do I navigate this control structure?
}
Control 2 (Several of these may be added to the placeHolder on Control1):
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Control2.ascx.cs" Inherits="Control2" %>
<div id="divControl2">
<p><strong><asp:RadioButton ID="rdoBtn1" GroupName="Group1" Checked="true" runat="server" /> Check this</strong></p>
<p><strong><asp:RadioButton ID="rdoBtn2" GroupName="Group1" Checked="false" runat="server" /> No, check this one</strong></p>
</div>

Check below code and let me know if you have any queries.
protected void submit_Click(object sender, EventArgs e)
{
for (int count = 0; count < phControls.Controls.Count; count++)
{
UserControl uc = (UserControl)(phControls.Controls[count]);
if (uc != null)
{
RadioButton rdoBtn1 = new RadioButton();
RadioButton rdoBtn2 = new RadioButton();
rdoBtn1 = (RadioButton)(uc.FindControl("rdoBtn1"));
rdoBtn2 = (RadioButton)(uc.FindControl("rdoBtn2"));
if (rdoBtn1.Checked == true)
{
Response.Write("1st checked ");
}
else if (rdoBtn2.Checked == true)
{
Response.Write("2nd checked");
}
}
}

This isn't the best design in the world, but you can accomplish what you're looking for with some relative ease. The problem is that the page where these controls are will have to have intimate knowledge of the inner workings of the dynamically added controls. And, you're going to want them to implement a common abstract class or interface so that you can look for the right ones by type.
The following code assumes that you've created properties for accessing the internal controls values rather than having to reference the internal controls yourself. This is just good practice when you use any kind of control.
protected void submit_Click(object sender, EventArgs e) {
foreach (var control in phControls.Controls) {
IMySpecialControl mySpecialControl = control as IMySpecialControl;
if (mySpecialControl != null) {
// access some properties (and probably need a cast to the specific control type :(
}
}
}
Instead, why not just access the fields via Request.Form collection instead?
string rdoBtn1Value = Request.Form["rdoBtn1"];

Related

Page_Load Vs Page_Init for the user control in a Page

I have a ASP.NET page called customer.aspx which has a user control customerdetails.ascx inside of it. This user control reads data from the database and populate some fields. If the loading is successful (if there is some data), the user should stay on the page. Otherwise the page should be redirected to a different page. This redirection is done in the container customer.aspx page and not in the control.
What are the ASP.NET page cycle events (Page_Load Vs Page_Init) that I should use for customer.aspx and customerdetails.ascx for the above logic to work?
If you've established that both options work functionally, then, for performance, you should prefer Page_Init.
If you might end up with a redirect, then you want to give the server the chance to do that redirect as soon as possible in the page lifecycle, to avoid using resources on lifecycle steps that might not be necessary.
I was rereading this - https://web.archive.org/web/20210330142645/http://www.4guysfromrolla.com/articles/092904-1.aspx, old, but good. I think you should be able to do what you need from the Page_Load event of customer.aspx because user controls are loaded by then. simply check the user control's controls for values and redirect, or not.
edit:
Actually, I think it depends. controls prob have to be preloaded, which changes things.
If we need our dynamically added controls to maintain their view state it is paramount that these controls be added before the Load View State stage. That is, these controls must exist within the page's control hierarchy before the view state is loaded. There's only one stage before Load View State - Initialization. That means, if we want our dynamic controls to persist view state we must add them to the control hierarchy in the page's Init event.
One way to make the user control accessible (answer at bottom): https://forums.asp.net/t/1674095.aspx?Loop+through+User+Controls+in+Content+Page
edit:
User Control
<h4>The User Control</h4>
<div>
<asp:Label ID="lblUserCtrl" runat="server" Text="Label"></asp:Label>
</div>
.cs
public partial class Testing_Controls_TestCtrl : System.Web.UI.UserControl
{
// public properties
public Label lbl
{
get { return lblUserCtrl; }
set { lblUserCtrl = value; }
}
public bool isLoaded;
// end public properties.
protected void Page_Init(object sender, System.EventArgs e)
{
lblUserCtrl.Text = "User ctrl loaded at: " + DateTime.Now;
isLoaded = true;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
container page:
note: I still had to register the control even though I loaded it from code.
<%# Register Src="~/Testing/Controls/TestCtrl.ascx" TagPrefix="uc1" TagName="TestCtrl" %>
...etc...
<form id="form1" runat="server">
<h4>The Container Page</h4>
<div>
<asp:Label ID="lblContainerLabel" runat="server" Text="Label"></asp:Label>
</div>
<br />
<hr />
<asp:PlaceHolder ID="ph1" runat="server"></asp:PlaceHolder>
</form>
.cs
public partial class Testing_user_control_test : System.Web.UI.Page
{
private bool ucIsLoaded;
protected void Page_Init(object sender, System.EventArgs e)
{
Testing_Controls_TestCtrl c = (Testing_Controls_TestCtrl)Page.LoadControl("~/Testing/Controls/TestCtrl.ascx");
ph1.Controls.Add(c);
c.lbl.Text += "<br />This is text appended to the user control by the container page.";
ucIsLoaded = c.isLoaded;
}
protected void Page_Load(object sender, EventArgs e)
{
lblContainerLabel.Text = "Container page loaded at: " + DateTime.Now + "<br />The user control is loaded: " + ucIsLoaded.ToString().ToUpper();
}
}

Why binding event is not called after submit button clicked for user control (added code)

I have a web page that uses single user control and a asp:repeater that uses the same user control and both are created in Page_Load. Both bahave differently in a submit button clicking event. The binding event for each of the repeater (depdentBasicInfo)'s user control is called after submit button clicking before page_load. But the binding event is not for the single user control (spouseBasicInfo). Why? Also the user control create a runtime control (assuming a TextBox). I found there is no way to retrieve the runtime control's Text property after clicking the submit as the control becomes null after PostBack. How to retrieve the property easily? Could any one help me? Thanks.
<!--USER CONTROL -->
<asp:Panel runat="server" ID="PnlSpouseInformation" Visible="true">
<h3 id="ApplicantLabel" runat="server"></h3>
<div class="dependentInformation">
<asp:PlaceHolder ID="phDependentInformation" runat="server"></asp:PlaceHolder>
</div>
</asp:Panel>
<!-- code behind -->
public partial class userInfo : System.Web.UI.UserControl
{
public string identity;
public string applicantTitle
{
set { ApplicantLabel.InnerText = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
public void Bind()
{
WebControl textBox = new TextBox
{
Text = identity,
ID = "textbox"
};
phDependentInformation.Controls.Add(textBox);
}
}
<html>
<head runat="server">
<title>Test user control binding</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:userInfo runat="server" ID="SpouseBasicInfo" Visible="false" />
<asp:Repeater runat="server" ID="RptDependents" OnItemCreated="RptDependents_ItemCreated">
<ItemTemplate>
<uc1:userInfo runat="server" ID="DependentBasicInfo" />
</ItemTemplate>
</asp:Repeater>
<asp:LinkButton runat="server" ID="submit" OnClick="OnClickSubmit"><span>Submit</span></asp:LinkButton>
</div>
</form>
</body>
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
SpouseBasicInfo.identity = "spouse";
SpouseBasicInfo.Bind();
SpouseBasicInfo.Visible = true;
List<String> list = new List<string>();
list.Add("Dependent A");
list.Add("Dependent B");
RptDependents.DataSource = list;
RptDependents.DataBind();
}
}
protected void OnClickSubmit(object sender, EventArgs e)
{
if (!Page.IsValid)
{
return;
}
var textbox = SpouseBasicInfo.FindControl("textbox") as TextBox;
string spouseName = textbox.Text;
}
protected void RptDependents_ItemCreated(object sender, RepeaterItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.Item:
case ListItemType.AlternatingItem:
{
var dependentInfo = e.Item.DataItem as String;
var dependentBasicInfo = e.Item.FindControl("DependentBasicInfo") as userInfo;
if (dependentBasicInfo == null) return;
dependentBasicInfo.applicantTitle = "Dependent " + (e.Item.ItemIndex + 1);
dependentBasicInfo.identity = dependentInfo;
dependentBasicInfo.Bind();
}
break;
}
}
}
</html>
Can you please post your code so we can get a look at it? Also, you say the runtime control becomes null after post back. Is the text property of the control set after the page is rendered? if so you may be able to use a hidden asp label on the page that can hold the text property. When the page is posted back the hidden label still holds the last value held for the text property of said control.
"User control (spouseBasicInfo). Why? Also the user control create a runtime control (assuming a TextBox). I found there is no way to retrieve the runtime control's Text property after clicking the submit as the control becomes null after PostBack. How to retrieve the property easily? Could any one help me? Thanks."
I haven't been working with asp.net for a long time so I don't think I can answer all the questions you are asking. But, if you are creating a text box at runtime and using the placeholder to deliver the object to the screen then yes, the textbox control will be null after post back. This is because it is not an asp.control like a textbox or label that is not runtime created. You can retrieve the value previously held in the text box by creating a label on screen that is hidden. Place your value in the hidden label and the runtime created text box. After post back the hidden label will still hold the value previous to post back and you can retrieve as you would normally retrieve a value held in a control. I hope this helps you get a step closer to solving your issue. Sorry I can't be of more help. Good luck.

why the form posted values are not those submitted by users?

I use Asp.net web forms. I have a form with a table and a combo box. The table changes as the selected item of the combo box changes. The combo box is an asp.net control and I set autopostback = true. The table is also an asp.net control, and all table cells are created/rendered in the server side.
Users will input values in the table and submit it to the server.
The problem I find is that when a user changes the selected item of the combo box, the table changes and the web page rendered correctly. Then the user inputs some values and clicks submit. From the server side, the value I get is the default values of the table, not the user inputs. If the user submits again, the server side can get the user inputs.
Here is the code I write to reproduce this issue. I create a default web form project, add a new web from which inherits the site master. To reproduce, take following steps: 1. select one radio button 2. submit and you will see a text about your selection at the top of the page. 3. change the combo box selection 4. select another radio button 5. submit and you will find the bug. 6. redo 4 and 5, you will find the text correct.
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="PostBackUsingMasterPage.aspx.cs" Inherits="WebFormBug.PostBackUsingMasterPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<asp:DropDownList ID="comboBox" runat="server" AutoPostBack="true" OnSelectedIndexChanged="UpdateTable">
<asp:ListItem>Apple</asp:ListItem>
<asp:ListItem>Beet</asp:ListItem>
<asp:ListItem>Citron</asp:ListItem>
</asp:DropDownList>
<asp:Label ID="userInput" runat="server"></asp:Label>
<asp:Table runat="server" ID="testTable"> </asp:table>
<asp:Button ID="submit" runat="server" Text="Submit for validation" OnClick="SubmitButton_Click" />
</asp:Content>
The aspx.cs is like this
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace WebFormBug
{
public partial class PostBackUsingMasterPage : Page
{
private string _scope;
protected void Page_Load(object sender, EventArgs e)
{
_scope = comboBox.SelectedValue ?? "Apple";
PopUpTable(_scope);
}
private void PopUpTable(string item)
{
testTable.Rows.Clear();
var row = new TableRow();
row.Cells.Add(new TableCell {Text = item});
row.Cells.Add(AddRadioButtons(item));
testTable.Rows.Add(row);
}
private TableCell AddRadioButtons(string name)
{
var cell = new TableCell();
var radioButtons = new HtmlInputRadioButton[5];
for (var i = 0; i < 3; i++)
{
radioButtons[i] = new HtmlInputRadioButton { Name = name, Value = name + " " + i };
cell.Controls.Add(radioButtons[i]);
var label = new Label { Text = name + " " + i };
cell.Controls.Add(label);
}
return cell;
}
protected void UpdateTable(object sender, EventArgs e)
{
PopUpTable(comboBox.SelectedValue);
}
protected void SubmitButton_Click(object sender, EventArgs e)
{
int valueIndex = 1;
for (int i = 0; i < testTable.Rows.Count; i++)
{
var row = testTable.Rows[i];
string inputValue = null, inputName = null;
foreach (var ctrl in row.Cells[valueIndex].Controls)
{
if (ctrl is HtmlInputRadioButton)
{
var radioInput = (HtmlInputRadioButton) ctrl;
if (!radioInput.Checked) continue;
inputValue = radioInput.Value;
inputName = radioInput.Name;
break;
}
}
if (inputName != null && inputValue != null)
{
userInput.Text = inputName + " " + inputValue;
}
}
}
}
}
Preparation knowledge of ASP.NET WebForm: dynamically added table data is missing from form post asp.net table adding rows dynamically don't remain after postback. Your sample is the case - you are composing table data dynamically.
Solution for posting back dynamic table data is to re-create the form data in each post back in (and should ONLY in) page Load method. and your sample is doing this (the PopUpTable method is always called in Page_Load).
However, in your code Page_Load is not the only place doing table re-creation, but also in OnSelectedIndexChanged which results in cleaning your table data. Actually, you don't need register this event.
So, solution (sorry for attaching the code as image, but I found attaching large segment of code is very hard to format):
Remove OnSelectedIndexChanged
Change code behind as below:
Try this:
in page load:
if(!IsPostBack)
{
// set data to table
}
I'm assuming you have updatepanel with which the change of the selected index on the dropdown updates the radiobuttons. You could try changing the eventhandler for the dropdown to
protected void UpdateTable(object sender, EventArgs e)
{
if (!IsPostBack)
{
PopUpTable(comboBox.SelectedValue);
}
}

cannot access hiddenfield value in masterpage on page_load event from child page

I'm trying to access a hiddenfield value from my masterpage that is set in my child aspx page, but cannot access it the masterpage codebehind page_load event.
Child aspx page:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="Server">
<telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server">
</telerik:RadStyleSheetManager>
<div class="center_content">
<div style="text-align: left">
<h2>
</h2>
</div>
<div style="text-align: left">
<uc1:Chart ID="Chart1" runat="server" />
</div>
</div>
<asp:HiddenField ID="hid1" runat="server" Value="Satellite Availability % Report" />
Master page:
<asp:Label runat="server" ID="Label1" Style="text-align: right; font-size: xx-large; color: #808080"></asp:Label>
Master page code behind: This is where I want to set the text value of the report from the child page.
protected void Page_Load(object sender, EventArgs e)
{
HiddenField hid1 = (HiddenField)MainContent.FindControl("MainContent_hid1");
if (hid1 != null)
{
Label1.Text = hid1.Value;
} }
<input type="hidden" name="ctl00$MainContent$hdnRptTitle" id="MainContent_hdnRptTitle" value="Satellite Availability % Report" />
There is no intellisense for the hdnRptTitle variable.
How can I get this to work? It seems simple enough, but don't know why it not working...
You can add the below code in your MasterPage:
HiddenField hid1 = (HiddenField)MainContent.FindControl("hid1");
if (hid1 != null)
{
Label1.Text = hid1.Value;
}
EDIT: Make sure your Label on the MasterPage is outside your ContentPlaceHolder, as I made this mistake when I first tested.
The above code should work as provided, with your control names, I'm not sure why you are using:
.FindControl("MainContent_hid1");
instead of
.FindControl("hid1");
You can use like this.
There can be multiple conterntPlaceHolder on your master page.
use the id which contains your hidden field in this case I assume that it is ContentPlaceHolder1
HiddenField hid1 = (HiddenField)ContentPlaceHolder1.FindControl("hdnRptTitle");
if (hid1 != null)
{
Label1.Text = hid1.Value;
}
There is a similar post on so
How to access content page controls from master page in asp.net
You can reference a master page and get the control like this:
VB.Net:
Dim te As HiddenField
Dim val As String
te = Me.Page.Master.FindControl("hdnRptTitle")
val = te.Value
c#:
HiddenField te = default(HiddenField);
string val = null;
te = this.Page.Master.FindControl("hdnRptTitle");
val = te.Value;
Why do you think that you can access a control in a content-page of a master-page? A MasterPage is used for multiple pages, why do you want to hardlink it with a specific page, it won't work anymore without it. If the HiddenField is essential for the operation of the master you should declare it there.
For every child page, there is a different rpt title which needs to
show up on the master page. How can I accomplish this?
Then the content page can access it's master to set the text but not vice-versa.
You could provide a public property in the master, e.g.:
public string ReportTitle
{
get { return this.LblReportTitle.Text; }
set { this.LblReportTitle.Text = value; }
}
and in the ContentPage, for example in it's Page_Load:
protected void Page_Load(object sender, EventArgs e)
{
// assuming the type of your master is SiteMaster
var master = this.Master as SiteMaster;
if (master != null)
master.ReportTitle = hdnRptTitle.Value;
}
This approach is still linking the master with one (or multiple) of it's childs, but it would still "work" if the content-page would use a different master-type. You'd also be informed with a compiler error if somebody remove or change the property.
However, where the content stores the report-type or where the master displays it is an implementation detail and can be changed in future without breaking anything.

How to add persistent dynamic controls based on user input (not on initial page load)

I am familiar with creating and persisting dynamic controls on the first load of a page and on subsequent postbacks but I am having trouble with the following user initiated scenario...
In my demo I have a placeholder, two buttons and a literal
<div>
<asp:PlaceHolder ID="phResponses" runat="server" />
</div>
<div>
<asp:Button ID="btnAdd" Text="Add" runat="server" OnClick="Add"/>
<asp:Button ID="btnInspect" Text="Inspect" runat="server" OnClick="Inspect"/>
</div>
<div>
<asp:Literal ID="litResult" runat="server"/>
</div>
I want the user to be able to click the add button to provide a response so I have...
protected void Page_Init(object sender, EventArgs e)
{
BuildControls();
}
protected void Add(object sender, EventArgs e)
{
BuildControls();
}
protected void BuildControls()
{
phResponses.Controls.Add(new LiteralControl { ID = "response_" + _Count.ToString() });
_Count++;
}
_Count is a static member variable to enable me to have unique ids for the new controls. I realise I need to rebuild the dynamic controls on Page_Init but the problem is that I end up with 2 new Literal controls on every postback. Also if any Text property is put into the new controls it is lost when the controls are rebuilt.
So how do I avoid adding multiple controls and how do I persist newly added properties when rebuilding these controls?
I use the following to inspect the responses
protected void Inspect(object sender, EventArgs e)
{
foreach (var control in phResponses.Controls)
{
if (control is LiteralControl)
{
litResults.Text += "<p>" + control.Text + " : " + control.ID + "</p>";
}
}
}
Which itself adds another unwanted control because of the rebuilding on Page_Init
I'd not sure that I quite understand what you're asking, but it looks like you just want to ensure that BuildControls is only called once per lifecycle. You could do that by making the following changes:
Add a new private bool _isControlsBuilt = false;.
Change Page_Init to check _isControlsBuilt before calling BuildControls.
Set _isControlsBuilt to true within BuildControls.
Make sure that BuildControls occurs earlier in the page lifecycle than Page_Init.
As for losing the values of controls on postback, it'll be that they're never hitting the viewstate. I'm not sure if it'd work, but my first guess would be to add a line to the end of BuildControls to call Page.RegisterRequiresControlState:
protected void BuildControls()
{
LiteralControl newLiteral = new LiteralControl { ID = "response_" + _Count };
this.RegisterRequiresControlState(newLiteral);
phResponses.Controls.Add(newLiteral);
_Count++;
_isControlsBuilt = true;
}
If that doesn't work (which might imply that it's the _view_state, not the _control_state that matters to you here), you may need to look at rolling your own viewstate. I wrote about how to do that in my answer to #3854193, which you might find useful.

Resources