Home > Object Model, Share Point > SharePoint: How to add Content Type programmatically

SharePoint: How to add Content Type programmatically

September 16th, 2011 Admin Leave a comment Go to comments

     To add a new Content Type through the SharePoint Object Model I’ve developed a few auxiliary methods and classes. The main class is ContentType class. It contains information used for specifying the required properties of the SPContentType object being added.

public class ContentType
{
    public string         ParentContentTypeName { get; set; }
    public string         Name                  { get; set; }
    public List<FieldRef> FieldRefs             { get; set; }

    public ContentTypeFormUrls   FormUrls       { get; set; }
    public ContentTypeProperties Properties     { get; set; }
    public SPContentTypeId       Id             { get; set; }
}

The FieldRefs property represents a collection of fields that have to be added to the Content Type. The other property names are self-descriptive.

The ContentType class makes reference to classes like FieldRef, ContentTypeFormUrls and ContentTypeProperties.

public class FieldRef
{
    public Guid?  ID                 { get; set; }
    public string Name               { get; set; }
    public string DisplayName        { get; set; }
    public bool?  IsRequired         { get; set; }
    public bool?  IsHidden           { get; set; }
    public bool?  IsReadOnly         { get; set; }

    public string Aggregation        { get; set; }
    public string Customization      { get; set; }
    public string PIAttribute        { get; set; }
    public string PITarget           { get; set; }
    public string PrimaryPIAttribute { get; set; }
    public string PrimaryPITarget    { get; set; }
    public string Node               { get; set; }
}

public class ContentTypeFormUrls
{
    public string DisplayFormUrl { get; set; }
    public string NewFormUrl     { get; set; }
    public string EditFormUrl    { get; set; }
}

public class ContentTypeProperties
{
    public string Description                 { get; set; }
    public string Group                       { get; set; }
    public bool?  IsHidden                    { get; set; }
    public bool?  IsReadOnly                  { get; set; }
    public string NewDocumentControl          { get; set; }
    public bool?  RequireClientRenderingOnNew { get; set; }
    public bool?  IsSealed                    { get; set; }
}

The main method is AddContentType:

public static void AddContentType(SPWeb spWeb, SPList spList, ContentType contentType)
{
    // get appropriate parent content type
    SPContentType parentContentType = spWeb.AvailableContentTypes[contentType.ParentContentTypeName];
    if (parentContentType != null)
    {
        if (spList.ContentTypes[contentType.Name] == null)
        {
            spList.ContentTypesEnabled = true;

            // create new content type
            SPContentType spContentType = new SPContentType(parentContentType, spList.ContentTypes, contentType.Name);
            // set content type id
            SetContentTypeId(spContentType, contentType.Id);
            // set content type properties
            SetContentTypeProperties(spContentType, contentType.Properties);

            // add fields to conent type
            foreach (FieldRef fieldRef in contentType.FieldRefs)
            {
                if (spList.Fields.ContainsField(fieldRef.Name))
                {
                    SPField targetField = fieldRef.ID.HasValue ? spList.Fields[fieldRef.ID.Value] : spList.Fields.GetFieldByInternalName(fieldRef.Name);
                    if (targetField != null && !spContentType.Fields.ContainsField(fieldRef.Name))
                    {
                        SPFieldLink fLink = new SPFieldLink(targetField);
                        SetFieldRefProperties(fLink, fieldRef);
                        spContentType.FieldLinks.Add(fLink);
                    }
                }
                else
                    Console.Write(string.Format("Couldn't find field {0} in the list {1}", fieldRef.Name, spList.Title));
            }

            // set content type form urls
            SetContentTypeFormUrls(spContentType, contentType.FormUrls);

            // save changes
            spList.ContentTypes.Add(spContentType);
            spContentType.Update();
            spList.Update();
        }
        else
            Console.Write(string.Format("Content type {0} already exists in the list {1}", contentType.Name, spList.Title));
    }
    else
        Console.Write(string.Format("Couldn't find the parent content type {0}", contentType.ParentContentTypeName));
}

It utilizes SetContentTypeFormUrls, SetContentTypeProperties, SetFieldRefProperties and SetContentTypeId (you can read about the last method in detail in my other post – How to set Id to just created SPContentType).

public static void SetContentTypeFormUrls(SPContentType spContentType, ContentTypeFormUrls contentTypeFormUrls)
{
    if (contentTypeFormUrls == null)
        return;

    if (contentTypeFormUrls.NewFormUrl != null)
        spContentType.NewFormUrl = contentTypeFormUrls.NewFormUrl;

    if (contentTypeFormUrls.DisplayFormUrl != null)
        spContentType.DisplayFormUrl = contentTypeFormUrls.DisplayFormUrl;

    if (contentTypeFormUrls.EditFormUrl != null)
        spContentType.EditFormUrl = contentTypeFormUrls.EditFormUrl;
}

public static void SetContentTypeProperties(SPContentType spContentType, ContentTypeProperties cntProps)
{
    if (cntProps == null)
        return;

    if (cntProps.Description != null)
        spContentType.Description = cntProps.Description;
    if (cntProps.Group != null)
        spContentType.Group = cntProps.Group;
    if (cntProps.IsHidden != null)
        spContentType.Hidden = cntProps.IsHidden.Value;
    if (cntProps.IsReadOnly != null)
        spContentType.ReadOnly = cntProps.IsReadOnly.Value;
    if (cntProps.IsSealed != null)
        spContentType.Sealed = cntProps.IsSealed.Value;
    if (cntProps.NewDocumentControl != null)
        spContentType.NewDocumentControl = cntProps.NewDocumentControl;
    if (cntProps.RequireClientRenderingOnNew != null)
        spContentType.RequireClientRenderingOnNew = cntProps.RequireClientRenderingOnNew.Value;
}

public static void SetContentTypeId(SPContentType spContentType, SPContentTypeId contentTypeId)
{
    try
    {
        FieldInfo fi = typeof(SPContentType).GetField("m_id", BindingFlags.NonPublic | BindingFlags.Instance);
        if (fi != null)
            fi.SetValue(spContentType, contentTypeId);
        else
            Console.Write("Couldn't set content type id!");
    }
    catch(Exception ex)
    {
        Console.Write(string.Format("Couldn't set content type id! {0}", ex.Message));
    }
}

public static void SetFieldRefProperties(SPFieldLink fLink, FieldRef fieldRef)
{
    if (fieldRef == null)
        return;

    if (fieldRef.DisplayName != null)
        fLink.DisplayName = fieldRef.DisplayName;
    if (fieldRef.IsHidden != null)
        fLink.Hidden = fieldRef.IsHidden.Value;
    if (fieldRef.IsRequired != null)
        fLink.Required = fieldRef.IsRequired.Value;
    if (fieldRef.IsReadOnly != null)
        fLink.ReadOnly = fieldRef.IsReadOnly.Value;

    if (fieldRef.Aggregation != null)
        fLink.AggregationFunction = fieldRef.Aggregation;
    if (fieldRef.Customization != null)
        fLink.Customization = fieldRef.Customization;
    if (fieldRef.PIAttribute != null)
        fLink.PIAttribute = fieldRef.PIAttribute;
    if (fieldRef.PITarget != null)
        fLink.PITarget = fieldRef.PITarget;
    if (fieldRef.PrimaryPIAttribute != null)
        fLink.PrimaryPIAttribute = fieldRef.PrimaryPIAttribute;
    if (fieldRef.PrimaryPITarget != null)
        fLink.PrimaryPITarget = fieldRef.PrimaryPITarget;
    if (fieldRef.Node != null)
        fLink.XPath = fieldRef.Node;
}

Here is how you can use all of that stuff:

public void AddSomeContentType(SPWeb spWeb, SPList spList)
{
    ContentType newContentType = new ContentType()
    {
        ParentContentTypeName = "Item",

        Id   = new SPContentTypeId("0x0100078C8B39671A4532AB9C5AB6DCB388A6"), // id has to correspond to the parent content type
        Name = "some new content type name",

        Properties = new ContentTypeProperties() { Description = "super modern content type", Group = "List Content Types" },
        FormUrls   = new ContentTypeFormUrls() { DisplayFormUrl = "Lists/your list name/your custom page name.aspx" },
        FieldRefs  = new List<FieldRef>()
            {
                new FieldRef() { Name = "Title",  ID = new Guid("{fa564e0f-0b71-4ab7-b863-0177e6ddd247}"), IsRequired = false, IsReadOnly = true, DisplayName = "List Item Title" },
                new FieldRef() { Name = "Status", IsRequired = true, DisplayName = "List Item Status" },
                new FieldRef() { Name = "Involved_User", ID = new Guid("869963ef-9ca3-4ad7-a5f0-8fff724a6877"), DisplayName = "User Involved" }
            }
    };

    AddContentType(spWeb, spList, newContentType);
}

Please note that you don’t have to fill out all properties of the auxiliary classes (ContentTypeProperties, ContentTypeFormUrls and FieldRef); the unspecified properties will be merely ignored, and the appropriate properties of SPContentType object will be left with their default values. Als,o pay attention to the fact that Content Type identifier has to contain the id of parent Content Type. Find out how to build a Content Type identifier recursively here.

 
  1. Nicki
    February 21st, 2012 at 08:02 | #1

    Thanks for the great example!
    Where can I put this code ?How can I deploy it?
    In an empty sharepoint project?In a class?In an empty element?

    Thanks ahead!

    • Administrator
      February 21st, 2012 at 20:03 | #2

      As for me, I use the code like this in Features or console applications. Activating such feature or executing such console app, this code is performed and required content type will be added.

  2. Nicki
    April 16th, 2012 at 09:26 | #3

    Hi!
    I was tring to create an independent content type (without a list)
    but was getting a few errors, like:”Content type which does not belong to a collection cannot be updated” and “column x does not exist”,
    I’ve changed the code that it would get only spweb in your “AddSomeContentType” function, and a few other changes.
    Do you have any idea to do it easily?

  3. Administrator
    April 16th, 2012 at 11:25 | #4

    Hello, Nicki!
    First of all, the AddContentType method was intended for adding a content type to a list. To add a new content type on Site level (list-independent, in your terms), the method must be partly rewritten.

    So, the new content type has to be added to the ContentTypes collection of a SPWeb-object:

    spWeb.ContentTypes.Add(customContentType);
    customContentType.Update();
    

    Call customContentType.Update() only after the new content type is added to the collection. The error “A content type which does not belong to a collection cannot be updated” reminds about this rule.

    To the site-level Content Type you can add only Site Columns, that are fields created on site-level. Otherwise, you gets “Column x does not exist”. An example below demonstrate how to add Site Column

    spWeb.Fields.Add("StringColumn", SPFieldType.Text, true);
    SPFieldLink fLink = new SPFieldLink(spWeb.Fields["StringColumn"]);
    customContentType.FieldLinks.Add(fLink);
    
  4. Nicki
    April 17th, 2012 at 03:52 | #5

    T×™hank a lot for your quick answer and your explanations!
    It helped me a lot!

  1. July 16th, 2012 at 16:23 | #1