Ms MVC at CNUG

Yesterday at the Chicago .Net User Group meeting Nermin Dibek presented on the new ms mvc framework.  Several people were interested in a follow up session.  If you are one of those people and have stumbled onto this blog, or if you are local to Chicago and want to see some more stuff, feel free to ping me or email me and we can try and set something up.

Here is a list of links to get you started with Ms MVC.

MS MVC: Blocks Refactored

In my last post I talked about adding blocks to ms mvc to capture output.  Our solution used an ugly hack with reflection, type caching, and lwg.  Thanks to Jeremy Skinner and Phil who both pointed out you can use a response output filter to do the same thing.  So here is an update to our capturing class.

/// <summary>Renders the action and returns a string.</summary>
/// <param name="viewRenderer">The delegate to render.</param>
/// <returns>The rendered text.</returns>
public string Capture(Action viewRenderer)
{
  IHttpResponse resp = _httpContext.Response;
  Stream originalFilter = null;
  CapturingResponseFilter innerFilter;
  string capturedHtml = "";

  if (viewRenderer != null)
  {
    try
    {
      resp.Flush();
      originalFilter = resp.Filter;
      innerFilter = new CapturingResponseFilter(resp.Filter);
      resp.Filter = innerFilter;
      viewRenderer();
      resp.Flush();
      capturedHtml = innerFilter.GetContents(resp.ContentEncoding);
    }
    finally
    {
      if (originalFilter != null)
      {
        resp.Filter = originalFilter;
      }
    }
  }
  return capturedHtml;
}

The only thing to remember is to flush current response before and after you capture the text.  The CapturingResponseFilter just inherits from Stream and is using a MemoryStream internally.  The call to GetContents just dumps the memory stream to a string using the current encoding of the response.

What do you think… much simpler :)

Thanks Jeremy and Phil.

MS MVC Gets Blocks From Rails

Updated 2008-01-16:

Check out the refactored version which drops the reflection and uses a straight response filter.


Sergio Pereira and I started writing some stories today for Javascript helpers in Mvc Contrib.  During our talk we had to deal with rendering html elements with inner html and javascript blocks ensuring they all got closed but still allowing the ultimate in flexibility for the developer.MvcToolkit goes about this with a using(…) pattern on their FormHelper.  The one disadvantage of this is that the using block will be limited to only a single block.  What happens when you need to render a block for onSuccess and onFailure?

Sergio brought up how powerful ruby blocks are and how cool it would be if we could use lambdas to do something similar.

Sergio proposed being able to do something like this.

<% Ajax.Tag(“div“,  “/controller/action/123“,
new {CssClass=”bigSquare“}, myDiv =>
{ %>
Some Html here
Some helper: <%= Html.Link(“Click me“, “/other/non-ajax/url“) %>
}) %>

What is myDiv?  myDiv is the blocks outer HtmlElement object so inside of your lambda you can manipulate its attributes and perform some other cool stuff we are still cooking up.  While the lambda executes we capture the output and defer rendering to the response until after the lambda is finished.  This allows us to render the HtmlElement with any modifications you made.

So how do we do this, well, we needed a pretty big hack because our delegate is being called from another anonymous delegate which has the HtmlTextWriter as a local variable.  This means that we cannot use IHttpContext.SwitchWriter to capture our output.

Any way to get a fix for this MS?

So in reflector I noticed my anonymous type for my delegate had a public field __w which was the HtmlTextWriter.  Using some reflection I was able to code up my own SwitchWriter which took care of everything.

The helper code is pretty simple.

public static void FromTag(this AjaxHelper helper
  ,string tag
  ,string url
  ,object options,
  Action<Element> innerHtml)
{
  Element element = new Element(tag);
  HtmlTextWriter responseWriter = null;

  if(innerHtml != null)
  {
      try
      {
        using(StringWriter innerWriter = new StringWriter())
        using (HtmlTextWriter innerHtmlWriter =
          new HtmlTextWriter(innerWriter))
        {
          responseWriter = SwitchWriter(innerHtml, innerHtmlWriter);
          innerHtml(element);
          innerHtmlWriter.Flush();
          element.InnerHtml = innerWriter.ToString();
        }
      }
      finally
      {
        if(responseWriter != null)
        {
          SwitchWriter(innerHtml, responseWriter);
        }
      }
  }
  RenderElement(new HtmlTextWriter(
    helper.ViewContext.HttpContext.Response.Output), element);
}

The switch writer is pretty basic, it was tracking it down that took a little bit of time.

public static HtmlTextWriter SwitchWriter(object obj
  , HtmlTextWriter newWriter)
{
  Type actionType = obj.GetType();

  object target = actionType.GetField("_target",
    BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(obj);

  Type targetType = target.GetType();

  HtmlTextWriter response = targetType.GetField("__w")
    .GetValue(target) as HtmlTextWriter;

  targetType.GetField("__w").SetValue(target, newWriter);

  return response;
}

With the above in place I can call it with something like this.

<% Ajax.FromTag("div", "/Home/About",
  new object(),
  myDiv => { myDiv.Id = "Justice"; %><h6>
      <%=Html.ActionLink("Where is Justice?", "About")%>
   </h6>
<% }); %>

And it renders something like this… no we didn’t spike any ajax stuff yet.

<div id="Justice">
 <h6><a href="/Home/About">Where is Justice?</a></h6>
</div>

You may wonder why we do not return a string from our helper.  Asp.Net currently can’t handle that.  It thinks its a block and not a simple expression so using the regular <%= %> will not work.  Instead of having you call <%Response.Write(…);%> we thought it would be easier if the helper just rendered it for you.

This got me excited about some the cool things we can really start to do.  I like getting an instance of Element to set properties on, this can help reduce the abuse that anonymous types are taking in mvc right now.  It would be great if there could be patch so we do not have to resort to reflection to switch writers, but I quote Sergio

how do you feel about that __w hack? For me it looks prettier by the minute

Let me know what you think,

Cheers