ASP.NET AJAX: ScriptControl change from Beta2 to RC – ScriptManager.RegisterScriptDescriptors()


There has been some confusion about a change made to ScriptControls between
the ASP.NET AJAX Beta2 and RC.  In Beta2, the only requirement for controls implementing
IScriptControl or IExtenderControl was to call
ScriptManager.RegisterScriptControl() or ScriptManager.RegisterExtenderControl()
from their PreRender() method.  In RC, there is an additional requirement to
call ScriptManager.RegisterScriptDescriptors() from the Render() method.

Beta2
public class MyScriptControl : Control, IScriptControl {
   
protected override void OnPreRender(EventArgs e) {
       
base.OnPreRender(e);
       
ScriptManager.GetCurrent(Page).RegisterScriptControl(this);
    }
}

RC
public
class MyScriptControl : Control, IScriptControl {
   
protected override void OnPreRender(EventArgs e) {
       
base.OnPreRender(e);
       
ScriptManager.GetCurrent(Page).RegisterScriptControl(this);
    }

   
protected override void Render(HtmlTextWriter writer) {
       
base.Render(writer);
       
ScriptManager.GetCurrent(Page).RegisterScriptDescriptors(this);
    }
}

In the RC, we added this to the ExtenderControl base class, so this change
doesn’t affect controls deriving from ExtenderControl.  It only affects controls
directly implementing IScriptControl and IExtenderControl.  This change
is mentioned several times in the ASP.NET AJAX CTP to RC
whitepaper
 (search for “RegisterScriptDescriptors”).  The rest of
this post explains the rationale behind this change.

We needed to make this change because there are some scenarios where a
control’s PreRender() method is called but its Render() method is not.  One
example is a control inside a closed WebPart.  In Beta2, ScriptControls did not
work inside closed WebParts.  The ScriptDescriptors would be registered, but the
control would never render any HTML, which causes a JavaScript error when we try
to find the associated HTML element.

To fix this issue, we needed to somehow verify that a ScriptControl was
rendered before registering its ScriptDescriptors.  Unfortunately, there is no
way to externally determine whether a control has been rendered, so the only
solution was to require the ScriptControl to call into the ScriptManager from
its Render() method.  We realize this extra call to RegisterScriptDescriptors()
is annoying, but it was the only to fix the bug, and the change only affects
controls directly implementing IScriptControl or IExtenderControl.  As a result,
we added the ScriptControl base class in RC, which makes it easier to write a
basic ScriptControl without directly implementing the IScriptControl
interface.

The new model is also more correct from a purist’s standpoint.  A control’s
script library is registered via its ScriptReferences, and it is safe to
register these even if the control is never rendered.  To maximize flexibility
in rendering these script libraries, the ScriptReferences must be registered
during the PreRender phase.  A control’s instance script is registered via its
ScriptDescriptors, and it is not safe to register these if the control is never
rendered.  So, the instance script must be registered during the control’s
Render phase.