Using taxonomyfields in Sharepoint 2010: Part I

By bhoeijmakers at November 05, 2010 13:18
Filed Under: .NET, SharePoint 2010

In this series of posts I will talk about using Taxonomy fields in SharePoint 2010 development. They are a funky bunch and don´t work quite as you would expect. I will talk about the following subjects:

 

Introduction

Part I: Declaration and initialization (you are reading it!)

Part II: Using taxonomyfields programmatically

Part III: Searching taxonomy fields

 

The other parts are now also online!

 

Declaration and initialization

The problem with declaring TaxonomyFields programmatically is that you’ll have to do all the wiring of the field by hand. If you fail to follow all the neccesary steps, strange behaviour will occur and your TaxonomyField will not behave properly. In this post I try to give you a practical guide how to properly initialize a TaxonomyField. If you want to know why exactly all this is necessary, send me a message or do some more searching on the Internet. In the introduction post I’ve posted a link to a good blogpost about this.

 

For this example i'm using a new Visual Studio project and choosing the "Empty SharePoint project" template.

A taxonomy field actually consist of two parts; a hidden note field and the actual TaxonomyField. To declare a taxonomyfield, add the following xml to your site column declarations:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field Type="TaxonomyFieldTypeMulti" 
         DisplayName="MyTaxonomyField" 
         ShowField="Term1033" Required="TRUE" 
         ID="{8D0458C1-0FB7-4981-BEE1-52D0DF01895C}" 
         StaticName="MyTaxonomyField" 
         Name="MyTaxonomyField" 
         Group="Custom"
         Mult="TRUE"
         ></Field>
  
  <Field Type="Note" 
         DisplayName="MyTaxonomyField_0" 
         StaticName="MyTaxonomyFieldTaxHTField0" 
         Name="MyTaxonomyFieldTaxHTField0" 
         ID="{3a913424-fc7f-49ef-8fb6-a2ca1712cdc3}" 
         Hidden="TRUE"
         DisplaceOnUpgrade="TRUE"
         />
</Elements>

Note: The naming of these fields (DisplayName, StaticName and Name) I’ve kept the same as the ones created when adding a TaxonomyField through the UI. You could use other names for them, but I recommend keeping them like this. 

 

In the above example i'm creating a Multi valued taxonomyfield. To create a taxonomyfield that can only contain one value, use

 

<Field Type="TaxonomyFieldType" ....  Mult="FALSE"></Field>

 

Also note that I set the DisplaceOnUpgrade property to true on the hidden note field. This makes sure that the field is overwritten when upgraded.

 

Set up the eventreceiver

After declaring the pair of fields, the notefield needs to be connected to the taxonomyfield and the field needs to be configured for using the TermStore. So the feature deploying my Taxonomy SiteColumn, gets a featurereceiver that looks like this:

 

Update 4th of November 2011: Be sure to read my follow-up post to learn how to improve this code to make it ready for a production environment!

 

    public class FieldsEventReceiver : SPFeatureReceiver
    {
        // Uncomment the method below to handle the event raised after a feature has been activated.
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            // declare constants
            const string _MYTAXONOMYFIELDID = "8D0458C1-0FB7-4981-BEE1-52D0DF01895C";
            const string _MYTAXONOMYNOTEFIELDID = "3a913424-fc7f-49ef-8fb6-a2ca1712cdc3";
            SPSite site = properties.Feature.Parent as SPSite;
            if (site != null)
            {
                // get the taxonomyfield from the sitecollection
                TaxonomyField field = site.RootWeb.Fields[new Guid(_MYTAXONOMYFIELDID)] as TaxonomyField;
                if (field != null)
                {
                    // attach the note field
                    field.TextField = new Guid(_MYTAXONOMYNOTEFIELDID);
                    // set up the field for my termstore
                    TaxonomySession session = new TaxonomySession(site);
                    
                    if (session.TermStores.Count > 0)
                    {
                        // get termstore values
                        TermStore ts = session.TermStores[0];
                        Group group = GetGroup("SomeTaxonomyGroup", ts);
                        if (group == null)
                            throw new Exception("Group was not found in the termstore");
                        TermSet termSet = group.TermSets["Planes"];
                        // actually setup the field for using the TermStore
                        field.SspId = ts.Id;
                        field.TermSetId = termSet.Id;
                    }
                    // update the changes to the field
                    field.Update();
                }
            }
        }
        /// <summary>
        /// Gets a group with the given name from the termstore
        /// </summary>
        /// <param name="name">The name of the group</param>
        /// <param name="ts">The termstore containing the group</param>
        /// <returns></returns>
        private Group GetGroup(string name, TermStore ts)
        {
            Group g = null;
            foreach (Group group in ts.Groups)
            {
                if (group.Name == name)
                {
                    g = group;
                    break;
                }
            }
            return g;
        }

 

You'll need to add a reference to the Microsoft.Sharepoint.Taxonomy dll to your project to make the code work.

 

Adding the taxonomyfield to a contenttype

Now that we have our TaxonomyField site-column set-up, it is time to add it to a custom ContentType. When you add a taxonomyfield to a contenttype, add BOTH fields to your contenttype. Failing to do so will result in unpredictible behaviour of the field. Besides that, you’ll have to add two other fields to the list; the TaxCatchAll and TaxCatchAllLabel internal SharePoint fields. These fields make sure that for example the search refinement panel works.

 

I've added a new SPI (contenttype) to my project. I chose Item as the base contenttype and edited the elements.xml file to contain the following:

 

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!-- Parent ContentType: Item (0x01) -->
  <ContentType ID="0x0100b58d33764b384da58bbadee9721e1843"
               Name="MyCustomContentType"
               Group="Custom"
               Description="Custom provisioned contenttype containing a Taxonomyfield"
               Inherits="TRUE"
               Version="0">
    <FieldRefs>
      <FieldRef ID="8D0458C1-0FB7-4981-BEE1-52D0DF01895C" Name="MyTaxonomyField"/>
      <FieldRef ID="3a913424-fc7f-49ef-8fb6-a2ca1712cdc3" Name="MyTaxonomyFieldTaxHTField0"/>
      <FieldRef ID="f3b0adf9-c1a2-4b02-920d-943fba4b3611" Name="TaxCatchAll"/>
      <FieldRef ID="8f6b6dd8-9357-4019-8172-966fcd502ed2" Name="TaxCatchAllLabel"/>
    </FieldRefs>
  </ContentType>
</Elements>
 

The TaxCatchAll and TaxCatchAllLabel fields are being synchronized with the data in the TaxonomyField by two eventreceivers. These have to be wired to either the just created contenttype, or to the list that uses the contenttype. Best option is to attach them to the contenttype. That way you won’t forget to attach them if you create a list. These are the receivers:

 

<Receiver>         
    <Name>TaxonomyItemSynchronousAddedEventReceiver</Name>
    <Type>ItemAdding</Type>
    <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
    <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
    <SequenceNumber>10000</SequenceNumber>
</Receiver>
<Receiver>
    <Name>TaxonomyItemUpdatingEventReceiver</Name>
    <Type>ItemUpdating</Type>         
    <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
    <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
    <SequenceNumber>10000</SequenceNumber>       
</Receiver>

 

Thats it! Deploying my solution and activating the features results in a simple custom contenttype, containing a taxonomy field.

 

TaxonomyContenttype

Comments (6) -

11/11/2010 10:32:59 PM #

Hi Bert-Jan,

a quick note on this. If you are creating a taxonomy site column you should *not* create the hidden note field yourself. The field implementation takes care of this. Decompile the TaxonomyField class and open the OnAdded method. This reveals the following structure:

    // Sanity check, column is actually part of a site
    if ((this.Fields != null) && (this.Fields.Web != null))
    {
        try
        {
            // get the site column
            TaxonomyField field = this.Fields.Web.AvailableFields[base.Id] as TaxonomyField;
            if (field != null)
            {
                // if the column is added to a list as part of a content type
                if (base.ParentList != null)
                {
                }
                // or when the field is not the default Keyword field
                else if (!this.IsKeyword)
                {
                    // and the ID of the hidden note field is not set
                    if (this.textField == Guid.Empty)
                    {
                        // add a new note field
                        this.AddHiddenTextField(this.Fields);

Hope it helps,

Wouter

Wouter van Vugt Netherlands | Reply

11/14/2010 12:05:25 PM #

Hi Wouter;

  You are totally right. I ran into some weird problems using my custom contenttypes with taxomomyfields. This seemed to be the solution, but obviously isn't. I edited the article. Thanks for your input.

Bart-Jan

bhoeijmakers Netherlands | Reply

3/4/2011 3:07:29 PM #

Hello Bart-Jan,

When I use this code and inspect result with Sharepoint manager 2010 there is no note field created by Sharepoint. Only when I add attribute overwrite=true to XML Sharepoint generates the hidden note field. Why should I not provision note field in XML? I am currently fixing an application of some one else that uses the note field in code.

Danny Netherlands | Reply

3/22/2011 3:55:17 PM #

hello Danny;

  It turns out that I was wrong. The OnAdded event that creates the hidden notefield is NOT fired when provisioning taxonomy fields through XML. So the notefield must be added manually. I've edited the article, sorry for the confusion Smile

bhoeijmakersd Netherlands | Reply

1/20/2011 5:52:48 PM #

Hey there, thanks for the helpful sample code.  Have you noticed that when one creates the list definition (and instance) in Visual Studio, the meta data does not appear refine search results ("No refinements available" appears), but if one creates the list via the browser w/ the custom content type this does not seem to be any issue.

-Ryan


Ryan United States | Reply

3/22/2011 3:57:52 PM #

This is due to missing (normally hidden) columns in your listdefinition. They are the columns TaxCatchAll and TaxCatchAllLabel. Also you need to manually attach eventreceivers to the list for everything to work. Check out this post for all the info you need:

www.sharepointconfig.com/.../

bhoeijmakersd Netherlands | Reply

Pingbacks and trackbacks (1)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

Bart-Jan Hoeijmakers is Lead SharePoint Developer at VX Company. Since 2006 he is the driving force behind the development of several enterprise SharePoint projects within VX Company. He is a hardcore developer in both SharePoint and ASP.NET and loves to dive into the dark sides of SharePoint development.

Month List