If you've been doing SharePoint development for some time, you have undoubtedly used custom content types at some point. Simply stated, a content type defines the attributes of a list item, a document, or a folder. When you create a new custom list and you add or remove columns from the list, you are essentially defining a content type for the list. This is an example of content types at the list level, and list content types are derived from content types defined at the site collection level.
When defining content types at the site collection level, the power in this approach is even more apparent. For example; you can create a content type for a specific document template and associate that content type with multiple document libraries. This allows you to manage the template of the document and its usage, regardless of where it may be used on your site.
While this is truly wonderful for the management and propagation of content types in your site, it does fall short in one way: there's not a straightforward way from the SharePoint Web UI to find occurrences of where your custom content type is actually being used in your site collection. However, as with almost all things SharePoint; whatever is left unaddressed by the UI can almost always be achieved by using the object model.
The SPContentTypeUsage Class
The SPContentTypeUsage class is used to track where a content type is used as the basis for another content type. The static/shared GetUsages method returns a list of SPContentTypeUsage objects that contain information about the content type usage, such as the ID, URL and whether or not the URL is the URL to a list.
Consider the following scenario:
It has been asked that we obtain a collection of all lists in our site collection that use a specific content type. The name of the content type may change, so we must query this information specifically by content type ID.
While there are a few different ways we could accomplish this task, we are going to create a class that will query this information and could then be used in tandem with a data source control such as the ObjectDataSource. The class definition is shown below (please note the ContentTypeInfo object is a custom class for storing information about the a content type):
public class ContentTypeData
{
/* .ctors */
public ContentTypeData()
{
}
public ContentTypeData(Guid siteID)
{
this.SiteId = siteID;
}
/* Properties */
public Guid SiteId
{
get;
set;
}
/* Methods */
public Collection<ContentTypeInfo> SelectContentTypes()
{
Collection<ContentTypeInfo> retVal = null;
/* PRECONTRACT: The SiteId property must be set */
if (this.SiteId == Guid.Empty)
{
throw new InvalidOperationException("The SiteId property cannot be an empty Guid");
}
/* Begin */
retVal = new Collection<ContentTypeInfo>();
using (var site = new SPSite(this.SiteId))
{
using (var web = site.OpenWeb())
{
foreach (SPContentType item in web.ContentTypes)
{
retVal.Add(
new ContentTypeInfo
{
CTypeID = item.Id.ToString(),
Name = item.Name
});
}
}
}
return retVal;
}
public IList<SPContentTypeUsage> SelectUsages(string cTypeID)
{
IList<SPContentTypeUsage> retVal = null;
SPContentTypeId cTypeId = default(SPContentTypeId);
/* PRECONTRACT: The SiteId property must be set */
if (this.SiteId == Guid.Empty)
{
throw new InvalidOperationException("The SiteId property cannot be an empty Guid");
}
/* Begin */
if (!string.IsNullOrEmpty(cTypeID))
{
cTypeId = new SPContentTypeId(cTypeID);
using (var site = new SPSite(this.SiteId))
{
using (var web = site.OpenWeb())
{
/* Find lists using the content type */
retVal = SPContentTypeUsage.GetUsages(web.ContentTypes[cTypeId]);
}
}
}
else
{
retVal = new List<SPContentTypeUsage>();
}
return retVal;
}
}
Now that we have the code, we need only create an instance of an ObjectDataSource that uses our data class. We will create an event handler for the ObjectCreating event so that we can use the parameterized constructor for our data class. The implementation below shows how we could create an instance of the ObjectDataSource in a Web Part, which could then be used as the data source for any data bound controls on the Web Part. Much of the implementation was removed for brevity. The full code is available for download.
public class DisplayUsages
: WebPart
{
protected ObjectDataSource odsContentTypes;
protected void ContentTypeData_Creating(object sender, ObjectDataSourceEventArgs e)
{
e.ObjectInstance = new ContentTypeData(SPContext.Current.Site.ID);
}
protected override void CreateChildControls()
{
this.Controls.Clear();
base.CreateChildControls();
/* Create the Data Source Controls */
this.odsContentTypes = new ObjectDataSource();
this.odsContentTypes.ID = "odsContentTypes";
this.odsContentTypes.SelectMethod = "SelectContentTypes";
this.odsContentTypes.TypeName = typeof(ContentTypeData).AssemblyQualifiedName;
this.odsContentTypes.ObjectCreating += new ObjectDataSourceObjectEventHandler(this.ContentTypeData_Creating);
this.Controls.Add(this.odsContentTypes);
}
}
The code download demonstrates the topics covered in this post by using a DropDownList bound with the content types from the site collection and a GridView that displays the usages for the selected content type.