Writing Windows SharePoint Services Server-Side controls
Overview
This post tries to describe the process of writing a .NET server-side control for Microsoft Windows SharePoint Services
There are a number of ways to write add-in code for SharePoint Technologies, the most notable being web parts.
However, these do not always help in the customisation of a site. Sometimes, we need to be able to change the
SharePoint look'n'feel and web parts do not integrate seamlessly in an area we want to control.. If we consider that
SharePoint is an ASP.NET application, we are able to use other .NET techniques to deliver the look and customisation
we want.
One of those methods is via compiled controls. Most commonly used would be user controls but we can also write
server controls
Mini-Navigator
In this post, I will be describing how I wrote a server control that I call a Mini-Navigator. See below:

This is a server control written in VB.NET. It initially outputs the logged on user information, this is because when I am sometimes testing,
it is really easy for me to forget with IE session is representing which user. Then, it should give you the current site, the parent site to you
and the children site beneath you. So, it renders out 1 level above and 1 level below and displays the appropriate icon for the site.
Hovering on the site will display the sites' Title/Description information and clicking on the site will navigate the current window to it.
If you wanted to open the site in a new window, remember to hold the <Shift> key when clicking on the site.
In the above example, if I clicked on the site "bridport", you would get:

So that’s what the control looks like, so how can we put this into a SharePoint site?
Step 1 - Building the control
This is just using one of the default templates that comes with Visual Studio .NET 2003. The control that I will be building here
will be going into the Global Assembly Cache (GAC) on my SharePoint server so I need to strongly name it.
So, open Visual Studio .NET 2003, then "File/New/Project". In your language of choice, you should see a template called
"Windows Control Library". This is the template I will be using.
The control uses the WSS Object Model (OM), se we need to add a reference to "Windows SharePoint Services"

Note: This assumes you are developing on a machine that has WSS installed already. If
not, you will need to obtain the appropriate DLLs and add them.
You will need to set up your own appropriate namespace. My code looks like:
MiniNavigatorSC.VB
Imports System.ComponentModel
Imports System.Web.UI
Imports Microsoft.SharePoint
Imports Microsoft.SharePoint.Utilities
Imports Microsoft.SharePoint.WebControls
<DefaultProperty("Text"), ToolboxData("<{0}:MiniNavigator runat=server></{0}:MiniNavigator>")> Public Class MiniNavigator
Inherits System.Web.UI.WebControls.WebControl
Dim _text As String
<Bindable(True), Category("Appearance"), DefaultValue("")> Property [Text]() As String
Get
Return _text
End Get
Set(ByVal Value As String)
_text = Value
End Set
End Property
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Protected Overrides Sub Render(ByVal output As System.Web.UI.HtmlTextWriter)
Dim oSite As SPWeb = SPControl.GetContextWeb(Context)
Dim oSubWebs As SPWebCollection = oSite.Webs
Dim oSubWeb As SPWeb
Dim sOutput As String = ""
Dim sImg As String = ""
'Output the CurrentUser so that the user is aware of who they are logged in as.
sOutput = "<table><tr><td colspan=2 class='ms-tabselected' title='Logged in as:'>"
sOutput += oSite.CurrentUser.LoginName.ToString + "</TD></TR>"
'If at a site collection, the ParentWeb object will be nothing
If (oSite.IsRootWeb) Then
'Site Collection level. The parent maybe a portal
sOutput += "<tr><td valign='top'><img src='/_layouts/images/opx16.gif' alt=''></td><td width=90% class='ms-nav a' title='This is a SharePoint Portal site'" + oSite.PortalName + "><a href='" + oSite.PortalUrl.ToString + "'>" + oSite.PortalName.ToString + "</a></td></tr>"
Else
Dim sSiteName As String = ""
If (oSite.ParentWeb.Name.ToString.Length <> 0) Then
sSiteName = oSite.ParentWeb.Name.ToString
Else
sSiteName = Right(oSite.ParentWeb.Url.ToString, oSite.ParentWeb.Url.ToString.Length - oSite.ParentWeb.Url.ToString.LastIndexOf("/") - 1)
End If
sOutput += "<tr><td><img src='/_layouts/images/" + getGif(oSite.WebTemplate.ToString, oSite.Configuration) + ".gif' alt=''></td><td width=90% class='ms-nav a' title='Site Title: " + oSite.ParentWeb.Title.ToString + " / " + oSite.ParentWeb.Description.ToString + "'><a href='" + oSite.ParentWeb.Url.ToString + "'>" + SPEncode.HtmlDecode(sSiteName) + "</a></td></tr>"
End If
'The current site
sOutput += "<tr><td><img src='/_layouts/images/" + getGif(oSite.WebTemplate.ToString, oSite.Configuration) + ".gif' alt=''></td><td class='ms-selectednav' width=90% title='You are here'>" + oSite.Title.ToString + "</td></tr>"
'The child sites
For Each oSubWeb In oSubWebs
sOutput += "<tr><td valign='top'><img src='/_layouts/images/" + getGif(oSubWeb.WebTemplate.ToString, oSubWeb.Configuration) + ".gif' alt=''></td><td width=90% class='ms-nav a' title='Site Title: " + oSubWeb.Title.ToString + " / " + oSubWeb.Description.ToString + "'><a href='" + oSubWeb.Url.ToString + "'>" + oSubWeb.Name.ToString + "</a></td></tr>"
Next oSubWeb
sOutput += "</table>"
output.Write(sOutput)
End Sub
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Private Function getGif(ByVal sType As String, ByVal iConfig As Integer)
Select Case sType
Case "MPS"
Return "MTGICON"
Case Else
Select Case iConfig
Case 0
'Team Site
Return "STSICON"
Case 1
'Blank Site
Return "MSCNTVWL"
Case 2
'Document Workspace
Return "DOCICON"
Case Else
'Set as a team site
Return "STSICON"
End Select
End Select
End Function
End Class
AssemblyInfo.VB
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("Mini-Navigator")>
<Assembly: AssemblyDescription("This is a server control that can be added to a SharePoint site to display the parent and children site information")>
<Assembly: AssemblyCompany("Bridport")>
<Assembly: AssemblyProduct("")>
<Assembly: AssemblyCopyright("")>
<Assembly: AssemblyTrademark("")>
<Assembly: CLSCompliant(True)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("ED83C45C-4EBC-4a51-89AE-E95EB38C4AB1")>
' Version information for an assembly consists of the following four values:
'
'
Major Version
'
Minor Version
'
Build Number
'
Revision
'
' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
<Assembly: AssemblyVersion("1.0.0.0")>
<Assembly: AssemblyKeyFile("../../MiniNav.snk")>
Click here to download the project. The project contains the files previously mentioned plus the MiniNavigatorSCCS.cs which is the C# equivalent code for all you C#'ers out there!
Note: you will have to add your own KeyFile using SN -K called MiniNav.SNK and located in the
project directory at the same level as the .VB files
Now we have the control built and it is ready to deploy to the SharePoint Server
So, what does the code do?
Nothing hard or tricky really, just gets the site context from:
Dim oSite As SPWeb = SPControl.GetContextWeb(Context)
Then this context is used to get the Current User information from the WSS OM property
oSite.CurrentUser.LoginName.ToString
We then just work through the object model to get the parent and children site information.
To get the correct icons for the workspace, we use:
oSubWeb.WebTemplate and oSubWeb.Configuration
I do make an assumption here that you are using the default site definitions. If you have modified these,
you may need to change this piece of code
Step 2 - Deploy the control
As I may want to use the control on any site on my SharePoint server, over a number of portals,
I copy the built DLL, MiniNavigator.DLL, to the server GAC.
The control sits on a WSS default page, so we have to edit the sites' default.ASPX. There are a number of
ways to update this file using many editors such as FrontPage2003 but… I normally edit the page by
first mapping a web folder to the site

(REALLY IMPORTANT NOTE: Make sure you keep a copy of the original DEFAULT.ASPX file before
you edit it just in case!!!!)
I would then drag'n'drop the default.aspx file to my desktop and open it using NotePad and add the control
reference thus:

The line we are interested in is:
….
<%@ Register Tagprefix="NMB" Namespace="MiniNavigator" Assembly="MiniNavigator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7dd16f83bffd70b1" %>
….
With the reference in, we now need to place the control at the appropriate location within the ASPX file. As, this
control is to be displayed on the navigation area of the site, I put it as shown below:

Again, the lines I added are:
…
<TR>
<TD style="padding-left:0px;padding-right:0px">
<img width=1px src='/_layouts/images/blank.gif' ID='100' alt='Icon' border=0>
<NMB:MiniNavServerControl id="NigelSC1" runat="server" />
</TD>
</TR>
…
NOTE: You may find it easier to use FrontPage2003 to actually do this manipulation as it is easier to locate
the actual HTML where you want to drop the control
Now, save the file and drag it back to the WSS web folder where you originally dragged the file from, overwriting the
original file. (This will create a custom version of the file and it is handled slightly differently by SharePoint from
default sites without customisation. These files are more commonly called "one-off" or "unghosted")
The final part of the deployment is to update the appropriate web.config file to allow the control to
render. So, open the web.config and add the reference for the control into the <SafeControls> section:

The line is
…
<SafeControl Assembly="MiniNavigator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7dd16f83bffd70b1"
Namespace="MiniNavigator" TypeName="*" Safe="True" />
…
Now do an IISRESET and view the site!