MonoRail is pretty cool.  The whole MVC stuff allows you to more easily work with HTML.  What you plop on the page is what you're going to get in the output.  There isn't any crazy viewstate to manage, there's no zany controls that output wacky id names, just you, some loops and HTML.

I read Rob Conery's post about Inline Scripting and a light bulb went off.  There are some times when you want both, the functional yet strictly limited controls that ASP.NET webforms provides, but the flexibility of Monorail's WYSIWYG HTML, and inline scripting allows you to have it.  My cockles were already bubbling with delight, now to just try it on something.

One of our operators last week here asked for a report that gave tabular data, and I created it using a GridView and an Object Data Source (ODS).  Total kool aid MS way of doing stuff.  Hey it's easy, sue me. Today she came back and wanted the report to be able to sort on multiple columns.  Man...this was going to be a bitch... or was it?  JQuery has a Tablesorter javascript thingy.  Copy over some files, and it takes like 6 lines to code up.  Sounds fantastic, lets see THEAD, TBODY tags are required.  Crap, the GridView doesn't plop those out.  It also needs the ClientID of the GridView to bind the javascript to.  Well I could hack that, plus do all sorts of Hackish crap to the GridView which would pretty much remove the easyness from the control or just use some inline scripting.  Instead of thinking about it further and in an attempt to do instead of just talk I went with the inline script.

I'll compare my GridView stuff that only sorts on a single column and has no fancy smancy arrows or anything, and the inline script I created.

First the GridView with the ODS


<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="always">
  <ContentTemplate>
  <asp:GridView ID="Grid" runat="server" ShowFooter="true" AllowPaging="False" DataSourceID="GridSource" SkinID="Report" AutoGenerateColumns="false">
    <Columns>
      <asp:BoundField DataField="ProcessResultID" HeaderText="ID" SortExpression="ProcessResultID" />
      <asp:BoundField DataField="Nsstartdate" HeaderText="Monitor Date" DataFormatString="{0:MM/dd/yyyy}" HtmlEncode="false" SortExpression="Nsstartdate" />
      <asp:BoundField DataField="SiteCode" SortExpression="SiteCode" HeaderText="Site Code" />  
      <asp:TemplateField HeaderText="PI" SortExpression="LastName">
        <ItemTemplate>
          <%# Eval("FirstName") %> <%# Eval("LastName") %>
        </ItemTemplate>
      </asp:TemplateField>
      <asp:BoundField DataField="DOB" HeaderText="DOB" SortExpression="DOB" DataFormatString="{0:MM/dd/yyyy}" HtmlEncode="false" />
      <asp:BoundField DataField="PatientCode" HeaderText="Subject Code" SortExpression="PatientCode" />
      <asp:BoundField DataField="Name" HeaderText="Visit Name" SortExpression="Name" />
      <asp:TemplateField HeaderText="Overall Result" SortExpression="OverallResult">
        <ItemTemplate>
          <%# TallyOverallResult(Eval("OverallResult")) %>
        </ItemTemplate>
        <FooterTemplate>
          <%# GetOverallResultSum() %>
        </FooterTemplate>
      </asp:TemplateField>
      <asp:TemplateField HeaderText="Failure Reason" SortExpression="FailureReason">
        <ItemTemplate>
          <%# TallyFailureReason(Eval("FailureReason")) %>
        </ItemTemplate>
        <FooterTemplate>
          <%# GetFailureReasonSum() %>
        </FooterTemplate>
      </asp:TemplateField>
      <asp:BoundField DataField="OriginalFileName" FooterText="" HeaderText="File Name" SortExpression="OriginalFileName" />
    </Columns>
  </asp:GridView>  
  </ContentTemplate>
</asp:UpdatePanel>
  
<asp:ObjectDataSource ID="GridSource" runat="server"
  SelectMethod="GetSummary" TypeName="Ns.Data.RptStudySiteResultsSummary">
  <SelectParameters>
    <asp:QueryStringParameter Name="studyId" QueryStringField="studyId" Type="Int32" DefaultValue="1" ConvertEmptyStringToNull="true" />
    <asp:QueryStringParameter Name="siteId" QueryStringField="siteId" Type="Int32" ConvertEmptyStringToNull="true" />
    <asp:QueryStringParameter Name="piId" QueryStringField="piId" Type="Int32" ConvertEmptyStringToNull="true" />
    <asp:Parameter Name="orderBy" Type="String" ConvertEmptyStringToNull="true" />
  </SelectParameters>
</asp:ObjectDataSource>

That's 46 lines of code that can only do what it was programmed for.  Hey it works well in other situations so I'm not going to knock it one bit, but in this case isn't getting the job done.  The GridView through the UpdatePanel sorts asyncronously so there are server calls for each sort.  I'm on an Intranet app so don't really care too much to do caching or anything along those lines.  At the end of the day though this isn't getting things done.

Here's my inline code.

<script type="text/javascript" src="../../js/jquery-1.2.1.min.js"></script> 
<script type="text/javascript" src="../../js/jquery.tablesorter.js"></script> 
<script type="text/javascript">
$(document).ready(function() 
    { 
        $("#SummaryReport").tablesorter(); 
    } 
);
</script>
Javascript comes first, that was easy...
<table id="SummaryReport" class="tablesorter">
  <thead>
    <tr>
      <th>ID</th>
      <th>Monitor Date</th>
      <th>Site Code</th>
      <th>PI</th>
      <th>DOB</th>
      <th>Subject Code</th>
      <th>Visit Name</th>
      <th>Overall Result</th>
      <th>Failure Reason</th>
      <th>Filename</th>
    </tr>
  </thead>
  <tbody>
    <%
      foreach (Ns.Data.VwResultSummary rs in Ns.Data.VwResultSummary.GetSummaries(StudyId.Value, SiteId.GetValueOrDefault(0), BeginDate, EndDate))
      {
    %>
    <tr>
      <td><%= rs.ProcessResultID %></td>  
      <td><%= rs.NsStartDate.ToShortDateString()%></td>  
      <td><%= rs.SiteCode %></td>  
      <td><%= rs.FirstName %> <%= rs.LastName %></td>  
      <td><%= rs.Dob.Value.ToShortDateString() %></td>  
      <td><%= rs.PatientCode %></td>    
      <td><%= rs.Name %></td>    
      <td><%= TallyOverallResult(rs.OverallResult) %></td>    
      <td><%= TallyFailureReason(rs.FailureReason) %></td>
      <td><%= rs.OriginalFilename %></td>    
    </tr>
    <%
      }  
    %>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="7">&nbsp;</td>
      <td>
        <%= GetOverallResultSum() %>
      </td>
      <td>
        <%= GetFailureReasonSum() %>
      </td>
    </tr>
  </tfoot>
</table>
<p class="noprint">
    <em>TIP!</em> Sort multiple columns simultaneously by holding down the shift key and clicking a second, third or even fourth column header!
</p>

 Then my inline code.  As Rob told us, intellisense is there (something MonoRail doesn't have for Boo syntax), the only thing that seems to be missing is the compile time errors, but you'll see them if they're there when you run the page, just like in classic ASP (or PHP, JSP, etc).  The inline code clocks in at 50 lines, but I'm thinking if you take away all the line breaks for the header and footer elements that I think make it more readable you're looking at more like 35 lines.  The biggest thing here was the call to the collection that I loop through, and that wasn't tough at all just different as I haven't done it before, and is nicely tucked away in my data layer.

The thing is, now I have a table that only needs to be populated once, is sorted client side, and has the multi-column sorts.  For a bonus I now also have sort arrows and pretty colors which work as advertised too.  So very cool.

Last thoughts: The beautiful thing about coding is there's no one right way.  This is simply yet another way that I found very easy to implement and it'll be very easy manage, and it's real world, I'm deploying it here in a second for testing.  So take that you negative WebForm CodeBehind only people!

Link to Rob Conery » Crazy Talk: Inline Scripting and Code-Behind