Custom controls are more lightweight and must be compiled into an assembly DLL before being deployed to the front-end Web server.
Constructing Pages with Custom Controlsdevelopers often choose to create custom controls by deriving from a richer class named WebControl, which inherits from the Control class.
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
namespace CustomSitePages {
public class CustomControl1 : WebControl {
protected override void RenderContents(HtmlTextWriter output) {
SPWeb site = SPContext.Current.Web;
output.Write("Current Site: " + site.Title);
output.Write("
");
output.Write("Current Site ID: " + site.ID.ToString());
} } }
You have two choices as to where to deploy an assembly DLL that contains a custom control when you want to use it in a site page. First, you can compile the assembly DLL with a strong name and install it in the Global Assembly Cache. Alternatively, you can deploy the assembly DLL by placing it inside the root directory of the hosting Web application inside a nested directory named bin. Note that when you plan to deploy the assembly DLL with custom controls in the bin directory, you have the option of compiling it with or without a strong name.Once the assembly DLL with the custom control is properly deployed, you can then reference it within a page template by using the ASP.NET Register directive. The following code example
displays a page template that uses the custom control shown previously.
%@ Page MasterPageFile="~masterurl/default.master"
meta:progid="SharePoint.WebPartPage.Document" %>
%@ Register Assembly="CustomSitePages, ... "
Namespace="CustomSitePages" TagPrefix="CustomSitePages" %>
asp:Content ID="main"
ContentPlaceHolderId="PlaceHolderMain"
runat="server">
h3>A custom control example/h3>
CustomSitePages:CustomControl1 ID="cc1" runat="server" />
/asp:Content>
This TagPrefix value is then used to instantiate instances of the control within the page.
CustomSitePages:CustomControl1 ID="cc1" runat="server" />
When you navigate to .aspx, you should see that the page renders the output of the
control so that it is visible to the user. However, this works only while the hosting page remains in a ghosted state. Remember that customized pages allow only for controls that are registered as safe controls. If a user customizes Page02.aspx with the SharePoint Designer, the page begins to execute in safe mode, and the presence of a control that is not registered as a safe control results in the error message.
To fix this problem, you must add a custom SafeControl entry to the hosting Web
application’s web.config file. You can accomplish this by adding a SafeControl entry that looks like the following:
SafeControl
Assembly="CustomSitePages, ..."
Namespace="CustomSitePages"
TypeName="CustomControl1"
/>
Constructing Pages with User ControlsThe ASP.NET runtime provides the functionality to parse these .ascx files at run time and compile them into assembly DLLs just as it does for .aspx files.
Let’s examine the source file for a simple user control. The following example of an .ascx file creates a simple user interface with a command button and a label and adds in an event handler to provide the classic “Hello World” functionality.
%@ Control Language="C#" %>
script runat="server">
protected void cmdButton1_Click(object sender, EventArgs e) {
lblStatus.Text = "Hello, World";
}
/script>
asp:Button ID="cmdAddCustomer" runat="server" Text="Add Customer"
OnClick="cmdAddCustomer_Click" />
br/>
asp:label id="lblStatus" runat="server" text="">
User controls are always loaded from the file system of the front-end Web server and
compiled into assembly DLLs. Furthermore, user controls can be copied to the front-end Web server only by someone with farm-level administrative privileges. For these reasons, you can assume that you can always write in-line code in an .ascx file.
In Chapter 2, you were introduced to the virtual _layouts directory and learned that this was the proper place to deploy application pages. WSS provides a similar virtual directory for deploying user controls. Inside the TEMPLATE directory resides a nested directory named CONTROLTEMPLATES.
Fortunately, you don’t have to worry about this if you deploy your custom user controls inside the virtual _controltemplates directory because the standard web.config file for a Web application already contains the following SafeControl entry:
SafeControl
Src="~/_controltemplates/*"
IncludeSubFolders="True"
Safe="True"
AllowRemoteDesigner="True"
/>
Now that you have seen how to create and properly deploy a user control, the final step is constructing a page template that references the .ascx file and creates an instance. However, the process is different with user controls because the Register
directive requires an src attribute that points to the virtual path of the target .ascx file.
%@ Page MasterPageFile="~masterurl/default.master"
meta:progid="SharePoint.WebPartPage.Document" %>
%@ Register TagPrefix="luc" TagName="UserControl1"
src="~/_controltemplates/Litware/UserControl1.ascx" %>
asp:content runat="server" contentplaceholderid="PlaceHolderMain">
luc:usercontrol1 id="id1" runat="server">
/asp:Content>
Designing Web Part PagesWeb Parts go even further to allow individual users to add personalization changes that are seen only by them.WSS provides the underlying mechanisms to track all of this customization and personalization inside the content database along with all of the other site-related data.
All of the data for tracking Web Part instances and their customization and personalization data are kept in separate tables inside the content database.This means that a Web Part page can remain in a ghosted state even though users are continually adding, customizing, and personalizing the Web Parts within its zone.
To create a Web Part page in an ASP.NET 2.0 application, you must create an .aspx page that contains exactly one instance of a control named WebPartManager and one or more WebPartZone controls. The WebPartManager is responsible for managing the lifetime of Web Part instances as well as serializing Web Part–related data so that they can be stored and retrieved from the tables in the ASP.NET services database.
WSS relies on a specialized control named SPWebPartManager that derives from the ASP.NET 2.0 WebPartManager control. The SPWebPartManager control overrides the standard behavior of the WebPartManager control to persist Web Part data inside the WSS content database instead of inside the ASP.NET services database.
Two things must be done when creating a page template for a Web Part page. The first is to inherit from the WebPartPage class that is defined inside the Microsoft.SharePoint.dll assembly. The second is to add one or more WebPartZone controls.To add WebPartZone controls to a page template, you must add a Register directive that imports all of the controls from the Microsoft.SharePoint.dll assembly defined in the Microsoft.SharePoint.WebPartPages namespace as shown in the following page template definition.
%@ Page Language="C#" MasterPageFile="~masterurl/default.master"
Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"
meta:progid="SharePoint.WebPartPage.Document" %>
%@ Register Tagprefix="WebPartPages"
Namespace="Microsoft.SharePoint.WebPartPages"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
asp:content id="main" runat="server" contentplaceholderid="PlaceHolderMain">
h3>Custom Web Part Page/h3>
table width="100%">
tr>
td valign="top" style="width:50%">
/td>
td valign="top" style="width:50%">
WebPartPages:WebPartZone ID="Right" runat="server"
FrameType="TitleBarOnly"
Title="Right Web Part Zone" />
/td>
/tr>
/table>
/asp:Content>
There are two common techniques for adding a Web Part instance to a Web Part zone. The first technique involves a declarative approach used inside a feature in which you define an AllUsersWebPart element inside a File element. The following example demonstrates the File element that is used in the CustomSitePages project to provision the Web Part page named WebPartPage02.aspx.
Elements xmlns="http://schemas.microsoft.com/sharepoint/">
Module Path="PageTemplates" Url="SitePages" >
File Url="WebPartPage.aspx" Name="WebPartPage03.aspx" Type="Ghostable" >
AllUsersWebPart WebPartZoneID="Left" WebPartOrder="0">
Assembly>Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart
Title>Yet Another Web Part is Born/Title>
FrameType>TitleBarOnly
cewp:Content>
This Web Part was added through declarative logic
/cewp:Content>
/WebPart>
]]>
/AllUsersWebPart>
AllUsersWebPart WebPartZoneID="Right" WebPartOrder="0">
Assembly>Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
TypeName>Microsoft.SharePoint.WebPartPages.ImageWebPart
FrameType>None
Title>Watch My Gears Run/Title>
iwp:ImageLink>/_layouts/images/GEARS_AN.GIF
/WebPart>
]]>
/AllUsersWebPart>
/File>
/Module>
/Elements>
The second technique for adding a Web Part instance to a Web Part page involves writing code against the WSS object model. An example of this type of code is supplied in the FeatureActivated event handler for the CustomSitePages project. The code obtains a reference to the SPFile object associated with WebPartPage03.aspx and uses an SPLimitedWebPart-Manager object to add a new Web Part instance to a particular target zone.
public override void FeatureActivated(
SPFeatureReceiverProperties properties) {
// acquire objects for site, page and limited Web Part Manager
SPWeb site = (SPWeb)properties.Feature.Parent;
SPFile page = site.GetFile("SitePages/WebPartPage03.aspx");
SPLimitedWebPartManager mgr;
mgr = page.GetLimitedWebPartManager(PersonalizationScope.Shared);
// add Web Part to Right Zone
ImageWebPart wp1 = new ImageWebPart();
wp1.ChromeType = PartChromeType.None;
wp1.ImageLink = @"/_layouts/images/IPVW.GIF";
mgr.AddWebPart(wp1, "RightZone", 0);
}
Master PagesThe pages that link to a master page are known as content pages. Standard master page is deployed during the standard WSS installation on the file system of the front-end Web server at the following path (there is a line break in the file path to make it more readable).
C:\Program Files\Common Files\Microsoft Shared
\web server extensions\12\TEMPLATE\GLOBAL\default.master
Whenever you create a new site, WSS provides provisioning instructions to create the Master Page gallery and provision an instance of default.master within the site by using a standard site-relative path.
/_catalogs/masterpage/default.master
The default.master page contains the basic layout for a site page including elements such as HTML, HEAD, BODY, and FORM. Within these standard HTML elements, you will find three important types of components:
■ Controls for links, menus, icons, and navigation components
■ Named placeholders
■ Delegate controls
The top link bar is defined by using a WSS-specific control of type AspMenu along with a SiteMapDataSource control that is configured to point to the standard SPNavigationProvider component. The Quick Launch menu is defined in the same way. The major difference between the two is that the SiteMapDataSource for the top link bar is configured with a StartingNodeUrl attribute with a value of sid:1002, whereas the Quick Launch menu is configured with a StartingNodeUrl attribute with a value of sid:1025.
The next question you should be asking is what the significance is between 1002 and 1025. It has to do with the data stored in the content database for tracking navigation nodes. The top node for the top link bar has an ID of 1002, and the top node to the Quick Launch menu has an ID of 1025.
The CustomSitePages feature provides code in the FeatureActivated event handler to add navigation nodes to construct a custom drop-down menu in a fashion that is not possible to replicate through the user interface. Examine the following code and observe how it creates SPNavigationNode objects and adds them to the collection of nodes that define the structure for the top link bar.
public override void FeatureActivated(SPFeatureReceiverProperties properties) {
// get a hold of current site in context of feature activation
SPWeb site = (SPWeb)properties.Feature.Parent;
SPNavigationNodeCollection topNav = site.Navigation.TopNavigationBar;
// create dropdown menu for custom site pages
SPNavigationNode DropDownMenu1;
DropDownMenu1 = new SPNavigationNode("Site Pages", "", false);
topNav[0].Children.AddAsLast(DropDownMenu1);
// add navigation nodes to create menu items
DropDownMenu1.Children.AddAsLast(
new SPNavigationNode( "Site Page 1",
"SitePages/Page01.aspx"));
DropDownMenu1.Children.AddAsLast(
new SPNavigationNode("Site Page 2",
"SitePages/Page02.aspx"));
}
Delegate ControlsLike a placeholder, a delegate control can optionally supply default content that is used until a substitution is performed.
One major difference when compared to placeholders is that the substitution mechanism for replacing the contents of a delegate control is driven through feature activation. Therefore, you can replace what’s defined inside a delegate control in default.master without requiring any changes to default.master or the site pages that link to it. All you need to do is define a Control element within a feature and then activate that feature.
SmallSearchInputBox delegate control by referencing a built-in user control with the standard WSS search area content.
control
Id="SmallSearchInputBox"
Sequence="100"
ControlSrc="~/_controltemplates/searcharea.ascx"
/>
Assume that you want to get rid of the standard search area content and replace it with your own custom content for a particular business solution. That’s what delegate controls were designed for.
If you want to replace a delegate control, such as the WSS search area, with your own customized version, you start by adding a Control element to a feature. The Control element should have an ID value of SmallSearchInputBox. The Control element should also have a Sequence number smaller than any other active Control element pointing to the same ID. The following code demonstrates how the Control element is defined inside the elements.xml file of the CustomBranding feature.
Note that this Control element has a sequence number of 10, which is smaller than the Control element defined in ContentLightup with a Sequence number of 100. Once the CustomBranding feature is activated within a site collection, all of the site pages that link to default.master replace the standard WSS search area with whatever content you have defined inside the custom user control named LitwareSearchArea.ascx. The following code defines a starting point for creating a custom user control that supplies custom search behavior.
%@ Control Language="C#" %>
script runat="server">
protected void cmdRunSearch_Click(object sender, EventArgs e) {
// add customer search here
txtSearchText.Text = "Hello";
}
/script>
table>
tr>
td>
td>
/tr>
/table>
Creating a Custom Master Page Template
Creating a custom master page template involves several steps. First, you must create the master page template itself. Second, you must create a custom feature that provisions an instance of this master page template inside the Master Page gallery for a specific site. Finally, you need to add some code to redirect site pages to use your custom master page instead of using default.master. The Visual Studio project named CustomBranding provides a working sample that demonstrates how all of the pieces fit together.
You can create a custom template by using two different approaches. First, you can make a copy of default.master and then modify it according to taste. A second approach involves starting from scratch so that you can design the exact HTML layout you’re looking for. The custom master page template used in the CustomBranding project is named Litware.master. The Litware.master template is a variation on the default.master template with changes to allow for fly-out menus on both the top link bar and Quick Launch menu. The CustomBranding feature includes a Module element that has been designed to provision an instance of the Litware.master page template into the Master Page gallery of the top-level site.
Module Name="MasterPages" List="116" Url="_catalogs/masterpage">
File Url="Litware.master" Type="GhostableInLibrary" />
/Module>
However, this Module targets the Master Page gallery, which is a special type of document library. Therefore, the Module is defined with a List attribute of 116, which is the list type identifier for the Master Page gallery. The Url attribute for this Module is defined with a value of _catalogs/masterpage,
which is the standard site-relative path to the Master Page gallery.
We have reviewed the steps involved in creating a master page template and provisioning an instance of it in the Master Page gallery of the top-level site. The next step involves redirecting all site pages within a site to link to this provisioned instance of our custom master page template. To understand the technique for accomplishing this, take a closer look at the MasterPageFile attribute defined within a Page directive. Examine the following page template, which is defined to link to a target master page by using a special syntax in the form of ~masterurl/default.master.
%@ Page MasterPageFile="~masterurl/default.master" %>
asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
Custom content goes here
/asp:Content>
You can redirect any site page that uses this token by acquiring an SPWeb reference to the current site and then updating the MasterUrl property.
SPWeb site = SPContext.Current.Web;
string MasterUrlPath = site.ServerRelativeUrl;
if (!MasterUrlPath.EndsWith(@"/"))
MasterUrlPath += @"/";
MasterUrlPath += @"_catalogs/masterpage/Litware.master";
site.MasterUrl = MasterUrlPath;
site.Update();
Once you
activate the CustomBranding feature, you can navigate to the application page named
CustomBrand.aspx by using a custom menu item that is added to the Site Settings menu. This application page provides a command button that allows the user to execute the following code.
protected void cmdApplyCustomBrand_Click(object sender, EventArgs e) {
SPWeb site = SPContext.Current.Site.RootWeb
string MasterUrlPath = site.ServerRelativeUrl;
if (!MasterUrlPath.EndsWith(@"/"))
MasterUrlPath += @"/";
MasterUrlPath += @"_catalogs/masterpage/Litware.master";
ApplyCustomBrand(MasterUrlPath, site);
}
protected void ApplyCustomBrand(string MasterUrlPath, SPWeb site) {
site.MasterUrl = MasterUrlPath;
site.Update();
// use recusion to update all child sites in site collection
foreach (SPWeb child in site.Webs) {
ApplyCustomBrand(MasterUrlPath, child);
}
}
The file that defines the majority of the standard CSS classes used by pages in WSS is named core.css.
SPWeb site = SPContext.Current.Web;
site.ApplyTheme("");
site.AlternateCssUrl = "/_layouts/1033/STYLES/Litware/LitwareBrand.css";
site.Update();