Introduction
This control solves the problem when you need inline client scripts within an ASP.NET UpdatePanel
to work. Inline scripts, to my knowledge, are not registered to the Script Manager when the "AJAX call" is made. The problem occurs if you have a UserControl
, containing an inline script that is dynamically added into an UpdatePanel
after an AJAX postback.
Background
If you, like me, already have (had) an existing Web application that's cluttered with inline scripts, and want to take advantage of the features that ASP.NET AJAX provides, you might find yourself with a tedious refactoring job. I managed to solve 99% (1% was invalid markup) of all my inline script problems by using this control.
Hopefully, it can do the same for you!
Using the Code
The control inherits from the original UpdatePanel
and uses a Regex
to parse for client scripts. You use it just like the original UpdatePanel
, and can switch on/off the parsing for inline scripts by setting the property RegisterInlineClientScripts
to false
.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Text.RegularExpressions;
using System.IO;
namespace Tuna.Web.Controls
{
public class TunaUpdatePanel : UpdatePanel
{
private static readonly Regex REGEX_CLIENTSCRIPTS = new Regex(
"<script\\s((?<aname>[-\\w]+)=[\"'](?<avalue>.*?)[\"']\\s?)*\\s*>(?<script>.*?)</script>",
RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled |
RegexOptions.ExplicitCapture);
private bool m_RegisterInlineClientScripts = true;
public bool RegisterInlineClientScripts
{
get
{
return this.m_RegisterInlineClientScripts;
}
set
{
this.m_RegisterInlineClientScripts = value;
}
}
protected virtual string AppendInlineClientScripts(string htmlsource)
{
if (this.ContentTemplate != null && htmlsource.IndexOf(
"<script", StringComparison.CurrentCultureIgnoreCase) > -1)
{
MatchCollection matches = REGEX_CLIENTSCRIPTS.Matches(htmlsource);
if (matches.Count > 0)
{
for (int i = 0; i < matches.Count; i++)
{
string script = matches[i].Groups["script"].Value;
string scriptID = script.GetHashCode().ToString();
string scriptSrc = "";
CaptureCollection aname = matches[i].Groups["aname"].Captures;
CaptureCollection avalue = matches[i].Groups["avalue"].Captures;
for (int u = 0; u < aname.Count; u++)
{
if (aname[u].Value.IndexOf("src",
StringComparison.CurrentCultureIgnoreCase) == 0)
{
scriptSrc = avalue[u].Value;
break;
}
}
if (scriptSrc.Length > 0)
{
ScriptManager.RegisterClientScriptInclude(this,
this.GetType(), scriptID, scriptSrc);
}
else
{
ScriptManager.RegisterClientScriptBlock(this, this.GetType(),
scriptID, script, true);
}
htmlsource = htmlsource.Replace(matches[i].Value, "");
}
}
}
return htmlsource;
}
protected override void RenderChildren(HtmlTextWriter writer)
{
ScriptManager sm = ScriptManager.GetCurrent(Page);
if (this.RegisterInlineClientScripts && sm != null && sm.IsInAsyncPostBack)
{
using (HtmlTextWriter htmlwriter = new HtmlTextWriter(new StringWriter()))
{
base.RenderChildren(htmlwriter);
string html;
int outputSize;
html = htmlwriter.InnerWriter.ToString();
outputSize = html.Length;
html = this.AppendInlineClientScripts(html);
outputSize -= html.Length;
if (outputSize > 0)
{
html = this.SetOutputContentSize(html, outputSize);
}
writer.Write(html);
}
}
else
{
base.RenderChildren(writer);
}
}
private string SetOutputContentSize(string html, int difference)
{
string[] split = html.Split('|');
int size = int.Parse(split[0]);
split[0] = (size - difference).ToString();
return string.Join("|", split);
}
}
}
Optimization suggestions are welcome!
Enjoy!
Thanks to Rogic for solving the issue I hade with replacing the response context to opitmize traffic!