Acess controls on ContentPages via Javascript by MasterPage - c#

I need my MasterPage to be able to get ControlIDs of Controls on ContentPages, but I cannot
use <%= xxx.CLIENTID%> as it would return an error as the control(s) might not be loaded by the contentplaceholder.
Some controls have a so called BehaviourID, which is exactly what I would need as they can be directly accessed with the ID:
[Asp.net does always create unique IDs, thus modifies the ID I entered]
Unfortunately I need to access
e.g. ASP.NET Control with BehaviouraID="test"
....
document.getElementById("test")
if I were to use e.g. Label control with ID="asd"
....
document.getElementById('<%= asd.ClientID%>')
But if the Labelcontrol isn't present on the contentpage, I of course get an error on my masterpage.
I need a solution based on javascript. (server-side)
Thx :-)

You could use jQuery and access the controls via another attribute other than the ID of the control. e.g.
<asp:Label id="Label1" runat="server" bid="test" />
$('span[bid=test]')
The jQuery selector, will select the span tag with bid="test". (Label renders as span).

Best solution so far:
var HiddenButtonID = '<%= MainContent.FindControl("btnLoadGridview")!=null?
MainContent.FindControl("btnLoadGridview").ClientID:"" %>';
if (HiddenButtonID != "") {
var HiddenButton = document.getElementById(HiddenButtonID);
HiddenButton.click();
}
Where MainContent is the contentplace holder.
By http://forums.asp.net/members/sansan.aspx

You could write an json-object with all the control-ids which are present on the content-page and "register" that object in the global-scope of your page.
Some pseudo pseudo-code, because I can't test it at the moment...
void Page_Load(object sender,EventArgs e) {
System.Text.StringBuilder clientIDs = new System.Text.StringBuilder();
IEnumerator myEnumerator = Controls.GetEnumerator();
while(myEnumerator.MoveNext()) {
Control myControl = (Control) myEnumerator.Current;
clientIDs.AppendFormat("\t\"{0}\" : \"{1}\",\n", myControl.ID, myControl.ClientID);
}
page.ClientScript.RegisterStartupScript(page.GetType(),
"ClientId",
"window.ClientIDs = {" + clientIDs.ToString().Substring(0, clientIDs.ToString().Length - 2) + "};",
true);
}

It sounds like your issue is that you are using the master page for something it wasn't intended. The master page is a control just like any other control, and therefore cannot access any of the controls of its parent (the page). More info:
ASP.Net 2.0 - Master Pages: Tips, Tricks, and Traps
My suggestion is to inject the JavaScript from your page where the controls can actually be resolved. Here is a sample of how this can be done:
#Region " LoadJavaScript "
Private Sub LoadJavaScript()
Dim sb As New StringBuilder
'Build the JavaScript here...
sb.AppendFormat(" ctl = getObjectById('{0});", Me.asd.ClientID)
sb.AppendLine(" ctl.className = 'MyClass';")
'This line adds the javascript to the page including the script tags.
Page.ClientScript.RegisterClientScriptBlock(Me.GetType, "MyName", sb.ToString, True)
'Alternatively, you can add the code directly to the header, but
'you will need to add your own script tags to the StringBuilder before
'running this line. This works even if the header is in a Master Page.
'Page.Header.Controls.Add(New LiteralControl(sb.ToString))
End Sub
#End Region

Related

Dynamically add div in .aspx page

I'm looking for a JavaScript-free way to dynamically add div elements inside a certain element of an aspx web page according to data passed to i.
I'm looking for something similar to razor c# where you can even create loops and write c# code that directly effects the content of the page.
It appears that razor c# doesn't work unless the page is a .cshtml page, so I'm kinda lost here since I don't want to use JavaScript.
Is there a better approach to it?
Say, below is the placeholder div
<div id = "maindiv">
<asp:PlaceHolder ID ="maindivs" runat="server">
</asp:PlaceHolder>
</div>
Then you may add divs to your placeholder div like below
protected void addmainDiv(int m)
{
for(int i = 0; i< m; i++)
{
System.Web.UI.HtmlControls.HtmlGenericControl newdivs = new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
newdivs.Attributes.Add("class", "maindivs");
newdivs.ID = "createDiv";
newdivs.InnerHtml = " I'm a div, from code behind ";
maindivs.Controls.Add(newdivs);
}
}
If you want to create HTML dynamically in .aspx then you can create your html code in your .cs (backend file)
string htmlAsStringToShow = "<div>" + variable + "</div>";
Then show its content in your .aspx page like this :
<%= htmlAsStringToShow %>

Creating and finding dynamic controls on a page

There is a problem to find a dynamic control on a page. The dynamic control is created every time when a user press a button. The button calls the following JavaScript function and create a new components.
<script type="text/javascript">
var uploadCount = 1;
function addFileInput(fName) {
var only_file_name = fName.replace(/^.*[\\\/]/, '');
var $div = $('<div />', {runat: 'server'});
var $cbox = $('<input />', { type: 'checkbox', id: 'attachement' + uploadCount, value: fName, checked: "true", runat: 'server'}).addClass;
var $label = $('<label />', { 'for': 'attachement' + uploadCount, text: only_file_name });
$div.append($cbox);
$div.append($label);
$('#newAttachment').append($div);
$("#uploadCountValue").prop("value", uploadCount);
uploadCount++;
}
</script>
newAttachment is DIV section on the page.
<div id="newAttachement" runat="server" />
The DIV section is situated inside section. The problem is when a user presses the button on the form I can't find the dynamic created components. The following code shows how I try to find the components:
for (int i = 1; i <= Convert.ToInt32(uploadCountValue.Value); i++)
{
if (RecursiveFind(newAttachement, "attachement" + i) != null)
{
... to do something
}
}
public Control RecursiveFind(Control ParentCntl, string NameToSearch)
{
if (ParentCntl.ID == NameToSearch)
return ParentCntl;
foreach (Control ChildCntl in ParentCntl.Controls)
{
Control ResultCntl = RecursiveFind(ChildCntl, NameToSearch);
if (ResultCntl != null)
return ResultCntl;
}
return null;
}
I have detected that Controls count value is always zero in spite of there are dynamic components there.
I would be happy to get any help from us. Thanks.
to find the controls created in the client-end you can't search them in the Page.Controls collection instead try to look for them in the Request.Form[] array
you are creating the dynamic controls in javascript? i.e. you are creating html elements in javascript. It won't matter even if you put a runat="server" attribute in there, because it is still at the client-end. That would not be a part of the viewstate bag, so not populated in the controls collection.
you need to change your logic. create dynamic control in code-behind on button postback.

Self closing Html Generic Control?

I am writing a bit of code to add a link tag to the head tag in the code behind... i.e.
HtmlGenericControl css = new HtmlGenericControl("link");
css.Attributes["rel"] = "Stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["href"] = String.Format("/Assets/CSS/{0}", cssFile);
to try and achieve something like...
<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css" />
I am using the HtmlGenericControl to achieve this... the issue I am having is that the control ultimatly gets rendered as...
<link rel="Stylesheet" type="text/css" href="/CSS/Blah.css"></link>
I cant seem to find what I am missing to not render the additional </link>, I assumed it should be a property on the object.
Am I missing something or is this just not possible with this control?
I think you'd have to derive from HtmlGenericControl, and override the Render method.
You'll then be able to write out the "/>" yourself (or you can use HtmlTextWriter's SelfClosingTagEnd constant).
Edit: Here's an example (in VB)
While trying to write a workaround for umbraco.library:RegisterStyleSheetFile(string key, string url) I ended up with the same question as the OP and found the following.
According to the specs, the link tag is a void element. It cannot have any content, but can be self closing. The W3C validator did not validate <link></link> as correct html5.
Apparently
HtmlGenericControl css = new HtmlGenericControl("link");
is rendered by default as <link></link>. Using the specific control for the link tag solved my problem:
HtmlLink css = new HtmlLink();
It produces the mark-up <link/> which was validated as correct xhtml and html5.
In addition to link, System.Web.UI.HtmlControls contains classes for other void element controls, such as img, input and meta.
Alternatively you can use Page.ParseControl(string), which gives you a control with the same contents as the string you pass.
I'm actually doing this exact same thing in my current project. Of course it requires a reference to the current page, (the handler), but that shouldn't pose any problems.
The only caveat in this method, as I see it, is that you don't get any "OO"-approach for creating your control (eg. control.Attributes.Add("href", theValue") etc.)
I just created a solution for this, based on Ragaraths comments in another forum:
http://forums.asp.net/p/1537143/3737667.aspx
Override the HtmlGenericControl with this
protected override void Render(HtmlTextWriter writer)
{
if (this.Controls.Count > 0)
base.Render(writer); // render in normal way
else
{
writer.Write(HtmlTextWriter.TagLeftChar + this.TagName); // render opening tag
Attributes.Render(writer); // Add the attributes.
writer.Write(HtmlTextWriter.SelfClosingTagEnd); // render closing tag
}
writer.Write(Environment.NewLine); // make it one per line
}
The slightly hacky way.
Put the control inside a PlaceHolder element.
In the code behind hijack the render method of the PlaceHolder.
Render the PlaceHolders content exactly as you wish.
This is page / control specific and does not require any overrides. So it has minimal impact on the rest of your system.
<asp:PlaceHolder ID="myPlaceHolder" runat="server">
<hr id="someElement" runat="server" />
</asp:PlaceHolder>
protected void Page_Init(object sender, EventArgs e)
{
myPlaceHolder.SetRenderMethodDelegate(ClosingRenderMethod);
}
protected void ClosingRenderMethod(HtmlTextWriter output, Control container)
{
var voidTags = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "br", "hr", "link", "img" };
foreach (Control child in container.Controls)
{
var generic = child as HtmlGenericControl;
if (generic != null && voidTags.Contains(generic.TagName))
{
output.WriteBeginTag(generic.TagName);
output.WriteAttribute("id", generic.ClientID);
generic.Attributes.Render(output);
output.Write(HtmlTextWriter.SelfClosingTagEnd);
}
else
{
child.RenderControl(output);
}
}
}

Can't get ScriptManager.RegisterStartupScript in WebControl nested in UpdatePanel to work

I am having what I believe should be a fairly simple problem, but for the life of me I cannot see my problem. The problem is related to ScriptManager.RegisterStartupScript, something I have used many times before.
The scenario I have is that I have a custom web control that has been inserted into a page. The control (and one or two others) are nested inside an UpdatePanel. They are inserted onto the page onto a PlaceHolder:
<asp:UpdatePanel ID="pnlAjax" runat="server">
<ContentTemplate>
<asp:PlaceHolder ID="placeholder" runat="server">
</asp:PlaceHolder>
...
protected override void OnInit(EventArgs e){
placeholder.Controls.Add(Factory.CreateControl());
base.OnInit(e);
}
This is the only update panel on the page.
The control requires some initial javascript be run for it to work correctly. The control calls:
ScriptManager.RegisterStartupScript(this, GetType(),
Guid.NewGuid().ToString(), script, true);
and I have also tried:
ScriptManager.RegisterStartupScript(Page, Page.GetType(),
Guid.NewGuid().ToString(), script, true);
The problem is that the script runs correctly when the page is first displayed, but does not re-run after a partial postback. I have tried the following:
Calling RegisterStartupScript from CreateChildControls
Calling RegisterStartupScript from OnLoad / OnPreRender
Using different combinations of parameters for the first two parameters (in the example above the Control is Page and Type is GetType(), but I have tried using the control itself, etc).
I have tried using persistent and new ids (not that I believe this should have a major impact either way).
I have used a few breakpoints and so have verified that the Register line is being called correctly.
The only thing I have not tried is using the UpdatePanel itself as the Control and Type, as I do not believe the control should be aware of the update panel (and in any case there does not seem to be a good way of getting the update panel?).
Can anyone see what I might be doing wrong in the above?
Thanks :)
Well, to answer the query above - it does appear as if the placeholder somehow messes up the ScriptManager.RegisterStartupScript.
When I pull the control out of the placeholder and code it directly onto the page the Register script works correctly (I am also using the control itself as a parameter).
ScriptManager.RegisterStartupScript(this, GetType(), Guid.NewGuid().ToString(), script, true);
Can anyone throw any light on why an injected control onto a PlaceHolder would prevent the ScriptManager from correctly registering the script? I am guessing this might have something to do with the lifecycle of dynamic controls, but would appreciate (for my own knowledge) if there is a correct process for the above.
I had an issue using this in a user control (in a page this worked fine); the Button1 is inside an updatepanel, and the scriptmanager is on the usercontrol.
protected void Button1_Click(object sender, EventArgs e)
{
string scriptstring = "alert('Welcome');";
ScriptManager.RegisterStartupScript(this, this.GetType(), "alertscript", scriptstring, true);
}
Now it seems you have to be careful with the first two arguments, they need to reference your page, not your control
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "alertscript", scriptstring, true);
I think you should indeed be using the Control overload of the RegisterStartupScript.
I've tried the following code in a server control:
[ToolboxData("<{0}:AlertControl runat=server></{0}:AlertControl>")]
public class AlertControl : Control{
protected override void OnInit(EventArgs e){
base.OnInit(e);
string script = "alert(\"Hello!\");";
ScriptManager.RegisterStartupScript(this, GetType(),
"ServerControlScript", script, true);
}
}
Then in my page I have:
protected override void OnInit(EventArgs e){
base.OnInit(e);
Placeholder1.Controls.Add(new AlertControl());
}
Where Placeholder1 is a placeholder in an update panel. The placeholder has a couple of other controls on in it, including buttons.
This behaved exactly as you would expect, I got an alert saying "Hello" every time I loaded the page or caused the update panel to update.
The other thing you could look at is to hook into some of the page lifecycle events that are fired during an update panel request:
Sys.WebForms.PageRequestManager.getInstance()
.add_endRequest(EndRequestHandler);
The PageRequestManager endRequestHandler event fires every time an update panel completes its update - this would allow you to call a method to set up your control.
My only other questions are:
What is your script actually doing?
Presumably you can see the script in the HTML at the bottom of the page (just before the closing </form> tag)?
Have you tried putting a few "alert("Here");" calls in your startup script to see if it's being called correctly?
Have you tried Firefox and Firebug - is that reporting any script errors?
When you call ScriptManager.RegisterStartupScript, the "Control" parameter must be a control that is within an UpdatePanel that will be updated. You need to change it to:
ScriptManager.RegisterStartupScript(this, this.GetType(), Guid.NewGuid().ToString(), script, true);
The solution is to put the scripts in an outside js file (lets called 'yourDynamic.js') and re-register de file everytime you refresh the updatepanel.
I use this in the updatepanel_prerender event:
ScriptManager.RegisterClientScriptBlock(UpdatePanel1, UpdatePanel1.GetType(), "UpdatePanel1_PreRender", _
"<script type='text/javascript' id='UpdatePanel1_PreRender'>" & _
"include('yourDynamic.js');" & _
"removeDuplicatedScript('UpdatePanel1_PreRender');</script>" _
, False)
In the page or in some other include you will need this javascript:
// Include a javascript file inside another one.
function include(filename)
{
var head = document.getElementsByTagName('head')[0];
var scripts = document.getElementsByTagName('script');
for(var x=0;x<scripts.length;> {
if (scripts[x].getAttribute('src'))
{
if(scripts[x].getAttribute('src').indexOf(filename) != -1)
{
head.removeChild(scripts[x]);
break;
}
}
}
script = document.createElement('script');
script.src = filename;
script.type = 'text/javascript';
head.appendChild(script)
}
// Removes duplicated scripts.
function removeDuplicatedScript(id)
{
var count = 0;
var head = document.getElementsByTagName('head')[0];
var scripts = document.getElementsByTagName('script');
var firstScript;
for(var x=0;x<scripts.length;> {
if (scripts[x].getAttribute('id'))
{
if(scripts[x].getAttribute('id').indexOf(id) != -1)
{
if (count == 0)
{
firstScript = scripts[x];
count++;
}
else
{
head.removeChild(firstScript);
firstScript = scripts[x];
count = 1;
}
}
}
}
clearAjaxNetJunk();
}
// Evoids the update panel auto generated scripts to grow to inifity. X-(
function clearAjaxNetJunk()
{
var knowJunk = 'Sys.Application.add_init(function() {';
var count = 0;
var head = document.getElementsByTagName('head')[0];
var scripts = document.getElementsByTagName('script');
var firstScript;
for(var x=0;x<scripts.length;> {
if (scripts[x].textContent)
{
if(scripts[x].textContent.indexOf(knowJunk) != -1)
{
if (count == 0)
{
firstScript = scripts[x];
count++;
}
else
{
head.removeChild(firstScript);
firstScript = scripts[x];
count = 1;
}
}
}
}
}
Pretty cool, ah...jejeje
This part of what i posted some time ago here.
Hope this help... :)
I had an issue with Page.ClientScript.RegisterStartUpScript - I wasn't using an update panel, but the control was cached. This meant that I had to insert the script into a Literal (or could use a PlaceHolder) so when rendered from the cache the script is included.
A similar solution might work for you.
DO NOT Use GUID For Key
ScriptManager.RegisterClientScriptBlock(this.Page, typeof(UpdatePanel)
Guid.NewGuid().ToString(), myScript, true);
and if you want to do that , call Something Like this function
public static string GetGuidClear(string x)
{
return x.Replace("-", "").Replace("0", "").Replace("1", "")
.Replace("2", "").Replace("3", "").Replace("4", "")
.Replace("5", "").Replace("6", "").Replace("7", "")
.Replace("8", "").Replace("9", "");
}
What worked for me, is registering it on the Page while specifying the type as that of the UpdatePanel, like so:
ScriptManager.RegisterClientScriptBlock(this.Page, typeof(UpdatePanel) Guid.NewGuid().ToString(), myScript, true);
Sometimes it doesnt fire when the script has some syntax error, make sure the script and javascript syntax is correct.
ScriptManager.RegisterStartupScript(this, this.GetType(), Guid.NewGuid().ToString(),script, true );
The "true" param value at the end of the ScriptManager.RegisterStartupScript will add a JavaScript tag inside your page:
<script language='javascript' defer='defer'>your script</script >
If the value will be "false" it will inject only the script witout the --script-- tag.
I try many things and finally found that the last parameter must be false and you must add <SCRIPT> tag to the java script :
string script = "< SCRIPT >alert('hello!');< /SCRIPT>";
ScriptManager.RegisterClientScriptBlock(Page, Page.GetType(), key, script, **false**);

ASP.NET POST Problem

I'm new to ASP.NET and have a form tag on an ascx user control. I'm unable to submit the form from javascript because I found out that this form is nested inside a form called 'aspnetForm'. If I just want to make a post to a cgi, how can I accomplish this?
Remove the <form runat='server'> if you don't need it and just use your own form: <form action="page.cgi" method="post">. You'll not be able to use some server controls. Use their HTML equivalents instead.
If you don't have control on the page, you can use javascript to inject a new form on the page with some hidden fields and set the values upon click of a button.
Something like this:
var myForm = document.createElement("form");
myForm.attributes["action"] = "mycgi.cgi";
myForm.attributes["method"] = "POST";
var myhiddenfield = document.createElement("input");
myhiddenfield.attributes["type"] = "hidden";
myhiddenfield.attributes["name"] = "name"
document.body.appendChild(myForm);
myForm.appendChild(myhiddenfield);
function onFormButtonClick() { // set as onclick on a <button>
myhiddenfield.value = ... //value read from a textbox or something.
...
myForm.submit();
}

Categories