|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionAnyone who has spent time developing URL rewriters will know that these do not always play nicely with AJAX components. This is because the BackgroundIn order to preserve the rewritten URL on post-back (and fix AJAX components), the Without this fix, you may experience the following error when using Asp.Net Ajax. This happens when your virtual URL is in a different directory than the real path. Sys.WebForms.PageRequestManagerServerErrorException:
An Unknown error occurred while processing the request on the server.
The standard approach might be to extend the A better solution would be to use a control adapter. This can be added via configuration settings in order to change the rendered HTML of all For anyone new to control adapters, these are discreet classes extended from the OK, so sounds like a good solution? Not yet! Imagine we are developing a component to be used by a third party that requires a control adapter. After they have plugged in our component, how do we ensure they add the control adapter to the solution? The standard approach is to provide heaps of documentation, and if it breaks... well... it's user error. A smarter solution is to set the control adapter programmatically from within our component. In this way, the component is self-contained. It will be attached, when required, automatically, wherever and whenever our component is used. The bad news is, this ability isn't supported in the current framework. The good news is, it can be done, and here's how. SolutionFirstly, we need a control adapter to correct the public class HtmlFormAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
base.Render(new HtmlFormWriter(writer));
}
private class HtmlFormWriter : HtmlTextWriter
{
public HtmlFormWriter(HtmlTextWriter writer)
: base(writer)
{
this.InnerWriter = writer.InnerWriter;
}
public HtmlFormWriter(TextWriter writer)
: base(writer)
{
this.InnerWriter = writer;
}
public override void WriteAttribute(string key, string value, bool fEncode)
{
if (string.Compare(key, "action")==0)
{
value = HttpContext.Current.Request.RawUrl;
}
base.WriteAttribute(key, value, fEncode);
}
}
}
The next step is to set the control adapter programmatically. In order to do this, we use Reflection to access the hidden private void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (context.Handler is Page)
{
Page page = (Page)context.Handler;
page.PreRender += new EventHandler(RegisterControlAdapters);
}
}
private void RegisterControlAdapters(object sender, EventArgs e)
{
Page page = (Page)sender;
page.PreRender -= new EventHandler(RegisterControlAdapters);
if (page.Form != null)
{
// attach new instance of control adapter
FieldInfo adapterFieldInfo = page.Form.GetType().GetField("_adapter",
BindingFlags.NonPublic | BindingFlags.Instance);
if (adapterFieldInfo != null)
{
HtmlFormAdapter adapter = new HtmlFormAdapter();
FieldInfo controlFieldInfo = adapter.GetType().GetField("_control",
BindingFlags.NonPublic | BindingFlags.Instance);
if (controlFieldInfo != null)
{
controlFieldInfo.SetValue(adapter, page.Form);
adapterFieldInfo.SetValue(page.Form, adapter);
}
}
}
}
SummaryThis is a pretty specific example; however, the point is that using this technique greatly aids the creation of smart components that require minimal configuration. It also provides a greater degree of power to component developers, who are then able to hook into the actual rendering of all the controls in an application from a single line in the web.config. <add name="RewriteModule" type="CodeKing.Web.UrlRewriter, CodeKing.Web"/>
This leads to more pluggable components and fewer problems when it comes to development cycles or swapping out of components. Further Reading
History
|
||||||||||||||||||||||