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.
