Anyone who is doing SharePoint development can attest to what a hassle it is to create new ASPX pages in your project and actually just how many manual steps there are to creating most project items. Ideally the tools would have been better by now, but they aren't, so I wanted to share an easy way to get items created the way you want them to be with a lot less cut and paste. Take the scenario of adding custom ASPX pages to your SharePoint project. Since you are in a C# class library project and not a web you don't natively have the Add Item > WebForm option so this typically means creating the whole thing from scratch or by doing some cut and paste into your newly added, renamed text file. So let's make this task a lot easier. Visual Studio provides a nice mechanism for creating a Project Item Wizard, which is simply a block of code you tell it to run in a specific item template. When you create a standard ASPX page in a web project you notice the class name gets created with what you typed as the page name, your namespace is set for you and the page will run in its newly created form because it all got wired up right. Well in that template there is a reference to the wizard that the Visual Studio Team built to do all that for you. So if we want real SharePoint items, we can just add our custom templates and our own wizard. Sounds like a lot of work, but in fact this is pretty simple to do and the rest of this post will walk you through the steps. As with all code I post, the disclaimer referenced in the ‘About’ section of my blog applies.
The code in this post will assume you are familiar with Visual Studio Templates and also familiar with Visual Studio Wizards, at least at a high level. Reading those 2 articles first will make the rest of this post a lot more meaningful to you.
The goal: Adding a custom web page to a C# library project that runs inside SharePoint and already references all the right stuff. The post will walk through the highlights, but not the entire solution. I am attaching the entire solution in a zip file (No executables, you have to build it yourself) and you will have to go through it as you read to get the full story.

The How-To:
First, Open Visual Studio 2008 SP1 and create a new C# class library project. Set a reference to EnvDTE (Visual Studio object model), SharePoint.dll and Microsoft.VisualStudio.TemplateWizardInterface (Magic for this task).

Create a folder in your project called “Item Templates” and add a sub folder to that for your first Template “SPCustomWebPage”. This is where we will build our first page template and also put our .vstemplate file which will tell Visual Studio what to do with it and how to launch our wizard.
Inside your SPCustomWebPage Folder, create 3 new files containing the following code:
Create a file named - SPCustomWebPage.aspx (Copy in the code below)
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C" %>
<%@ Assembly Name="$spassemblyname$" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" Debug="true" Inherits="$spprojectname$.$safeitemname$" meta:progid="SharePoint.WebPartPages.Document" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<!--
SAMPLE CODE:
The Asp.Net controls below are samples of how a content control or button can be used in a custom page.
Delete this comment and the controls, then add your own content.
->
<asp:Literal runat="server" ID="ltrlHelloText" />
<br />
<asp:Button runat="server" ID="btnHello" Text="Say Hello" />
</asp:Content>
<asp:Content ID="PageTitleInArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea"
runat="server">
SharePoint Custom Page
</asp:Content>
<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
SharePoint Custom Page
</asp:Content>
Next, create a file named - SPCustomWebPage.aspx.cs (Copy in the code below)
/// <CustomWebPageCodeBehind>
/// <Description>Sample CustomWebPage Template</Description>
/// <WorkItemInfo></WorkItemInfo>
/// <Author></Author>
/// <DateCreated></DateCreated>
/// <DateModified></DateModified>
/// </CustomWebPageCodeBehind>
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
/* SharePoint Namespaces */
using Microsoft.SharePoint;
using Microsoft.SharePoint.ApplicationPages;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
namespace $spprojectname$
{
public class $safeitemname$ : System.Web.UI.Page
{
#region ' locals '
// SharePoint Context
protected SPContext _spContext;
// Page Content Controls
protected Content Main;
protected Content PageTitleInArea;
protected Content PageTitle;
// SAMPLE CONTROLS -DELETE AND ADD YOUR OWN CONTENT
protected Literal ltrlHelloText;
protected Button btnHello;
#endregion ' locals '
#region ' page events '
protected override void OnInit(EventArgs e)
{
// Set local context ref
_spContext = SPContext.GetContext(this.Context);
// SAMPLE Button click mapping
btnHello.Click += new EventHandler(btnHello_Click);
}
protected override void OnLoad(EventArgs e)
{
// Put your code here...
}
#endregion ' page events '
#region ' control events '
/// <summary>
/// SAMPLE CODE
/// </summary>
protected void btnHello_Click(object sender, EventArgs e)
{
ltrlHelloText.Text = "Hello World.";
}
#endregion ' control events '
}
}
Last, Create a file named SPCustomWebPage.vstemplate (Copy in the code below)
<VSTemplate Type="Item" Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
<TemplateData>
<Name>SPCustomWebPage</Name>
<Description>Sharepoint Application ASP.Net Page Template</Description>
<Icon Package="{39c9c826-8ef8-4079-8c95-428f5b1c323f}" ID="4533"/>
<ProjectType>CSharp</ProjectType>
<DefaultName>SPCustomWebPage.aspx</DefaultName>
<Hidden>false</Hidden>
</TemplateData>
<TemplateContent>
<References>
<Reference>
<Assembly>Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C</Assembly>
</Reference>
</References>
<CustomParameters>
<CustomParameter Name="$ParentExtension$" Value="aspx"/>
<CustomParameter Name="$ChildExtension$" Value="cs"/>
</CustomParameters>
<ProjectItem ReplaceParameters="true" TargetFileName="$fileinputname$.$fileinputextension$">SPCustomWebPage.aspx</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="\App_Code\SPCustomWebPages\$fileinputname$.$fileinputextension$.$ChildExtension$"
SubType="ASPXCodeBehind" OpenInEditor="true">SPCustomWebPage.aspx.cs</ProjectItem>
</TemplateContent>
<WizardExtension>
<Assembly>SharePointProjectItemWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=056c747a3ee911c2</Assembly>
<FullClassName>SharePointProjectItemWizard.SPItemWizard</FullClassName>
</WizardExtension>
</VSTemplate>
So now let’s look at the code you just copied for each file.
First, in the .ASPX page, notice the line <%@ Assembly Name="$spassemblyname$" %> - what the heck is that? The project item wizard is going to allow us to dynamically replace tokens with meaningful code on ite creation. This is how we can wire this page up so it actually runs in SharePoint the first time.
Second file, the code behind for our page, you will notice that there are actually a couple controls already plumbed up to test the page in SharePoint and the page events are already wired up as well. Like our ASPX page, this code also contains tokens for the class name and namespace that will get replaced on item creation to complete our wire up:
namespace $spprojectname$
{
public class $safeitemname$ : System.Web.UI.Page
{
A notable on the ASPX.CS code behind file – notice in our vstemplate file we are putting the page code behind in the App_Code directory rather than adding it with our page file. This is done primarily because most SharePoint tools, (ous included) drive SharePoint feature content off a copy from specific directories (like the 12 hive mirror) in your project and we do not ultimately want the code behind file deployed to the server, just the compiled assembly. Rather than write more logic on deplyment or another tool workaround not to copy the code files too, I simply chose to move them. Because they all get wired up right on creation with or wizard, I still get “Go To Source” functionality in VS and because of that don’t care where they are.
Third file is our VSTemplate file for this item – Study the XML and notice that we are taking the file name parameter to use it for our class name and the block at the bottom that is going to point to our wizard assembly. We are also telling Visual Studio to open the item in the editor after creation and to add this to any C# project type. The nice thing about XMl is that it is nice and human readable.
So now that we have a template, let’s code our wizard. Add a new C# class to your project called “SPItemWizard.cs”. This file will be the actual class that gets called to do the work when a new item is created.
I am not going to go through all of the code (you can downloa d the attached solution code and see it all) but I am going to run through the part that does the real work for us.
Our wizard has to derrive from IWizard and one of the methods on the IWizard interface we need to implement is the “Run Started” method. This method will get called by Visual Studio when a new item of our type is created and the context of the item will get passed to us. First we get a reference to the VS Project IDE (Automation object) and then string Dictionary with our replacements parameters. Along with that is the type of run (New item/new project) and an array of custom params that may be passed in. Below is the implementation in this sample where I do the replacements of our tokens we put in the project item templates above with the valuse I get. I also reflect the project assembly to get the token to make my SharePoint page compile against it’s code behind page and I also use the project name for the defualt code behind namespace. Since this is a sample, I also do some seriously wicked error handling J. The “GetAsmToken” method in the code below is a private method I wrote (included in the code download) that does the assembly reflection
public class SPItemWizard : IWizard
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
try
{
// Grab Our Project Name and use that for our default Namespace
EnvDTE.Project activeProject = ((EnvDTE.Project)((object[])((((EnvDTE._DTE)automationObject)).ActiveSolutionProjects))[0]);
string _projectFile = activeProject.FileName;
// Get our projects assembly information
string _assemblyToken = this.GetAsmToken(activeProject.FileName.Substring(0, _projectFile.LastIndexOf("\\")), activeProject.Name);
// Add our SharePoint template custom parameters
replacementsDictionary.Add("$spprojectname$", activeProject.Name);
replacementsDictionary.Add("$spassemblyname$", _assemblyToken);
}
catch (Exception ex)
{
MessageBox.Show("An error occurred in the Item Wzard: " + ex.Message, "SPProjectItemWizard Error!", MessageBoxButtons.OK);
}
}
The trick to making this work
To use our item templates we need to zip them and get them into the “%USER% > Documents > Visual Studio 2008 > Templates > Item Templates > Visual C# > SharePoint Item Wizard” folder. The assembly we built for our wizard also needs to be in the GAC on the development machine. To accomplish this, add a setup project to your solution and set the primary output of the wizard to be registered in the GAC. The next trick is that we don’t want to build our templates, we just want to include them. To do this, zip the folders in your soution “Item Templates” directory and place the zip files in the root of your project like this:
The next thing you need to do for ALL of the item template files is set them to have a build action of “None” and set them not to copy to the output directory either.

To include the zip files, set the build action to “Content” and then in your setup project, direct them to the folder we want them in on the client machine.
Right clicking on the setup project and viewing the File System we can point them to their home-
As you can see in the sample attached, this works for Snippets too. It can also deploy more than one template at a time. I have included a sample ASPX page and also a WebPart page to help get you started.
Check out the attached solution and let me know what you think. Modify the templates to meet your needs and enjoy new SharePoint items that actually run in a flash.
Happy SharePoint project item creation!
Attachment(s): SharePointProjectItemWizard.zip
There is a lot of information out there on using ADAM with SharePoint, but the problem I found was going to 6 different places before you actually make it work right. Hopefully this will save you some time by running through it in one place.
For the purpose of this post, I am going to assume you have a directory setup already and have already downloaded and installed ADAM on a server we will refer to as 'ADAMServer' for the remainder of this post. We will also be assuming that ADAM is running on port 5000 and that we have created a forest named 'ADAM-Forest' in our configurations below. Our other assumption is that you have a server named ‘SharePoint-Server’ where we will be configuring the SharePoint side of things. Now let's go to it...
One of the first things you will need to do is grant permissions for the account you have running SharePoint (your App Pool acct) to be able to access your ADAM store. Next, let's create some ADAM users for our little test-
o Open ADAM ADSIEdit on your server running ADAM. Connect to:
§ Server name: localhost
§ Port: 50000
§ Distinguished Name: CN=Adam-Forest,dc=forest,dc=com
§ Create a new container "Users" by right clicking on the partition folder TokenRoleProvider in the tree view, select "New" and "object" from the context menu.
§ Select Container
§ Container Name: Users
Now, let's create 3 users: 'AdamUser1', 'AdamUser2', 'AdamUser3' (Name these whatever you like) by taking the following steps for each user: (Repeat the steps for all 3 users)
o Right click "CN=Users", click New, Object, Select user and click Next
o Value: AdamUser1
o Click Next, Finish
o Right click on "CN=AdamsUser1" and click Reset Password
o Enter the password and click OK
o Double click on "CN=Adam1"
o Edit msDS-UserAccountDisabled, set the value to False and click OK
§ NOTE: The account is disabled by default – if you forget this step, you get to chase your tail for a while J.
Now, let's create a group in ADAM so we can test that as well. Create a new group called 'AdamGroup1' by doing the following steps:
o Right click "CN=Users", click New, Object
o Select group and click Next
o Value: AdamGroup1
o Click Next
o Set the groupType Value: -2147483646
o Click Next, Finish
Add AdamUser1, AdamUser2 and AdamUser3 to the Member attribute of AdamGroup1:
Double click on "CN=AdamGroup1"
Edit the member attribute by clicking on Add ADAM Account and enter the distinguished name for the user:
CN=AdamUser1,CN=Users,CN=Adam-ForestB,DC=forestb,DC=com
(Repeat for AdamUser2 and AdamUser3)
Click OK, OK to exit out of that.
Now we have ADAM setup and have users and a group to test, so we need to configure SharePoint. To be able to set the provider up, we need to modify the Central Admin web.config file to make SharePoint aware of the ADAM provider.
Editing the web.config for Central Admin, we will add the following after the <system.web> tag and save our changes:
<membership defaultProvider="ADAMMembership">
<providers>
<add name="ADAMMembership"
type="Microsoft.Office.Server.Security.LDAPMembershipProvider,
Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
server="ADAMServer"
port="50000"
useSSL="false"
userDNAttribute="distinguishedName"
userNameAttribute="cn"
userContainer="CN=Users,CN=Adam-Forest,DC=forest,DC=com"
userObjectClass="user"
userFilter="(ObjectClass=user)"
scope="Subtree"
otherRequiredUserAttributes="sn,givenname,cn" />
</providers>
</membership>
<roleManager defaultProvider="AspNetWindowsTokenRoleProvider" enabled="true"
cacheRolesInCookie="true" cookieName=".PeopleDCRole">
<providers>
<add name="LdapRole"
type="Microsoft.Office.Server.Security.LDAPRoleProvider,
Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
server="ADAMServer"
port="50000"
useSSL="false"
groupContainer="CN=Users,CN=Adam-Forest,DC=forest,DC=com"
groupNameAttribute="cn"
groupMemberAttribute="member"
userNameAttribute="cn"
dnAttribute="distinguishedName"
groupFilter="(ObjectClass=group)"
scope="Subtree"/>
</providers>
</roleManager>
So what did we just do? The top configuration section is actually defining our ADAM membership provider and telling SharePoint how to connect to it. The second entry is telling the ASP.Net role provider that ADAM exists as a role provider store, but notice the defaultProvider entry is pointing to the ‘AspNetWindowsTokenRoleProvider’ provider. That is because we don’t want to use ADAM as our primary provider, we just want to include it.
Now configure our newly ‘installed’ provider:
For our next steps, we are going to assume you have extended the web app <http://SharePoint-Server> to <http:// SharePoint-Server:8888> and set the zone to ‘Extranet’.
Edit the web.config for the <http://SharePoint-Server:8888> extranet site by adding the following configuration after the <system.web> tag and then save the file:
<membership defaultProvider="ADAMMembership">
<providers>
<add name="ADAMMembership"
type="Microsoft.Office.Server.Security.LDAPMembershipProvider,
Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
server="ADAMServer"
port="50000"
useSSL="false"
userDNAttribute="distinguishedName"
userNameAttribute="cn"
userContainer="CN=Users,CN=Adam-Forest,DC=forest,DC=com"
userObjectClass="user"
userFilter="(ObjectClass=user)"
scope="Subtree"
otherRequiredUserAttributes="sn,givenname,cn" />
</providers>
</membership>
<roleManager defaultProvider="LdapRole" enabled="true" cacheRolesInCookie="true"
cookieName=".PeopleDCRole">
<providers>
<add name="LdapRole"
type="Microsoft.Office.Server.Security.LDAPRoleProvider,
Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
server="ADAMServer"
port="50000"
useSSL="false"
groupContainer="CN=Users,CN=Adam-Forest,DC=forest,DC=com"
groupNameAttribute="cn"
groupMemberAttribute="member"
userNameAttribute="cn"
dnAttribute="distinguishedName"
groupFilter="(ObjectClass=group)"
scope="Subtree" />
</providers>
</roleManager>
NOTE: In the extranet configuration we have the default role provider set to the LDAP store since that is our default store for external users.
One last thing we need to do to make this really useful is to set a wildcard for your people picker so you can find ADAM users and groups without typing an exact match for them. To do this, simply add the wildcard entry below to your web.config files:
<PeoplePickerWildcards>
<clear />
<add key="ADAMMembership" value="*" />
</PeoplePickerWildcards>
Completing the setup in SharePoint :
In Central Admin > Application Management tab > Authentication Providers:
o Click Extranet and choose-
§ Authentication Type: Forms
§ Membership provider name: ADAMMembership
§ Role manager name: LdapRole
o Click Save
In Central Admin > Application Management tab > Policy for Web Application
· Click Add Users
· Web Application: <http://SharePoint-Server/>
· Zones: Extranet
· Click Next
· Choose Users, click the address book icon and in the ‘Find’ field, type AdamUSer1 and click the magnifying glass icon
· Double click on Adam1
· In the Find field, type AdamGroup1 and click the magnifying glass icon
· Double click on AdamGroup1 and click OK
· Choose Permissions: put a checkmark beside Full Control and click Finish
NOTE: We have now explicitly granted our LDAP user AdamUser1 and our LDAP group AdamGroup1 Full Control. You can test it by logging in as AdamUser1 and then closing IE and logging in as AdamUser3. Happy Coding!