<CharlieDigital/> Programming, Politics, and uhh…pineapples

26Feb/08Off

SharePoint Layout Pages With CodeBehind And Prototype

Let it be known that I hate out of the box ASP.NET.  Hate it, hate it, hate it, hate it.  I detest it.  The simplicity with which it allows the average developer to create applications leads to applications designed for RAD and not for scalability and it does not encourage good decoupling of business logic from UI logic.  Certainly, there are a number of frameworks which aim to alleviate this (the Web Client Software Factory, for example), but I like to take it to another level all together.


Some would argue that I take the separation of UI and application logic to the extreme: my preferred methodology relies almost purely on client side scripts to render UI and using only web services to supply data using ASP.NET AJAX.  Certainly, I lose design time support, but I gain in pure speed (all of the UI logic is in Javascript files which are cached by the client), data transfer sizes (since the only traffic is data, no presentation whatsoever), and the ultimate decoupling of UI development and server component development (the UI developer only needs to know the data model exposed by the web services).


The way I look at it, you'll only write the code a few times, but it could be in use for months (and if you're lucky in this Web 2.0 age, even a year or two).  Sure, you lose some productivity for a single developer with the loss of design time support, but you gain tremendously over time with each request serviced in terms of performance and bytes saved (a particularly important point for high traffic/high data volume applications).  As a bonus, I find it generally easier to think about application design in these terms.


Admittedly, this model seems to work better for "business applications" as opposed to "content applications".


In any case, I was interested to see if this methodology could be applied to SharePoint development as I've been working with SharePoint for quite a while now, but not at the UI level.  SharePoint allows you to deploy "application pages" which can be seemlessly integrated (kind of) into a SharePoint deployment.  This seemed like the perfect starting point to try to integrate ASP.NET AJAX and prototype, one of my favorite Javascript libraries.


The general steps are:



  1. Create an ASP.NET AJAX web application

    1. Add a reference to the Microsoft.SharePoint assembly
    2. Add the prototype.js script file to the project
    3. Add a strong name key file and sign the project
    4. Build a simple page
    5. Create a simple service

  2. Copy the content files (.js, .aspx, .asmx) over to the server to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\ into a new directory.
  3. Test by visiting the URL: http://myserver/_layouts/mynewdirectory/default.aspx

The base page should be simple.  Use the default page created by the ASP.NET AJAX web application project template and change the base class for the _Default.aspx.cs file to LayoutsPageBase instead of of the default of Page.  If you're not using ReSharper ;-), you'll need to add a using statement to your file:


using System;
using Microsoft.SharePoint.WebControls;

namespace WssAjaxApplicationTest {
public partial class _Default : LayoutsPageBase {
protected void Page_Load(
object sender, EventArgs e) {}
}
}


Next, you will need to modify the Default.aspx file.  The gist of the modifications comes from an MSDN article by Ted Pattison:


<%@ Page Language="C#" 
AutoEventWireup="true"
CodeBehind="Default.aspx.cs"
Inherits="WssAjaxApplicationTest._Default"
MasterPageFile="~/_layouts/application.master"%>

<asp:Content ID="Main" runat="server" ContentPlaceHolderID="PlaceHolderMain">
<script type="text/javascript" src="_scr/prototype.js"></script>
<script type="text/javascript" src="_scr/WssAjaxApplication.js"></script>
<script type="text/javascript">
var application;

function Init() {
application = new WssAjaxApplication();
}

Event.observe(window, "load", Init, false);
</script>
<asp:ScriptManager ID="ScriptManager1" runat="server" >
<Services>
<asp:ServiceReference Path="~/Services/EchoService.asmx" />
</Services>
</asp:ScriptManager>
<div>
<input type="text" id="message-input" />
<input type="button" id="action-button" value="Go!" />
<br />
<div id="message-output"></div>
</div>
</asp:Content>

<asp:Content ID="PageTitle"
runat="server"
contentplaceholderid="PlaceHolderPageTitle" >
Echo Page
</asp:Content>

<asp:Content ID="PageTitleInTitleArea"
runat="server"
contentplaceholderid="PlaceHolderPageTitleInTitleArea" >
The Echo Page Test
</asp:Content>


I've bolded the key part above, which is linking to the master page for SharePoint layout application pages.  In addition, you can see that I've created three placeholder content sections with the key section being the PlaceHolderMain.  I've placed my Javascript references and my ScriptManager into this section, pointing to our simple service, EchoService.asmx.  Notice the use of the root squiggly "~" 😀 and the lack of squiggly on the script references to prototype.js and WssAjaxApplication.js.


The service I'm going to be using for this demo is a simple "echo service" which just echoes the input string with the server timestamp attached.  The following is my simple implementation of this web service:


using System;
using System.ComponentModel;
using System.Web.Script.Services;
using System.Web.Services;

namespace WssAjaxApplicationTest.Services {
/// <summary>
/// Summary description for EchoService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class EchoService : WebService {
[WebMethod]
[ScriptMethod]
public string Echo(string message) {
message = string.Format("You said: \"{0}\" at {1}",
message, DateTime.Now);

return message;
}
}
}


As you can see by the .aspx page above, I've organized my service in a sub-folder called "Services".  Now just make sure that you've added a copy of prototype.js under the _scr directory and my application script:


WssAjaxApplication = Class.create();

Object.extend(WssAjaxApplication.prototype, {
initialize:function() {
this.MessageInput = $("message-input");
this.ActionButton = $("action-button");
this.MessageOutput = $("message-output");

Event.observe($('action-button'), "click",
this.OnClickActionButton.bindAsEventListener(this), false);
},

OnClickActionButton:function(e) {
if(e) { Event.stop(e); } // Stop the event

// Perform the echo.
WssAjaxApplicationTest.Services.EchoService.Echo(
this.MessageInput.value,
this.OnClickActionButtonSuccess.bindAsEventListener(this),
this.OnClickActionButtonError.bindAsEventListener(this)
);
},

OnClickActionButtonSuccess:function(result) {
this.MessageOutput.innerHTML = result;
},

OnClickActionButtonError:function(error, userContext, methodName) {
window.alert(methodName +
" failed with the message: " + error.get_message());
}
});


The script simply attachs an event listener to the "Go" button and handles the click event.  Notice how clean and simple the HTML portion of the page is and how clean the Javascript is as well (admittedly, this is a very simple example).  The client rendering is completely decoupled from the UI logic except for the data and operations contract. 


You should be good to go so far as code goes.  Now compile your project with a strong named key file. 


Hopefully, the project was compiled successfully.  The next step is to copy the output dll to the GAC of the SharePoint server.  Be sure to note the public key token value.


This is probably the trickiest part: now you need to carefully merge the configuration files (is there a better tool to do this with?) generated by the project template with the web.config file located at the virtual directory root of your SharePoint application.  For example, if you have an application deployed at port 8080, the web.config file should be located at C:\Inetpub\wwwroot\wss\VirtualDirectories\8080.  Be sure to save a backup copy of the configuration file first before you attempt to merge it!


Once merged, you will need to add one more element to the configuration file:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
<system.web>
<compilation batch="false" debug="false">
<assemblies>
<add assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" />
<add assembly="System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
<add assembly="WssAjaxApplicationTest,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=97d3f1fd9f5212b9"/>

</assemblies>
</compilation>
</system.web>
</configuration>

I've highlighted the key line (the line above it should have been merged into the file previously).  The bolded entry above is for the web application binary.


To test whether you've succeeded, you can simply point your browser to the URL: http://myserver/_layouts/mywebapp/default.aspx and you will have a fully AJAX enabled application using ASP.NET AJAX to connect to a .NET web service with prototype as a general purpose Javascript utility library (and you can even add scriptaculous on top of that for more awesome).


I've included a self extracting 7z file of the solution (see link below) if you'd like a quick start.  Note that the Microsoft SharePoint binaries are not included and you will have to add them back manually before the project will build.


Happy coding!

WssAjaxApplicationTest.exe (162.02 KB)

Posted by Charles Chen

Filed under: .Net, SharePoint Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.