Sunday, June 10, 2012

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:



  1. Calling RegisterStartupScript from CreateChildControls

  2. Calling RegisterStartupScript from OnLoad / OnPreRender

  3. 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).

  4. I have tried using persistent and new ids (not that I believe this should have a major impact either way).

  5. 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.


Source: Tips4all

13 comments:

  1. 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?

    ReplyDelete
  2. 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);

    ReplyDelete
  3. 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);

    ReplyDelete
  4. using the below mentioned code in ascx.cs page helped me too :)

    ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "focuser", "script", true);

    Thanks Guys and gurlzz

    ReplyDelete
  5. I have a UserControl within a UserControl. Trying to reference a control that will get updated in an UpdatePanel did not work for me.

    Referencing the Page within the ScriptManager.RegisterStartupScript worked immediately! :D

    I would vote you dc2009, but I don't have enough reputation yet here on StackOverflow...

    ReplyDelete
  6. 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... :)

    ReplyDelete
  7. 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.

    ReplyDelete
  8. 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);

    ReplyDelete
  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);

    ReplyDelete
  10. Sometimes it doesnt fire when the script has some syntax error, make sure the script and javascript syntax is correct.

    ReplyDelete
  11. If interested good topic with common error we (developers) make, the last param should be set to false .
    Bellow is a good explanation
    http://www.dotnetcurry.com/ShowArticle.aspx?ID=200&AspxAutoDetectCookieSupport=1

    ReplyDelete
  12. 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 funtion

    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", "");
    }

    ReplyDelete
  13. 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.

    ReplyDelete