Reference a dynamically created control's Text property - asp.net

I'm trying to refer to a dynamically created label's .Text property but can't find a way.
If I try referring to label1.Text it won't let me because it hasn't been created yet.
I'm trying:
Page.FindControl("label" & i.ToString).Text
This doesn't work either, although you can access the control's .ID property that way.
Any ideas?
I'm using Visual Studio Express 2012 For Web.

FindControl returns a System.Web.UI.Control which does not have a .Text property. You need to cast it to a Label. Try this:
Dim label = DirectCast(Page.FindControl("label" & i.ToString()), Label)
label.Text = "foo"

If a control is nested inside other controls, you need to find it recursively. In addition, you want to cast the control to Label control before using Text property.
Here is a helper method. It searches a control recursively.
Helper Method
public static Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
return root;
return root.Controls.Cast<Control>()
.Select(c => FindControlRecursive(c, id))
.FirstOrDefault(c => c != null);
}
Usage
var myLabel = FindControlRecursive(Page, "label" + i.ToString) as Label;
if(myLabel != null)
{
myLabel.Text = "abc";
}

Related

How to access control from parent aspx from its child ascx

I try to access the MainContentBlock control from the aspx, but unable to do so.
In the aspx file I have registered both controls:
<uc3:ContentBlock ID="MainContentBlock" runat="server" DynamicParameter="id" DefaultContentID="3951" /></uc3>
<uc3:childshow ID="Childshow" runat="server"/></uc3>
In the code behind for child.ascx
If Me.Parent.Page.FindControl("MainContentBlock") IsNot Nothing AndAlso Me.MainContentBlock.Item.Id = 4357 Then
...
But the error says BC30456: 'MainContentBlock' is not a member of 'child'.
It's almost like the ".parent" part did not work.
However, If I try the following:
If Me.Parent.MainContentBlock IsNot Nothing AndAlso Me.MainContentBlock.Item.Id = 4357 Then
...
It will bring up the error "BC30456: 'MainContentBlock' is not a member of 'System.Web.UI.Control'.
and seems it at least recognized the .parent part again.
confused... please help, thanks.
It's because you're trying to reference MainContentBlock as a property of the child control. When you use Me.MainContentBlock, Me refers to the child control.
You just need to use FindControl, and properly reference the found control:
Dim myBlock As ContentBlock = TryCast(Me.Parent.FindControl("MainContentBlock"), ContentBlock)
If myBlock IsNot Nothing Then
'do things with myBlock
End If
Depending on where the control is located on the page, you may need to find it recursively, but in a simple situation you would just do this:
var pnl = Page.FindControl("MainContentBlock") as Panel; //or whatever it is
if (pnl != null)
{
//your code here
}
Here's a recursive method if you need it:
public Control FindControlRecursive(string controlID, Control parentCtrl)
{
foreach (Control ctrl in parentCtrl.Controls)
{
if (ctrl.ID == controlID)
return ctrl;
FindControlRecursive(controlID, ctrl);
}
return null;
}
And you would call it like this:
var pnl ((PageName)Page).FindControlRecursive("MainContentBlock") as Panel;
FindControl works but the pain is that what you're looking for can be higher then just at parent level. Here is a handy method I use:
public static 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;
}

Get Selected Text asp.net custom server control

I need to get the latest text set in the custom control by javascript. When i tried to get the selected text from server control, it is always returning the default text & not the modified text. How to retain the latest value set by the javascript in servercontrol? Below is the complete code for your reference..
ServerControl1.cs
[assembly: WebResource("ServerControl1.Scripts.JScript1.js", "text/javascript")]
namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
public List<string> ListItems
{
get
{
return ViewState["items"] as List<string>;
}
set
{
ViewState["items"] = value;
}
}
public string Text
{
get
{
return (FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor).InnerText;
}
set
{
((FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor)).InnerText = value;
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
HtmlAnchor selectedTextAnchor = new HtmlAnchor();
selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
anchor.Attributes.Add("onclick", "updateData()");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
Controls.Add(selectedTextContainer);
ChildControlsCreated = true;
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string resourceName = "ServerControl1.Scripts.JScript1.js";
ClientScriptManager cs = this.Page.ClientScript;
cs.RegisterClientScriptResource(typeof(ServerControl1), resourceName);
}
}
}
JScript1.js
function updateData() {
var evt = window.event || arguments.callee.caller.arguments[0];
var target = evt.target || evt.srcElement;
var anchor = document.getElementById("anchorID");
anchor.innerText = target.innerText;
return false;
}
TestPage Codebehind
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<string> items = GetDataSource();
ServerControl1.ListItems = items;
ServerControl1.Text = "Select ..";
}
}
protected void ClientButton_Click(object sender, EventArgs e)
{
string selectedText = ServerControl1.Text;
}
The server won't get your client changes unless you POST the changes to him. Your HtmlAnchors are being rendered in HTML as <a> controls, and these type of controls won't POST anything to the server.
You're going to need an <input> control to input the changes into the server (that's why they're called input controls after all). I suggest an <input type=hidden> to hold the value of the anchor.innerText and keeps its state.
Your Javascript function needs to be modified so it updates the anchor.innerText AND updates the hidden input value as well. This way when the page gets posted back to the server you can retrieve the updated and client-modified value from the hidden field.
First you need to define as private fields your selectedTextAnchor and the hiddenField you are going to insert. This is because you need to access them in your CreateChildControls method as well as in the getter and setter of yout Text property. Much in the way the partial designer classes define the controls you want to have available in code-behind.
ServerControl.cs
private HtmlAnchor selectedTextAnchor;
private HtmlInputHidden hiddenField;
In the CreateChildControls method you need to insert the hidden field.
You'll notice I removed the use of ClientIDMode.Static. Using that mode would make your client controls to have the same fixed IDs and Javascript might get confused when you have multiple copies of your ServerControl in a page, and thus losing the reusable purpose of a custom control.
Instead, you need to provide your Javascript function with the ClientID's of the controls it needs to modify. The key here is that you need to attach your controls to the Control's hierarchy BEFORE you try to get their ClientID's.
As soon as you do this.Controls.Add(dummyControl), you're making dummyControl to become a part of the Page and its dummyControl.ClientID will be suddenly changed to reflect the hierarchy of the page you're attaching it into.
I changed the order at which your controls are attached to the Control's collection so we can grab their ClientID's at the time we build the onclick attribute and pass the parameters so your Javascript function knows which anchor and hiddenField to affect.
ServerControl.cs
protected override void CreateChildControls()
{
base.CreateChildControls();
// Instantiate the hidden input field to include
hiddenField = new HtmlInputHidden();
hiddenField.ID = "ANCHORSTATE";
// Insert the hiddenfield into the Control's Collection hierarchy
// to ensure that hiddenField.ClientID contains all parent's NamingContainers
Controls.Add(hiddenField);
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
// REMOVED: selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
selectedTextAnchor = new HtmlAnchor();
// REMOVED: selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
// Insert the selectedTextContainer (and its already attached selectedTextAnchor child)
// into the Control's Collection hierarchy
// to ensure that selectedTextAnchor.ClientID contains all parent's NamingContainers
Controls.Add(selectedTextContainer);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
// The updateData function is provided with parameters that will help
// to know who's triggering and to find the anchor and the hidden field.
// ClientID's are now all set and resolved at this point.
anchor.Attributes.Add("onclick", "updateData(this, '" + selectedTextAnchor.ClientID + "', '" + hiddenField.ClientID + "')");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
}
Note the use of the keyword this in the updateData function, it'll help us to grab the object that is triggering the action. Also note that both Id's are passed as strings (with single quotes)
The Javascript function would need to be modified so it updates the anchor and the hidden input field.
JScript1.js
function updateData(sender, anchorId, hidFieldId) {
// Update the anchor
var anchor = document.getElementById(anchorId);
anchor.innerText = sender.innerText;
// Update the hidden Input Field
var hidField = document.getElementById(hidFieldId);
hidField.value = sender.innerText;
return false;
}
The last thing to do is change the way you are setting and getting your Text property.
When you GET the property you need to check if it's a Postback, and if it is, then you want to check if among all the info that comes from the browser there is your HiddenInputField. You can grab all the info coming from the client right at the Request object, more specifically, in the Request.Form.
All enabled input controls on your page will be part of the Request.Form collection, and you can get their values by using Request.Form[anyInputControl.UniqueID]. Note that the key used for this object is the UniqueID, NOT ClientID.
Once you get your client-modified value from the hidden input, you assign its value to the selectedTextAnchor, otherwise it'll go back to the original "Select..." text.
When you SET the property, you just need to assign it to the selectedTextAnchor.
In both GET and SET you need to call EnsureChildControls(), which will actually call your CreateChildControls() to make sure that your selectedTextAnchor and hiddenField controls are instantiated before you try to get some of their properties. Pretty much the same way that it's done in Composite Controls.
ServerControl.cs
public string Text
{
get
{
EnsureChildControls();
if (this.Page.IsPostBack)
{
string HiddenFieldPostedValue = Context.Request.Form[hiddenField.UniqueID];
// Assign the value recovered from hidden field to the Anchor
selectedTextAnchor.InnerText = HiddenFieldPostedValue;
return HiddenFieldPostedValue;
}
else
{
return selectedTextAnchor.InnerText;
}
}
set
{
EnsureChildControls();
selectedTextAnchor.InnerText = value;
}
}
This way you can have a control that recognizes the changes made in client. Remember that server won't know any change in client unless you notice him.
Another approach would be to notice the server everytime you click a link through an ajax request, but this would require a whole new different code.
Good luck!

Validate AutoCompleteExtender

Hi
i need to validate a textbox value in order to accept only values that are in the completion list of the associated autocompleteextender control.
I'm using ajaxtoolkit (version 20229) on asp.net 2.0.
For now i use the code below to validate the textbox ; as you can see i had a hiddenfield that keep the selected key. The hiddenfield is set to 0 if the user enter a value without selecting it from the list.
Do you have any idea?
Thanks
/**** Javascript code
function AutoCompleteItemPopulated(source, eventArgs)
{
var assocHiddenField = document.getElementById( source.get_element().id+'_hidden');
assocHiddenField.value=0;
}
function AutoCompleteItemSelected(source, eventArgs)
{
var assocHiddenField = document.getElementById( source.get_element().id+'_hidden');
assocHiddenField.value = eventArgs.get_value();
}
/*****CODEBEHIND code used to populate the autocompletion list
[System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]
public static string[] getStrada(string prefixText, int count, string contextKey)
{
System.Collections.Generic.List<string> items = new System.Collections.Generic.List<string>();
DataSetIncidentiTableAdapters.StradarioTableAdapter adapter = new DataSetIncidentiTableAdapters.StradarioTableAdapter();
DataSetIncidenti.StradarioDataTable dtStrade = adapter.GetStrade(contextKey, prefixText);
foreach (DataSetIncidenti.StradarioRow strada in dtStrade.Rows)
{
items.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(strada.DenominazioneCompletaVia, strada.IdStrada.ToString()));
}
return items.ToArray();
}
Yes this can be validated; you need to use a CustomValidator to do this, which you can setup both a client and server validation function, and then check the hidden field for its value.
This works great for us.
HTH.

ASP.NET How to retrieve a list of the empty labels on a page?

I have some labels on my Page (e.g. Label1...Label100).
I don't want to loop through all Labels to check if Text property is = "" (or string.Empty, whatever), so this's my question - is it possible to use LINQ or Lambda Expression to get all "empty" labels ?
You can find all the page controls through the Controls property
Page.Controls.OfType<Label>().Where(lbl => lbl.Text == "");
Note that this isn't recursive; i.e. if you have a PlaceHolder which has controls of its own, those will not be returned by Page.Controls.
You can make a "FindAllChildren" extension method that finds all controls recursively from some parent control (which could be the page), and have it return an IEnumerable<Control>. Then use a Linq query on that.
public static IEnumerable<Control> FindAllChildren(this Control control)
{
foreach(Control c in control.Controls)
{
yield return c;
foreach(control child in c.FindAllChildren()
yield return child;
}
}
var allEmptyLabels = parent.FindAllChildren().OfType<Label>()
.Where(l => l.Text == String.Empty);
Is that your label naming convention?
If so, this quick and dirty method could do:
List<Label> labels = new List();
for (int i = 0; i <= 100; i++)
{
var label = (Label)Page.FindControl("Label" + i);
if (label.Text != string.Empty)
labels.Add(label);
}
// use labels collection here
No LINQ, or Lambdas, but it's another perspective for you.

Return ASP DDL or Telerik control

I'm trying to write a more generic method that will populate either an ASP.NET dropdownlist OR a telerik RadComboBox with states. I'd like to pass the control in as a parameter to the method. I have a DataTable that holds all the states, which I loop through (see below) - I'd like to make this applicable to a Telerik RadComboBox - so I need to change the first parameter, and also the part where I Insert a new ListItem - for Telerik RadComboBox it is new RadComboBoxItem. How can I do this?
public void PopulateStates(DropDownList ddlStates, string country)
{
ddlStates.Items.Clear();
DataLookup dl = new DataLookup();
DataTable dt = dl.GetStatesByCountry(country);
if (dt != null)
{
if (dt.Rows.Count > 0)
{
ddlStates.Items.Insert(0, new ListItem(""));
for (int i = 0; i < dt.Rows.Count; i++)
{
ddlStates.Items.Add(new ListItem(dt.Rows[i]["STCD_Descr"].ToString(),
dt.Rows[i]["STCD_State_CD"].ToString()));
}
}
}
}
I looked up the telerik documentation & there doesn't seem to be common way of doing - what you are trying to do.
If it is possible, try using the databinding (setting the DataSource & calling DataBind).
Note: I haven't tried it. But I think that should be supported by both.
Since ListBox and RadComboBox does not have common classes except for the "Control" class you will need to check the actual type.
How about the following code?
public void PopulateStates(Control ddl, string country)
{
object listItem = new object();
switch (ddl.GetType().Name)
{
case "RadComboBox":
listItem = listItem as RadComboBoxItem;
ddl = ddl as RadComboBox;
break;
case "ListBox":
listItem = listItem as ListItem;
ddl = ddl as ListBox;
break;
default:
return;
}
// proceed with your code
}

Resources