Creating Dynamic controls based on the selected value of a static control - asp.net

I have a drop down list holding some choices. Based on user selection I need to create some dynamic controls and render them on the form.
My understanding is that dynamic controls need to be created in OnInit or in CreateChildControls so that the ViewState for these dynamic controls is restored correctly by the runtime.
The problem is, I am unable to get the SelectedValue of the dropdown in OnInit or CreateChildControls since the ViewState has not been restored for the dropdown as yet.
Is there any way to obtain the current selection so that I can create the dynamic controls based on the current user selection and add them the page correctly
EDIT:
The markup looks as follows:
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="ddl" runat="server" AutoPostBack="true" AppendDataBoundItems="true">
<asp:ListItem Text="(Select Color)" Value="" />
<asp:ListItem Text="Red" Value="Red" />
<asp:ListItem Text="Green" Value="Green" />
<asp:ListItem Text="Blue" Value="Blue" />
</asp:DropDownList>
<asp:PlaceHolder ID="plHolder" runat="server" />
</div>
</form>
and here is the code behind:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void CreateChildControls()
{
base.CreateChildControls();
TextBox tb = new TextBox();
if (ddl.Text != "")
{
tb.Text = ddl.Text;
if (Session["id"] != null)
{
string id = Session["id"].ToString();
tb.ID = id;
}
else
{
Session["id"] = tb.ID = Guid.NewGuid().ToString().Replace("-", "");
}
plHolder.Controls.Add(tb);
}
}
}
On the line "tb.Text = ddl.Text;" I'm hoping to get the current selection and based on that set the value of the text property for the dynamic control. But the current selection has not been set yet since it's in OnInit.

If the controls really need to be created in OnInit or CreateChildControls then one thing you can do is get the value of your static control from the Request.Form[] collection during OnInit.
So instead of doing:
string selected = myDropDown.SelectedValue;
you do
string selected = Request.Form[myDropDownUniqueID];
... where myDropDownID is the 'unique id' assigned to myDropDown. Note that usually this will be the same as the 'id' assigned to the control, unless it is inside a control container.
This is effectively pulling the value straight out of the HTML Form data that gets sent to the server, rather than waiting for ASP.NET to unpack it into the properties of the control.

In one of my projects I add controls dynamically in Page_Load.
I use SaveControlState and LoadControlState to manually save and load the control view state.
http://msdn.microsoft.com/en-us/library/system.web.ui.control.loadcontrolstate.aspx
Shay.

Related

asp:ListBox SelectedValue not setting (dynamically or when clicked)

I have an asp:ListBox that is populated dynamically from js based on the selected value of another asp:List box. The problem is that the second list box always returns a SelectedValue of "" regardless of whether i set lstBox.selectedIndex = 0 or actually select an item in the list.
.js to add to list then set default selected item
var Option = document.createElement("option");
lstId = document.getElementById(lstId);
Option.text = lstItem;
lstId.add(Option);
lstId.selectedIndex = 0;
.vb to get selected value
Dim selSchedule As String = lstCRMSched.SelectedValue
Now as this list is populated by javascript i had to set my #page EnableEventValidation = "false" otherwise the postback that came later would fail.
Side note: I'm noticing that asp.net doesn't like it when you use hidden divs as overlays that are unhidden based on menu selections as everything it does requires a postback, which wipes out the state of the other divs. Should i just have 10 .aspx files one for each div and just switch locations from the codebehind using sessions to transfer things like selected values and data that is to be shown in another div?
You can access the SelectedValue of the drop down list through the Request object since every element in the form that has a name is submitted in the request.
You simply need to do this:
Dim selSchedule As String = Request[lstCRMSched.UniqueID]
Now, this will work just because you disabled EventValidation on the page. The error you were getting is completely normal. ASP.NET is just making sure that no one sends data that wasn't rendered by the server initially to prevent attacks. If you were to keep the EventValidation enabled on the page, you'd need to register the list for Validation via ClientScriptManager.RegisterForEventValidation
If you add items to the dropdownlist on the client these items are not persisted on the server!
But you may try saving dynamically added items (text-value-pairs) within some hidden input fields and parse them out on the server. See this link for a working example. For your example you will also have to save your selectedIndex within another hidden field to be able to access it on the server.
EDIT
demo.aspx
<script type="text/javascript">
function saveValue() {
var hiddenField1 = document.getElementById("hiddenField1");
hiddenField1.value = "hello world";
}
</script>
<form id="Form1" method="post" runat="server">
<input type="hidden" id="hiddenField1" name="hiddenField1" value="" />
<asp:Button ID="btnPostBack" runat="server" Text="PostBack"
OnClientClick="saveValue()" onclick="btnPostBack_Click" />
</form>
demo.aspx.cs
protected void btnPostBack_Click(object sender, EventArgs e)
{
Debug.WriteLine("Value of hiddenField1: " + Request["hiddenField1"]);
Debugger.Break();
}
This one worked for me. I got "hello world" on the server.
EDIT 2
Icarus pointed out that you can always access any submitted element to the server by referring to the Request object and of course he is absolutelly right! According to your question I thought you'd like to have access to all dynamically created items - and that is - with solution shown below - not possible.
aspxPage
<script type="text/javascript">
function saveToList() {
var ListBox1 = document.getElementById("ListBox1");
var ListBox2 = document.getElementById("ListBox2");
var Option = document.createElement("option");
Option.text = ListBox1.options[ListBox1.selectedIndex].text;
ListBox2.add(Option);
ListBox2.selectedIndex = 0;
}
</script>
<form id="Form1" method="post" runat="server">
<asp:ListBox ID="ListBox1" runat="server">
<asp:ListItem Text="entry1" Value="1" />
<asp:ListItem Text="entry2" Value="2" />
<asp:ListItem Text="entry3" Value="3" />
<asp:ListItem Text="entry4" Value="4" />
</asp:ListBox>
<asp:ListBox ID="ListBox2" runat="server" Width="100"></asp:ListBox>
<input id="Button1" type="button" value="Save to List" onclick="saveToList()" />
<asp:Button ID="btnPostBack" runat="server" Text="PostBack" OnClick="btnPostBack_Click" />
codeBehind
protected void btnPostBack_Click(object sender, EventArgs e)
{
Debug.WriteLine("SelectedValue of ListBox2: " + Request["ListBox2"]);
// no access to all other clientSide created items in ListBox2
Debugger.Break();
}

Controls within .ascx are not displaying their new value on postback

This seems like an elementary issue but it has me stumped.
I have a main page which loads a custom control (.ascx) on page_load.
This custom control has two fields. One of them being a dropdownlist and the other being a text box. When the dropdownlist changes value it triggers a post back, some logic is executed server side and a value is generated for the textbox. I stepped through the code and the value is created and assigned correctly. However, when the page is rendered the value does not change.
If I change the textbox value to "QQQ" and trigger the postback, "QQQ" stays in the textbox so I can verify viewstate is working.
Is there any reason why the generated value is not being displayed in the form on postback. This process works fine on the initial page load.
.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
string ascxPath = #"~/_CONTROLTEMPLATES/TRC_BITS.ascx";
TRC_BITS control = Page.LoadControl(ascxPath) as TRC_BITS;
phForm.Controls.Add(control);
}
.ascx
<asp:TextBox ID="message" runat="server" TextMode="MultiLine" /><br/>
<asp:DropDownList ID="year" runat="server" AutoPostBack="true">
<asp:ListItem Text="2011">2011</asp:ListItem>
<asp:ListItem Text="2012">2012</asp:ListItem>
<asp:ListItem Text="2013">2013</asp:ListItem>
</asp:DropDownList>
.ascx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
year.SelectedValue = DateTime.Now.Year.ToString();
}
if (year.SelectedValue == 2012)
message.Text = "ABC";
else
message.Text = "XYZ";
}
Because you're adding these controls to the page dynamically, you need to assign an ID to the user control. Make sure to assign the same ID to the controls every time the page is posted back, and the fields will be repopulated from ViewState.
Also, as Shai suggested, it would be more appropriate if you loaded the controls during OnInit instead of Page_Load. The situation is a little different with user controls, but in general you want to add your dynamic content before the LoadViewState method is executed.
If you're looking for something to take the pain out of persisting dynamic content, I would suggest taking a look at the DynamicControlsPlaceHolder.
Because you are adding the controls dynamically, you need to add them during the page's oninit event.
Try it, believe me. Go for it. Yalla.

Can I populate list items in a ASP.NET dropdown list from a ASP.NET Placeholder?

I'm designing my own custom control that contains a .NET dropdownlist. What I'm wondering is if it is possible to populate my dropdownlist with listitems placed in a placeholder?
For example:
<asp:DropDownList ID="ddlFilter" runat="server" >
<asp:PlaceHolder ID="ListItemPlaceholder" runat="server"/>
</asp:DropDownList>
This doesn't work because the DropDownList control only allows ListItems as child controls. But, I want to do something similar to this so when the user includes my control on a page, they can do something like this:
<mytag:MyControl Mode="DropDown" runat="server">
<ListItemTemplate>
<asp:ListItem Text="C" Value="c"></asp:ListItem>
<asp:ListItem Text="E" Value="e"></asp:ListItem>
<asp:ListItem Text="B" Value="b"></asp:ListItem>
</ListItemTemplate>
</myTag:MyControl>
Any suggestions or ideas? I know I can do this by dynamically adding the ListItems in the page code behind, but I'd like to avoid that if possible. Thanks!
One approach to this would be to give your control a [ParseChildren(true, "ListItemTemplate")] attribute.
If you then had a property called "ListItemTemplate", which was an array of ListItems, your markup would be parsed into that property. At runtime in your control, you could simply hand your dropdownlist the contents of that property.
Some non-tested example code:
[ParseChildren(true, "ListItemTemplate")]
public class MyControl: Control
{
private ArrayList employees = new ArrayList();
private DropDownList myDropDownList = new DropDownList();
public ArrayList ListItemTemplate
{
get
{
return employees;
}
}
protected override void CreateChildControls()
{
myDropDownList.Items.AddRange(ListItemTemplate):
Controls.Add(myDropDownList);
}
}

Set Custom ASP.NET UserControl variables when its in a Repeater

<%# Register Src="~/Controls/PressFileDownload.ascx" TagName="pfd" TagPrefix="uc1" %>
<asp:Repeater id="Repeater1" runat="Server" OnItemDataBound="RPTLayer_OnItemDataBound">
<ItemTemplate>
<asp:Label ID="LBLHeader" Runat="server" Visible="false"></asp:Label>
<asp:Image ID="IMGThumb" Runat="server" Visible="false"></asp:Image>
<asp:Label ID="LBLBody" Runat="server" class="layerBody"></asp:Label>
<uc1:pfd ID="pfd1" runat="server" ShowContainerName="false" ParentContentTypeId="55" />
<asp:Literal ID="litLayerLinks" runat="server"></asp:Literal>
</ItemTemplate>
</asp:Repeater>
System.Web.UI.WebControls.Label lbl;
System.Web.UI.WebControls.Literal lit;
System.Web.UI.WebControls.Image img;
System.Web.UI.WebControls.HyperLink hl;
System.Web.UI.UserControl uc;
I need to set the ParentItemID variable for the uc1:pdf listed inside the repeater.
I thought I should be able to find uc by looking in the e.Item and then setting it somehow. I think this is the part where I'm missing something.
uc = (UserControl)e.Item.FindControl("pfd1");
if (uc != null) { uc.Attributes["ParentItemID"] = i.ItemID.ToString(); }
Any thoughts would be appreciated.
Also tried this with similar results... when I debug inside my usercontrol (pfd1) the parameters I am trying to set have not been set.
uc = (UserControl)e.Item.FindControl("pfd1");
if (uc != null)
{
uc.Attributes.Add("ContainerID", _cid.ToString());
uc.Attributes.Add("ParentItemId", i.ItemID.ToString());
}
UPDATE: It looks like my controls are not connected by a namespace. I've wrapped by the parent control (Layer) and the PressFileDownlad control in a namespace "MyControls". Also updated their Inherits reference on the aspx to read "MyControls.xxxxx". I'm able to type "MyControls.Layer" inside the code on layer.aspx.cs but I'm not able to get "MyControls.PressFileDownload"
If you implement ParentItemID as a public property in your user control, then you should be able to set it declaratively, e.g:
<asp:Repeater id="Repeater1" ...>
<ItemTemplate>
<uc1:pfd ID="pfd1" runat="server" ParentItemId='<%# Eval("ItemID") %>' ... />
Martin is right you should be able to set it in declarative way (in case your property is public) .
But your way should also work (just cast it properly)
((PressFileDownload)e.Item.FindControl("pfd1")).ParentItemId = 0;
The best way is to implement the OnDataBinding event for the user control. I try to stay away from putting code inline in the aspx using webforms if possible.
When the repeater gets bound, for each item that is bound, the OnDataBinding will fire for your user control and your handler can do what it needs. You don't have to go searching for the controls.
Here is an example:
// in your aspx
<uc1:pfd ID="pfd1" runat="server" ShowContainerName="false" ParentContentTypeId="55"
OnDataBinding="pfd1_DataBinding" />
// in your codebehind implement the OnDataBinding event
protected void pfd1_DataBinding(object sender, System.EventArgs e)
{
pfd uc = (pfd)(sender);
uc.ContainerID = _containerID.ToString();
uc.ParentItemID = Eval("ItemID");
// Here you can do more like access other items like hidden fields
// or cached objects or even other controls etc... Skys the limit.
}
EDIT: Notice from your comment you require more data than what is found in the datasource. In this case what I usually do is just make private member variables in the .cs that I store data in. So when you have the container ID just store it in a variable that will be accessible.
Eg in your .cs for your page:
public partial class _TestPage : System.Web.UI.Page
{
private int _containerID { get; set; }
Then when you load the data just set the _containerID property and it will be accessible in the OnDataBinding event. Just make sure you are binding after you have set the _containerID.

hidden field is null on !IsPostBack and not null on IsPostBack

First I'll apologize for the unclear title of my question. I wasn't sure how to succinctly describe my problem in a title.
I have a hidden field in my .aspx
<input type="hidden" name="hid1" value="0" />
I want to set the value of this field during the page load event, and if it is not a postback.
protected void Page_Load(object sender, EventArgs e) {
if (!Page.IsPostBack) {
// This doesn't work!
Request.Form["hid1"] = "1";
}
if (Page.IsPostBack) {
// This DOES work!
Request.Form["hid1"] = "1";
}
}
The problem is that the Request doesn't contain the hidden field in the Form array during the page load event when it's not a postback (ie - the first time the page is hit). Subsequent hits to the page (ie - postbacks) result in the Form array containing the hidden field.
I'm sure that it has to do with the lifecycle of the page, but what I really need to know is how do I set the hidden field during the page load event and when it is not a postback?
EDIT:
I really, really don't want to incorporate the runat="server" attribute!
You could define a property in your page class and then modify the property value in your code:
protected string HiddenFieldValue { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
HiddenFieldValue = "postback";
else
HiddenFieldValue = "not postback";
}
Then define the hidden form field like this so that it's value is set to the property value:
<input type='hidden' id='hidden1' value='<%=HiddenFieldValue %>' />
If you only want to set the value form the property during a postback or non-postback you could add the condition as well:
<input type='hidden' id='hidden1' value='<% if(IsPostBack) { %> <%=HiddenFieldValue%> <% } %>' />
Try converting the input into a HiddenField control (or, at least, a runat="server" input), and reference it by it's ID, rather than through Request.Form.
Instead of:
<input type="hidden" name="hid1" value="0" />
try this:
<asp:HiddenField runat="server" ID="hid1" />
Then in your Page_Load()
hid1.Value = "whatever...";
It will be visible both before and after postback when you declare it in this manner.
Why don't you make it a server control by setting 'runat="server"' on the input control? Then it will be accessible from your code behind, and you will be able to set the value during the first page load.
why dont you access that field through a style class and use runat server=?

Resources