HowTo: Add SPList based on a custom template

This blogpost will cover two methods that I use when dealing with custom SharePoint lists and document libraries.
First I will step through a more basic approach and then I will use a more advanced scenario dealing with a custom form page (DispForm in this example).

Basic scenario

When provisioning instances of lists (that will support a custom content type) in SharePoint I usually use either the Generic list template or the Document list template depending on whether I want a doc lib or just a list (base type 0 or 1).

I usually go about it like this:

1. Create features for the contenttype (sitecollection level) and the list instance (this one a with feature receiver)

2. Add feature receiver code
Here is the code that I use in the feature receiver:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

SPWeb web = properties.Feature.Parent as SPWeb;
SPList instance = web.FindListByName("MyCustomList");

if (instance == null)
{

Guid id = web.Lists.Add("MyCustomList", "An instance of my custom list", SPListTemplateType.GenericList);
instance = web.Lists[id];

}

instance.RemoveContentType(SPBuiltInContentTypeId.Item);
instance.AddContentType(new SPContentTypeId("0x010008F15D6B2D0A454BA95AA5D91E21416E"));
instance.Title = "My custom list";

instance.Update();

}

a) add an instance of the specified list template
b) add my custom content type to list and customize it further if necessary

OK, first of all we have a couple of extension methods here:

SPWeb.FindListByName(which I have blogged about before)
SPList.RemoveContentType() and SPList.AddContentType() look like this (with some helper methods):

public static void AddContentType(this SPList instance, SPContentTypeId contentTypeId)
{

if (instance.ContentTypesEnabled == false)
{

instance.ContentTypesEnabled = true;

}

if (instance.ContainsContentType(contentTypeId) == true)
{

return;

}

SPWeb web = instance.ParentWeb; // no need to dispose

try
{

SPContentType ct = web.AvailableContentTypes[contentTypeId];

if (web.ContentTypes.BestMatch(contentTypeId).Parent.Equals(contentTypeId) == false)
{

instance.ContentTypes.Add(ct);
instance.Update();

}

}

catch (Exception e)
{

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture,
"Failed to add contenttype id '{0}' to list '{1}' on '{2}'.",
contentTypeId.ToString(), instance.Title, web.Url), e);

}

}

public static void RemoveContentType(this SPList instance, SPContentTypeId contentTypeId)
{

SPContentTypeId ctId = instance.ContentTypes.BestMatch(contentTypeId);

if (ctId.Parent.Equals(contentTypeId) == false)
{

return;
}

RemoveContentType(instance, ctId, contentTypeId);

}

private static void RemoveContentType(SPList instance, SPContentTypeId contentTypeId, SPContentTypeId originalContentTypeId)
{

try
{

if (contentTypeId.Parent.Equals(originalContentTypeId) == true)
{

instance.ContentTypes.Delete(contentTypeId);
instance.Update();

}
else
{

if (contentTypeId.Parent.Equals(contentTypeId) == false)
{

RemoveContentType(instance, contentTypeId.Parent, originalContentTypeId);

}

return;

}

}
catch (Exception e)
{

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture,
"Failed to remove contenttype id '{0}' from list '{1}' on '{2}'.",
contentTypeId.ToString(), instance.Title, instance.ParentWeb.Url), e);

}

}

public static bool ContainsContentType(this SPList instance, SPContentTypeId contentTypeId)
{

bool contains = false;

SPContentTypeId bm = instance.ContentTypes.BestMatch(contentTypeId);
contains = bm.Parent.Equals(contentTypeId);

return contains;

}

The advanced scenario

Sometimes though, I need more control over my list than I can have by just using one of the built in templates and then I go about crafting my own list template. I always prefer doing stuff in code versus CAML so I usually do as little as possible in the schema file and customize my list through code instead. Here´s a step-by-step on how I do that.

1. Create features for the contenttype (sitecollection level) and the list template (this one a with feature receiver).

a) Add the elements.xml file to the list template feature and it will look something like this:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListTemplate
Name="MyCustomDocLib"
DisplayName="My Custom Doc Lib"
Description="My Custom Doc Lib"
Hidden="TRUE"
BaseType="1"
Type="10001"
SecurityBits="11"
OnQuickLaunch="TRUE" />
</Elements>

b) Add minimal schema.xml and customize it to fit my needs.

Like I said, I´m not a big fan of CAML (and especially the schema and onet files.
This i why I use a minimalistic schema file (as little as possible) and then customize the list through code. (That is how I do it, if you like the CAML way it´s OK to)

The minimal schema.xml looks like this:

<?xml version="1.0" encoding="utf-8"?>

<List xmlns:ows="Microsoft SharePoint" Url="Shared Documents" BaseType="1" xmlns="http://schemas.microsoft.com/sharepoint">
<MetaData>
<ContentTypes>
<ContentTypeRef ID="0x01"> <!--ITEM-->
<Folder TargetName="Item" />
</ContentTypeRef>
<ContentTypeRef ID="0x0120" /> <!--FOLDER-->
</ContentTypes>
<Fields>
<Field Type="Text" Name="Title" DisplayName="Name" Required="TRUE" />
</Fields>
<Views>
<View BaseViewID="0" Type="HTML" WebPartZoneID="Main" DisplayName="All Items" DefaultView="TRUE" MobileView="True"
MobileDefaultView="False" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/issues.png" Url="AllItems.aspx">
<ViewStyle ID="17"/>
<RowLimit Paged="TRUE">100</RowLimit>
<Toolbar Type="Standard" />
<ViewFields>
<FieldRef Name="Title"/>
</ViewFields>
<Query>
<OrderBy>
<FieldRef Name="Title"/>
</OrderBy>
</Query>
</View>
</Views>
<Forms>
<Form Type="DisplayForm" Url="Forms/DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="EditForm" Url="Forms/EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="Forms/Upload.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
</Forms>
</MetaData>
</List>

c) Add form page
I usually need one or more custom form pages (DispForm etc). If need to customize views I usually do that by code but of course you could use the UI to do that and then extract the views (aspx pages) to include them in the feature (I will not show that however).
So, in this example I´m going to add a custom DispForm so my feature will look like this:

d) Customize  schema to support form page

I will also customize the schema.xml to reflect this by changing:

<Form Type=”DisplayForm” Url=”Forms/DispForm.aspx” WebPartZoneID=”Main” />

Note: I have removed the SetupPath attribute.

2. Add feature receiver code
Here is the code that I use in the feature receiver:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

SPWeb web = properties.Feature.Parent as SPWeb;
SPList instance = web.FindListByName("MyCustomDocLib");

if (instance == null)
{

SPListTemplate template = web.FindListTemplateByName("MyCustomDocLib");

if (template != null)
{

Guid id = web.Lists.Add("MyCustomDocLib", "List containing documents", template);
instance = web.Lists[id];

}
}

instance.RemoveContentType(SPBuiltInContentTypeId.Item);
instance.RemoveContentType(SPBuiltInContentTypeId.Document);
instance.AddContentType(new SPContentTypeId(MyCustomDocument));
instance.Title = "My custom document library";
instance.CustomizeDefaultView(new Guid[]
{

SPBuiltInFieldId.LinkFilename

});

instance.Update();

}

a) find the custom template (extension method)
b) add an instance of the specified list template
b) add my custom content type to list, remove item and document,  and <a href=”https://johanleino.wordpress.com/2009/08/10/customize-defaultview-extension-method/&#8221; target=”_blank”>customize the default view.</a>

The extension method for finding the template by name (as opposed to display name) looks like:

public static SPListTemplate FindListTemplateByName(this SPWeb web, string name)
{

var query = from SPListTemplate template in web.ListTemplates
where template.InternalName.Equals(name, StringComparison.InvariantCulture)
select template;

return query.SingleOrDefault();

}

So when I have added a document to my new list and pull up the display form this is what you see:

NOTE: OK, so I just added a custom text to show that the template has in fact picked up that I wan´t to use my own form page.

UPDATE 091026:
Download the code from here –>

Advertisements

,

  1. #1 by gianluca on October 21, 2009 - 16:30

    hi, can you upload the complete project files?

    • #2 by Johan Leino on October 21, 2009 - 18:40

      Sure, ASAP. I´m in Vegas now for the SPC 2009 but as soon as I get home I´ll look into that.

    • #3 by Johan Leino on October 26, 2009 - 21:02

      @Gianluca – I´ve uploaded the code to my skydrive. Enjoy!

  2. #4 by MrBarns on February 1, 2010 - 20:56

    Hey, great blog…but I don’t understand how to add your site in my rss reader. Can you Help me, please 🙂

  1. Sharepoint Custom Document Library « Sladescross's Blog
  2. 2010 in review « Johan Leino

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: