Working with pagemethods and jQuery

On a project I worked with recently we needed a preview of the number of search hits on the page before we submitted the search form. The form included a large amount of search properties so this made it easier for the users to see if there will be any hits or not, also faster than hitting search and waiting for the results to load. So the first part is the page method, a static method with the WebMethod and ScriptMethod attributes. This way the method will available ajax calls. The reason I choose to use a page method and not a web service (asmx or wcf) is that I only needed this on one page and having an web service felt like overkill. In this example the method takes a string as input (named ids) and returns 2 integers using a tuple.
public partial class Search : Page
{
  [WebMethod]
  [ScriptMethod]
  public static Tuple<int,int> SearchHits(string ids)
  {
    return new Tuple<int,int>(1,0);
  }
}
Second step is to call the method with jQuery using the Ajax function. The URL in the call should be to the page and then with appended slash and the name of the method you want to call. In this example I've set the timeout to 10000 since the default timeout is 1000ms and my development machine was to slow so I ended up with a lot of failed requests. The function for success receives an object from the server that has one property called d this is due to some XSS protection added by asp.net, the tuple class has 2 properties Item1 and Item2 so to access one of the I use msg.d.Item1. The json syntax for the data field is quite simple, just a sequence of name:value quoted unless it's numbers or booleans.
$.ajax({
        type: 'POST',
        url: '/Search.aspx/SearchHits',
        data: "{'ids':'" + ids +"}",
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        timeout: 10000,
        success: function(msg) {
            handleSearchResult(msg.d.Item1, msg.d.Item2);
        },
        error:function(xhr, status, error) {
        }
      });
One last thing™ to change if you intend to run this on IIS 7+ is to add the following to your web.config. If it's not present then the ajax request returns the whole page instead of what you would expect.
<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="ScriptModule"/>
      <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <remove name="ScriptHandlerFactory"/>
      <remove name="ScriptHandlerFactoryAppServices"/>
      <remove name="ScriptResource"/>
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    </handlers>
</system.webServer>

My Multiselect fork

Forked my first repository on github today, a jQuery widget called Multiselect. It was only a small change, added a change event to the widget to be able to subscribe to selection changes. Why ? needed it to do nice ajax stuff. Haven't decided yet if it's a change worth sending back, might need to test it some first to see if it's stable enough. My version can be found here.

Building PDFs in Asp.Net MVC 2

To build a PDF I use iTextSharp, it supports most things with a bit of tweaking, the code below creates a 1 page pdf with 3 fields: header, body and footer. Some versions (not 5.0 I think) has support for headers and footers however only with a limited number of rows in each field. This way it's possible to have a larger header and/or footer. I set the MinimumHeight on the body cell, that way my footer will be in the bottom of the page. The exact size use depends on the size of the content in the other fields. Also the method below uses a MemoryStream to output the PDF that way I can get the result without doing any disk i/o.
public byte[] GetPdf(ViewModel model)
{
  MemoryStream ms = new MemoryStream();
  Document doc = new Document(PageSize.A4);
  PdfWriter writer = PdfWriter.GetInstance(doc, ms);
  doc.Open();
  var mainTable = new PdfPTable(1);
  mainTable.TotalWidth = size.Width;
  mainTable.WidthPercentage = 100f;

  var header = new PdfPCell();
  header.Border = Rectangle.NO_BORDER;

  var body = new PdfPCell();
  body.Border = Rectangle.NO_BORDER;
  body.MinimumHeight = 600f;

  var footer = new PdfPCell();
  footer.Border = Rectangle.NO_BORDER;

  mainTable.AddCell(header);
  mainTable.AddCell(body);
  mainTable.AddCell(footer);            

  doc.Add(mainTable);
  doc.Close();

  return ms.ToArray();
}
So once I have my little pdf I want to return it from my Details action in my controller. The way I solved it was to return a File with the byte array containing the pdf and with the content type set to application/pdf.
[AcceptVerbs("get")]
public ActionResult Details(int id)
{
  ViewModel vm = GetViewModel(id);

  PdfLib lib = new PdfLib();
  Byte[] output = lib.GetPdf(vm);

  return File(output, "application/pdf");
}