DevExpertise » Blog Archive » Integrating a Custom ASP.NET Application into SharePoint (Part 1)

DevExpertise

Practical tips and tricks for all things .NET, SharePoint, Silverlight, InfoPath, and general application development.

Integrating a Custom ASP.NET Application into SharePoint (Part 1)

Posted by DevExpert on February 18th, 2009

One of the great things about SharePoint is in addition to all cool stuff it does out-of-the-box, you can add on functionality.  More importantly though, SharePoint can be a great platform to build your own application on top of.  In this series, I will show you how to build a custom ASP.NET application and integrate it seamlessly into SharePoint.

The first thing to understand is the location at which we will deploy our custom artifacts.  Since the application will run under the context of a SharePoint site, the files will be deployed to the LAYOUTS folder within the ~12 directory.  There isn’t a need to create a new IIS web site or virtual directory, as it’s using the SharePoint site.

Now, there are different opinions on where certain artifacts should go.  I really don’t think it matters; just personal preference.  One option is to stick with the folder structure that SharePoint uses, and just place a custom folder in each of the destinations that contain your custom artifacts.  Typically this involves placing your files in the following directories:

Type Destination  Reference Path 
.aspx 12\TEMPLATE\LAYOUTS\<ProjectName>\ ~/_layouts/<ProjectName>/Page.aspx
.ascx 12\TEMPLATE\CONTROLTEMPLATES\<ProjectName>\ ~/_controltemplates/<ProjectName>/control.ascx
web.config 12\TEMPLATE\LAYOUTS\<ProjectName>\ (none)
.css 12\TEMPLATE\1033\Styles\<ProjectName>\ /_layouts/1033/styles/<ProjectName>/style.css
.js 12\TEMPLATE\LAYOUTS\1033\<ProjectName>\ /_layouts/1033/<ProjectName>/script.js
.dll Either web app’s BIN directory or GAC (none)
Resource DLLs GAC (none)
Images 12\TEMPLATE\IMAGES\<ProjectName>\ /_layouts/images/<ProjectName>/image.gif
Custom Folders 12\TEMPLATE\LAYOUTS\<ProjectName>\ ~/_layouts/<ProjectName>/MyFolder/…

 

The other option (and my personal preference) is to put everything within a custom folder in the LAYOUTS directory, and only put those files that would require other changes in their respective places.  For example, since a SafeControls entry is required in the web.config for user controls, it makes sense to keep your user controls within that folder.  You could definitely put them inside the LAYOUTS folder with everything else, but then you’d have to create another SafeControls entry.

Type Destination  Reference Path 
.aspx 12\TEMPLATE\LAYOUTS\<ProjectName>\ Page.aspx
.ascx 12\TEMPLATE\CONTROLTEMPLATES\<ProjectName>\ ~/_controltemplates/<ProjectName>/control.ascx
web.config 12\TEMPLATE\LAYOUTS\<ProjectName>\ (none)
.css 12\TEMPLATE\LAYOUTS\<ProjectName>\Styles\ Styles/style.css
.js 12\TEMPLATE\LAYOUTS\<ProjectName>\Scripts\ Scripts/script.js
.dll Either web app’s BIN directory or GAC (none)
Resource DLLs GAC (none)
Images 12\TEMPLATE\LAYOUTS\<ProjectName>\Images Images/image.gif
Custom Folders 12\TEMPLATE\LAYOUTS\<ProjectName>\ MyFolder/…

 

Now that the file locations are ironed out, let’s start getting into how to develop the pages.  I will dive into utilizing built-in SharePoint controls, permissions, and some of the fancier stuff in later posts, and will focus on just getting a page to show up within SharePoint.  Your approach to this may differ, but this has proven very effective for me.  First, I create a new web application in Visual Studio, and create a folder structure that mimics SharePoint’s 12 directory:

image

You’ll notice that I have 2 web.config files – one is created when the project is created and can be used to test the project locally, and the other is the one that will be put into SharePoint.  My web.config that goes into SharePoint is very simple and looks like the following.  For testing purposes, I added a test application setting which we’ll retrieve shortly:

<?xml version="1.0"?>
<configuration>
  <system.web />
  <appSettings>
    <add key="customKey" value="Sample Value" />
  </appSettings>
</configuration>


The next part is probably the most important part of this whole process – setting up the ASPX markup correctly.  Since this page will be integrated into SharePoint’s master page, the same master page/content page principles apply.  The master page contains content place holders which define where page content will go, and the pages themselves define the content that gets inserted in these areas.  SharePoint master pages have a ton of content place holders, most of which we don’t need in a custom application.  The ones that are important are:

  • PlaceHolderAdditionalPageHead: The content area where custom scripts and styles will be referenced.
  • PlaceHolderPageTitle: The title of the page.
  • PlaceHolderPageTitleInTitleArea: The text that shows up right above the main content area.
  • PlaceHolderMain: The main content area.
  • PlaceHolderLeftNavBar: If you want to define your own QuickLaunch or left navigation, you could place it here.

Since it’s up to us to define the content within these place holders, all we need to do is add content areas to our ASPX page and put in what we want.  I only used the top 4 aforementioned areas, as I want to utilize the existing quick launch navigation menu:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.cs"
         Inherits="DevExpertise.LayoutsApp.Home, DevExpertise.LayoutsApp,
                   Version=1.0.0.0, Culture=neutral, PublicKeyToken=d39eedb6cff9b1c8" %>

<asp:Content contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">
    <link rel="Stylesheet" type="text/css" href="Styles/style.css" />
    <script src="Scripts/script.js" type="text/javascript" />
</asp:Content>

<asp:Content ContentPlaceHolderID="PlaceHolderPageTitle" runat="Server">
    Page Title - Custom Application
</asp:Content>

<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
    Title Area - Custom Application
</asp:Content>

<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <h1>This is a custom application!</h1>
    <asp:TextBox id="txtValue" runat="server" />
    <asp:Button id="btnSetValue" runat="server" Text="Click Me!" OnClick="btnSetValue_Click" />
</asp:Content>


This doesn’t do much, but will prove the concept.  You can see that I added a custom style sheet (style.css), and also a custom script file (script.js), just so you could see where they go.  In addition, I added a textbox and a button and attached an event handler for the Click event of that button.  In this event handler, I’ll retrieve the web.config setting I mentioned above and set the textbox to this value. The code-behind for this page looks like the following:

namespace DevExpertise.LayoutsApp {
    public partial class Home :System.Web.UI.Page  {

        protected void Page_Load(object sender, EventArgs e) {
        }

        protected void btnSetValue_Click(object sender, EventArgs e) {
            txtValue.Text = WebConfigurationManager.AppSettings["customKey"].ToString();
        }
    }
}

 
Since this code will be executed under the context of SharePoint, the same code access security restrictions apply here as with web parts and custom web services.  You basically have 3 options: adding the assembly to the web application’s BIN directory and setting the trust level to at least WSS_Medium in the web.config, creating a custom code access security policy for your application, or adding the assembly to the GAC.  There are plenty of resources regarding the advantages and disadvantages to each approach out there so I’ll spare you here.  For the sake of simplicity, I added the assembly to the GAC.

Next, I deployed my files to the SharePoint 12 directory.  Since I’m doing this in a development/test environment, I created a handy copy.bat script that uses XCOPY to copy the files to the respective directories.  As soon as this project is ready to be deployed, I’ll run my solution through WSPBuilder and will generate a deployable solution package (.WSP).

After the files are deployed, it’s as simple as typing in the correct URL.  The syntax for this is as follows:

http://server/site/_layouts/<ProjectFolder>/<PageName>.aspx

The URL is extremely important when accessing your application pages, as your application runs under the context of the SharePoint site specified in the URL.  What does this mean?  Well, if you access your page at http://server/_layouts/MyProject/MyPage.aspx, then it’s running under the context of the site collection’s root site, and accessing SPContext.Current.Web will return that site.  If you access your page at http://server/sites/it/blog/_layouts/MyProject/MyPage.aspx, then it’s running under the context of the blog site under the IT site collection, and SPContext.Current.Web will reflect that.  Why is this important?  Well, since the application pages live in the 12 directory on the farm, they’re globally accessible, and not limited to a single site collection or site.  You could even get to your application at http://CentralAdminUrl/_layouts/MyProject/MyPage.aspx, and it will be running under Central Administration’s context.  Now do you see the importance?  I will show you in a later post how to implement safeguards to mitigate this, but be aware for now that your pages are out there for everyone to access.

For my development machine, I will be accessing this at the following URL:

http://server/sites/devexpertise/_layouts/DevExpertise.LayoutsApp/Home.aspx

However, when I try to access this, I get the following:

image


No worries — all this is telling me is that we forgot to specify the master page.  Since this will “inherit” the master page and styles of whichever site it’s accessed from, we must set the master page to that of the current site.  To accomplish this easily for each of my application pages, I create a base LayoutsAppPage that sets the master page:

namespace DevExpertise.LayoutsApp {
    public class LayoutsAppPage : Microsoft.SharePoint.WebControls.LayoutsPageBase {

        protected override void OnPreInit(EventArgs e) {
            base.OnPreInit(e);

            try {
                this.MasterPageFile = SPContext.Current.Web.MasterUrl;
            }
            catch { }
        }
    }
}


You’ll notice that I’m inheriting this from LayoutsPageBase – this is a base class defined in the Microsoft.SharePoint.WebControls namespace that provides us functionality for creating these types of pages.  That’s beyond the scope of this first post, but I will touch on this later in the series.  Next, I inherit each of my application pages from my custom LayoutsAppPage base class:

public partial class Home : LayoutsAppPage {

}


Now if we access the page in the browser, we should get a functioning page.  Clicking the button should retrieve the application setting from the web.config as well:

image


Pretty slick, huh?  Stay tuned for the next posts in this series where we’ll look at how to secure our application and utilize existing SharePoint controls to provide a rich and familiar user interface.

25 Responses to “Integrating a Custom ASP.NET Application into SharePoint (Part 1)”

  1. Will Says:

    Hi,

    Thanks for the tutorial. I’m new to SP, and this was easy to follow.
    I’ve replicated what you have done, and have managed to get a custom page up and running within SharePoint, but I am running into 2 issues though.

    1) Since the custom Web App doesn’t live within the context of a masterpage, the aspx page complains about validation of asp tags/etc. Is there any way around this?
    2) My button doesn’t trigger anything. I set it up just to set a preset value (rather than sniffing out the config settings), and it just sits there and doesn’t do anything onclick. What could be the cause of this?

    Thanks,
    Will

  2. DevExpert Says:

    Will,

    Not sure if these answer your questions, but hopefully they give you a few more things to try…

    1.) Are you only using ASP.NET controls (those that use a tagprefix of “asp”), or are you using SharePoint controls as well? If you’re not using a master page, you will need to make sure the controls that you’re using on your page are correctly registered at the top of your page. For example, if you’re using controls within the Microsoft.Sharepoint.WebControls namespace, you will need something like this at the top of your page:

    < %@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

    The ASP.NET controls should be picking up the tagPrefix that’s configured in SharePoint’s web.config under the section.

    2.) Try taking the reference to the JavaScript file out and see if that produces any different behavior. I had bad script and I experienced the same type of thing…

  3. Will Says:

    1) I’m just using basic asp controls, but I don’t have a master page set up, and I guess my solution isn’t aware of the fact that it’s meant to live inside a SharePoint site. It does work when I drop it into the _layouts folder, but VS just doesn’t want to play nice with my tags. The sharepoint declaration you provided works, but the more basic asp one doesn’t seem to. I’ll keep digging.

    2) I took out the script reference, and it did cause it to function correctly. I also moved the onclick in the button from the aspx page to the code behind:
    ie:

    protected override void OnInit(EventArgs e)
    {
    btnSetValue.Click += new EventHandler(btnSetValue_Click);
    }

    I read on another blog that since this is considered client side code, SharePoint could strip it out if the page was ever customised.

  4. DevExpert Says:

    Will,

    1.) If you’re not running the app under the context of SharePoint and aren’t using a master page, the “asp” tagPrefix is not being registered anywhere. You will have to register it either in the web.config in your custom app (take a look at SharePoint’s web.config to see how), or add the register tag to the top of your page exactly like the one I sent you for SharePoint (only referencing the System.Web.UI namespaces).

    2.) I haven’t heard that about page customizations — the blogs you read were probably referencing page customization made with SharePoint Designer, as that has been known to screw with the markup of a page. I personally haven’t experienced that, but it wouldn’t surprise me at all if that were the case.

    Glad you got it working!

  5. prasad punneri Says:

    great article
    thnaks for the ton fo inf.
    Anyway can you tel me how to bind a spgridview with custom spslist

    SPDataSource spdatasource = new SPDataSource();
    spdatasource.List = mySite.RootWeb.Lists["DCVisitorList"];

    spdatasource.SelectCommand = “1 “;
    //”"
    //+ this.CategoryBox.SelectedValue + “”;

    if (mySPGridView.Rows.Count > 0)
    {
    this.mySPGridView.DataSource = spdatasource;

    mySPGridView.DataBind()
    }

    this cde didint work for mw,
    though thru u2u caml return a SINGLE ROW.
    when i chk the if (mySPGridView.Rows.Count > 0)
    its returns zero records?

    WHY THIS HAPPNED??
    any thoughts highly appreciated.
    prasad

  6. DevExpert Says:

    Prasad,

    You don’t need to use SPDataSource to bind to the grid; you can just retrieve a regular ol’ DataTable from an SPQuery object that you execute on your list. Take a look at this article, which describes how to do it. Let me know how it goes, and I’ll try to help out!

  7. Omar Says:

    Hi, i hope, help me with the next error on asp.net application page code vb.net

    ERROR:

    Los controles de contenido tienen que ser controles de nivel superior en una página de contenido o una página anidada que haga referencia a una página principal.

    traduced (moreless)

    Content Controls to be a Controls superior level in content page.

    en System.Web.UI.WebControls.ContentBuilderInternal.SetParentBuilder(ControlBuilder parentBuilder)
    en System.Web.UI.ControlBuilder.CreateChildBuilder(String filter, String tagName, IDictionary attribs, TemplateParser parser, ControlBuilder parentBuilder, String id, Int32 line, VirtualPath virtualPath, Type& childType, Boolean defaultProperty)
    en System.Web.UI.TemplateParser.ProcessBeginTag(Match match, String inputText)
    en System.Web.UI.TemplateParser.ParseStringInternal(String text, Encoding fileEncoding)

    thanks for your time.

  8. MS Says:

    I tried to create an application as shown. When I create a home.aspx. I get unrecognized tag prefix or device filter ‘asp’. How do I point to masterpage file of sharepoint in home.aspx to get rid of this error. Is this sample avaiable for download so I can see what I missed

    I am using VS 2005, WSS 3.0

    Thanks for the help

  9. David Says:

    Good information! Can you post a download of the solution for us to poke through?

  10. DevExpert Says:

    David, unfortunately I no longer have the code in a handy VS solution I can provide. Luckily I didn’t have anything in the solution that I didn’t blog about, so there shouldn’t be any surprises. Sorry!

  11. Clarence Says:

    Thanks for the information. After reading your blog “with a find tooth comb,” I was able to get your sample working. I think you should write a book on this. I would definitely buy it. Mahalo Nui Loa…

  12. Tri Says:

    It looked simple, thanks.
    Since I’m still a newbie, can you walk me through how do you deploy? N what scripts are contained in the .bat file?

    Basically, I created my project on my local PC, and the SharePoint is on a different virtual machine. How do I deploy my project onto the different virtual machine?

    Again, thanks.

  13. Keith Says:

    Thanks for the info. I haven’t tried this out yet, but I do not see any reason this won’t work.

    My question is do you have any information on how to get a custom page more “connected” to a specific SharePoint site. Your URL could be referenced from any SharePoint site because the _Layouts folder is specific to the installation and not the site (I think). I am looking for a way to tie a custom page to specific SharePoint site.

    Our site is portal.z.com. From there, we have portal.z.com/LMS for our Learning Management System. Is there any way to have a custom page like this ONLY in LMS…so my URL would look more like portal.z.com/LMS/custompage.aspx?

    Thank you for any help you can provide.

  14. Jpw Says:

    I have deployed via feature using wsp. It is functionally correct but something is screwing with my css.
    Firebug show the css is finding my images, but they are not being rendered. Http://wwwdev.Andover.edu/tutorial2/pages/landing.aspx

    my hunch is that some SP CSS is walking on mine?

  15. DevExpert Says:

    Tri, unfortunately there’s no “perfect” way to deploy from a remote development environment. You can always copy the files to the SharePoint server as long as you can access the file system remotely, but some operations like adding assemblies into the GAC, installing features, etc. can only be performed directly on the SharePoint server. If you have to develop on different machines, my suggestion would be to set up xcopy scripts that copy everything to your SharePoint server, then have a script ON the SharePoint server that does the things you can only do there (like GAC’ing, feature installing, etc.). Good luck!

  16. DevExpert Says:

    Keith, to address your first point, i’m not sure you could say the custom page is “connected” to your site, but depending on how you access the page it will run under the context that is specified. For example, if you access MyCustomPage.aspx at http://myserver/sites/sitecollectionA/_layouts/myfolder/mycustompage.aspx, it will be running under the context of SiteCollectionA. If you access it at http://myserver/sites/sitecollectionB/_layouts/myfolder/mycustompage.aspx, it will be running under the context of SiteCollectionB. Does that make sense?

    As far as not requiring the _layouts/etc/etc in the URL, I don’t think there’s anything out of the box that can do that. Possibly try creating an HttpModule that rewrites the URL? Check this post out from Scott Guthrie. I’m not sure how nicely this will play with SharePoint, but it’s worth a shot. Good luck!

  17. DevExpert Says:

    Jpw, hmm…I’m not sure I have enough context around the issue to make a suggestion. I took a look a the site and the CSS is definitely messed up, but I’m not sure where the problem is. If you scroll all the way down there’s additional stuff hiding at the bottom of the page. My initial thought is the CSS just needs cleaned up and fixed so it renders correctly. Does it look OK outside of SharePoint? If so, then it’s VERY possible the stuff in CORE.CS is screwing with your layout.

  18. anitha Says:

    hi,

    The info is very good! but I am not able to view the site. It just gives unknown error. Is is possible to give the project as a download.I would really appreciate it. I have been trying for last one week and it doesn’t work. Thanks a bunch!

    Anitha

  19. anitha Says:

    I also want to know about the references? Do you just add Microsoft.sharepoint.dll from the GAC as a references or do you create a bin folder and then add the references?

    Thanks
    Anitha

  20. happy Says:

    Hi I have accomplished integrating asp.net application that has just one page (default.aspx) into sharepoint. But how do I go about with calling another webpage in the application? Can someone provide a sample of how to call another webpage on click of a button

  21. Anjan Maity Says:

    Hi
    Thanks for this valuable article.i am in new in share point.
    I am facing problem on using copy.bat .
    Please explain the steps and Xcopy script .
    That will be very help full.

    Thanks and regards
    Anjan Maity

  22. Anjan Maity Says:

    I am facing the error ….
    Z:\TataiBlog\TataiBlog>∩╗┐@SET WSPPBUILDER=”C:\Program Files\WSPTools\WSPBuilder
    Extensions\WspBuilder.exe”
    ‘∩╗┐@SET’ is not recognized as an internal or external command,
    operable program or batch file.

    Z:\TataiBlog\TataiBlog>ECHO Copying Files to Temporary Solution Directory
    Copying Files to Temporary Solution Directory

    Z:\TataiBlog\TataiBlog>xcopy bin\TataiBlog.LayoutsApp.dll Solution\GAC\ /y /r
    bin\TataiBlog.LayoutsApp.dll
    1 File(s) copied

    Z:\TataiBlog\TataiBlog>ECHO Building Solution Package
    Building Solution Package

    Z:\TataiBlog\TataiBlog>-CreateWSPFileList wspfiles.txt -outputpath solution
    ‘-CreateWSPFileList’ is not recognized as an internal or external command,
    operable program or batch file.

    Z:\TataiBlog\TataiBlog>-12path Solution\12 -gacpath Solution\GAC -Excludepaths b
    in
    ^C’-12path’ is not recognized as an internal or external command,
    operable program or batch file.
    Please
    Explain.
    Thanks

  23. RadySchool Says:

    I am trying to get the sample work and getting errors with the inherits on the page directive…it says not a valid value

  24. naru Says:

    Hi,

    I tried the above stuff exactly as is, with the following exception – no web.config file (the text value is directly assigned, no js/css files( the lines are commented in the aspx page).

    After deploying, when I try the following:
    http://server/-layouts//Home.aspx

    I get the following error : “Unknown Error”.

    Can you please help me out. Please treat this as urgent.

    Thanks

  25. naru Says:

    sorry, please read -layouts as “_layouts” in the last post.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>