A known bug in ASP.NET 1.x and 2.0 is that the action attribute of a form doesn’t respect URL rewrites. Everyone that uses URL rewrites uses one of several mechanisms to take care of the action attribute bug.

Recently it has been quite popular to use control adapters and .browser files to change the rendering of a form and its attributes. I’ve never liked that approach because it treats the symptom instead of fixing the problem in a transparent and clean way.

<strongOpinion>
The use of control adapters and .browser files to alter the output for different clients is just as bad as having separate stylesheet files for IE and Firefox. Instead of writing cross-browser mark-up and CSS you treat the browser differences as an illness and try to treat it. The thing is that you don’t cure it by using control adapters; you just treat the symptom of not being able to (or don’t care to) write cross-browser code.

However, it still has its right when dealing with mobile devices, but not much longer. The browser in the iPhone or Windows Mobile is becoming so good that it soon doesn’t matter anymore.
</strongOpinion>

In the past I’ve used a custom overridden HtmlForm but when I had to do it recently on my job, I thought it was about time to make it better and cleaner. I very much like the use of a custom form control approach because it is very transparent in what it does and where it does it.

The code

[code:c#]

#region Using

using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;

#endregion

/// <summary>
/// A custom HtmlForm for use when a postback is made to a website
/// that has URL rewriting enabled.
/// </summary>
[DefaultProperty("Text")]
[ToolboxData("<{0}:RewriteForm runat=server></{0}:RewriteForm>")]
public class RewriteForm : HtmlForm
{
  /// <summary>
  /// Renders the attributes using the RewriteFormHtmlTextWriter.
  /// </summary>
  protected override void RenderAttributes(HtmlTextWriter writer)
  {
    RewriteFormHtmlTextWriter custom = new RewriteFormHtmlTextWriter(writer);
    base.RenderAttributes(custom);
  }

  /// <summary>
  /// Writes the HtmlForm's markup to support URL rewrites.
  /// </summary>
  private class RewriteFormHtmlTextWriter : HtmlTextWriter
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="RewriteFormHtmlTextWriter"/> class.
    /// </summary>
    /// <param name="writer">The writer.</param>
    public RewriteFormHtmlTextWriter(HtmlTextWriter writer)
      : base(writer)
    {
      base.InnerWriter = writer.InnerWriter;
    }

    /// <summary>
    /// Writes all attributes the normal way, but writes the action attribute differently.
    /// </summary>
    /// <param name="name">The markup attribute to write to the output stream.</param>
    /// <param name="value">The value assigned to the attribute.</param>
    /// <param name="fEncode">true to encode the attribute and its assigned value; otherwise, false.</param>
    public override void WriteAttribute(string name, string value, bool fEncode)
    {
      if (name == "action")
      {
        HttpContext context = HttpContext.Current;

        if (context.Items["ActionAlreadyWritten"] == null)
        {
          value = context.Request.RawUrl;
          context.Items["ActionAlreadyWritten"] = true;
        }
      }

      base.WriteAttribute(name, value, fEncode);
    }
  }
}

[/code]

Download

RewriteForm.zip (320 bytes)

Today I had what seemed to be an easy task of serving the rendered HTML of a user control through an Httphandler (.ashx). It’s something I’ve done many times before but never from an HttpHandler. If you want to do it from an .aspx page it is very easy. You just reference the user control and then the code-behind can access it in a strongly typed way when calling LoadControl("control.ascx").

An HttpHandler however does not have a LoadControl method and it doesn’t have a way of referencing user controls like a page does. What started as an easy task now turned into something unknown – reflection was needed.

The code

So, this is the method I came up with using reflection.

[code:c#]

System.Web.UI.Page page = new System.Web.UI.Page(); 
UserControl profile = (UserControl)page.LoadControl("~/controls/profile.ascx");

Type type = profile.GetType();
type.GetProperty("Name").SetValue(profile, "John Smith", null);
type.GetProperty("Age").SetValue(profile, 53, null);

[/code]

Notice that I use a fully qualified relative path to load the profile.ascx control. That is because if you pre-compile the web application it would fail otherwise. I don’t know if it’s a bug or not, but this is the way around it.

I don’t think the solution is very pretty, but it’s not ugly either. It’s short and precise and the reflection part is very transparent. No matter what, it solved the problem of loading user controls from an HttpHandler.