Working with fields in CSOM

I’ve already detailed how to create a new Taxonomy Field in CSOM – here’s the more generic how to create a general field on a list.:

internal static void CreateFields(ClientContext clientContext, List targetList, string xmlDef)
{
 targetList.Fields.AddFieldAsXml(xmlDef, true, AddFieldOptions.AddFieldInternalNameHint);
 clientContext.ExecuteQuery();
}

And as a bonus, here’s how to set a field to be indexed in the client side object model:

internal static void SetIndex(ClientContext clientContext, List list, string fieldName)
{
	Field f = list.Fields.GetByInternalNameOrTitle(fieldName);
	clientContext.Load(f);
	clientContext.ExecuteQuery();
	f.Indexed = true;
	f.Update();
	list.Update();
	clientContext.ExecuteQuery();
}
Advertisement
Working with fields in CSOM

Hidden fields, and controlling them with the Object Model

SharePoint’s fields can be ‘hidden’ or shown, by setting the SPField.Hidden property. That’s great, but sadly it isn’t that simple. You might want a field hidden, but allow administrators, etc., to ‘unhide’ the field. Then again, sometimes you might want your hidden field to be really hidden, and never ‘unhidden’.

That is actually what SharePoint allows. The SPField.Hidden property also relies on a second property ‘CanToggleHidden’. You can see this in the CAML definition of a field. So you could define a field like:

<Field Type="Text" ID="{449cf8bc-88ce-445a-ac55-11ea0cb71fed}" Hidden="TRUE" CanToggleHidden="TRUE" ... >

Okay, that’s fine – that’d give us a field which is hidden, but we can unhide. Note that by default CanToggleHidden is false, and this led to my problem. Continue reading “Hidden fields, and controlling them with the Object Model”

Hidden fields, and controlling them with the Object Model

Programmatically making a Field Required in SharePoint 2007

One of my colleagues asked an interesting question – programmatically, he’d just added a Site Column to a list, and now he wanted to make that column required on that list. The SPField class had a Required property, and on MSDN this is described as

Gets or sets a Boolean value that determines whether users must enter a value for the field on New and Edit forms.

However, this wasn’t what seemed to happen – although he’d set this to true and update the field, it didn’t make the field required.

He came and asked me why – and I didn’t really know. We knew that the radio button on the column settings did what we wanted …

… so we cracked open reflector to take a look. And this is what we found:

Yup, that’s right – if the field is on a list, the list uses content types, and “Advanced Management of Content Types” isn’t set, then the code gets the first (i.e. Default) content type, and sets the SPFieldLink that relates to that field to be required. Finally, it saves changes to the content type.

So, on a list that doesn’t use content types, to make a field required, you have to update the content type. Interesting…

Programmatically making a Field Required in SharePoint 2007

Hide Column type from Lists

So, you’ve created a new custom Field (or column) type, but you don’t want it to be available to add directly to Lists and Libraries – that is, you want to force users to create columns as Site Columns, and add those to the list. Conversely, maybe you want to prevent a column being created as a Site column, and only available to add directly to lists (though I can’t thinik why).

Well, it turns out that that is possible. In the CAML defining your field in fldtypes_???.xml, you can have something like the following:

<FieldTypes>
    <FieldType>
        <Field Name="TypeName">MyCustomField</Field>
        <Field Name="ParentType">MultiChoice</Field>
        <Field Name="TypeDisplayName">My Custom Field</Field>
        <Field Name="ShowOnColumnTemplateCreate">TRUE</Field>
        <Field Name="ShowOnListCreate">FALSE</Field>
        <Field Name="ShowOnDocumentLibraryCreate">FALSE</Field>
        <Field Name="ShowOnSurveyCreate">FALSE</Field>
        <Field Name="Sortable">FALSE</Field>
        <Field Name="Filterable">TRUE</Field>
        <Field Name="ShowInEditForm">FALSE</Field>
        <Field Name="UserCreatable">TRUE</Field>

ShowOnColumnTemplateCreate controls visibility as a Site Column type. The other ‘Show On X’ values control visibility as a List Column Type for different types of List. You could, therefore, have a Column Type that can only be used on, say, Document Libraries. Hope that helps.

Hide Column type from Lists

Events on Adding/Deleting Fields from Content types

One of the things that’s driven me nuts in SharePoint 2007 is that there are no events to capture when a Field is added to a Content Type. There is an OnAdded event – which sometimes gets used to save Custom Properties of the field (though I prefer Gunnar Peipman’s approach)

Well, no more! In SharePoint 2010, there are now OnAddingToContentType() and OnDeletingFromContentType() methods. We can override this in our custom fields, and this lets us do lots of useful things – like register event handlers, add workflows, or set properties on the content type, which are things that I’ve wanted to do a few times now. So, good news!

Events on Adding/Deleting Fields from Content types

Rendering List Fields from other sites

I had a requirement recently to display the fields of a SharePoint item in another SharePoint site. Now, you can do this with things like the Content Query Web Part, or Data View Web Part, but I was doing a few other things, and specifically, I needed to pull the fields to display from a particular View on the list.

This turned out to be quite an interesting problem. All fields use a subclass of BaseFieldControl, and this is what renders the field – so it appeared to be fairly straight forward. As always, though, there was a little kink to it – you need an SPContext for the site the item comes from, and you need to use this as the contexts for the SPField‘s rendering control:

SPContext ctx = SPContext.GetContext(HttpContext.Current, item.ID, relatedList.ID, relatedWeb);

SPView relatedView = list.DefaultView;

foreach (string vf in relatedView.ViewFields)
{
    SPField fld = relatedList.Fields.GetFieldByInternalName(vf);

    HtmlGenericControl titleLabel = new HtmlGenericControl("H3");
    titleLabel.InnerText = fld.Title;
    this.Controls.Add( titleLabel );

    BaseFieldControl ctl = fld.FieldRenderingControl;
    ctl.ControlMode = SPControlMode.Display;
    ctl.ListId = relatedList.ID;
    ctl.ItemId = item.ID;
    ctl.RenderContext = ctx;
    ctl.ItemContext = ctx;
    ctl.FieldName = fld.Title;
    ctl.ID = Guid.NewGuid().ToString();

    this.Controls.Add( ctl );
}

Groovy!

Rendering List Fields from other sites

"To save to the server, correct the invalid or missing required properties"

I’ve been working on a custom field for, well, a while now, and after making some changes I started to get the error “To save to the server, correct the invalid or missing required properties” when trying to edit a document and save the changes back in Word 2007. This was a little strange, as I’d already sorted out hiding the custom field from the document information panel, and things had been working fine since then.

New documents seemed to be alright, but the older ones weren’t. So, maybe the problems were all the old documents, rather than my field.

Eventually, I came across this knowledgebase article – so I “inspected” the document, removed any of it’s custom data, and resaved it – and it worked fine.

What I think happened here was, I was testing different settings for Content Approval and Versioning on my document libraries. I think the troublesome documents were created when one of those was set to be ‘on’, and I subsequently turned it ‘off’. This meant that the document actually had extra data that referred to a column that no longer existed (I think) – which probably means this is related to Content Approval.

But I’d be curious if anyone else has managed to cause this error, and how they did so.

"To save to the server, correct the invalid or missing required properties"

Inconvenient GetCustomProperty and SetCustomProperty

So, I’m working on a custom field definition for SharePoint. My field has a few custom properties that you need to fill in when you create it. However, after creation, those fields were empty – they were simply never set. You could then update them, and that worked fine, but creation was broken.

The code I’d inherited was trying to solve this problem (it’s a known issue) this way – which is a bit ugly. It also holds memory for the Dictionary of values that, as far as I could see, was only being freed on IISReset. Yuck. And it didn’t work in our code, despite matching the example in that thread quite closely.

Fortunately, I came across a neat post on Gunnar Peipman’s blog – Temporary Solution for GetCustomProperty an SetCustomProperty Errors. I don’t like using reflection to invoke the methods I need to use, but at least it’s a solution, even if not ideal. And it works!

Please go to Gunnar’s post – but just in case his blog goes down, I’m going to shamelessly plagiarise his code below…

Thanks Gunnar! Continue reading “Inconvenient GetCustomProperty and SetCustomProperty”

Inconvenient GetCustomProperty and SetCustomProperty

Hide a custom field from the Document Information Panel

So, I’ve been working with a custom field type recently. It’s quite a complicated one – I’ll not go into details – but we did hit a problem with it. When a Word document was opened it would try to display our field in the Document Information Panel, which isn’t really possible, and caused Word to die in a horrible fashion. I forget the exact error we were getting, but it was something to do with the XSN being invalid.

What we really needed was a way to set that the property wasn’t to be shown in the Document Information Panel. However, there is no easy way of doing this with a purely programmatically created column. There are properties on the SPField object that can be accessed programmatically, and the CAML for a FieldRef can set ShowInFileDlg – but there isn’t an obvious way to set this value from C# code.

Naturally, that’s what we wanted to do. Well, there is a combination approach – the SPField.SchemaXml property allows us to get/set the CAML that defines the field.

So, I came up with this static function:

static void SetShowInFileDlg(SPField f, bool value)
        {
            XmlDocument fieldSchemaXml = new XmlDocument();
            fieldSchemaXml.LoadXml(f.SchemaXml);
            XmlAttribute attr = fieldSchemaXml.CreateAttribute("ShowInFileDlg");
            attr.Value = value.ToString().ToUpper();
            XmlNode fieldXmlNode = fieldSchemaXml.SelectSingleNode("Field");
            XmlAttributeCollection fieldAttributes = fieldXmlNode.Attributes;
            fieldAttributes.Append(attr);

            f.SchemaXml = fieldXmlNode.OuterXml;
            f.Update(true);
        }

I’m not sure what happens if you’re trying to update an already widely used column – will it update all content types that that column – but this worked for us.

Hide a custom field from the Document Information Panel

Event Properties AfterProperties – what should they be?

While working on pre-filling ListItem fields on an item, I became a bit puzzled. The SPItemEventProperties.AfterProperties collection is a dictionary which can contain the named value for one of the fields of the item. In other words, if we wanted to set a value “Tax Area” to “Europe” we’d do:

properties.AfterProperties["Tax Area"] = "Europe";

In our case, however, we didn’t know what these properties were before hand. Rather, we were ‘inheriting’ values from a parent folder. Thus, we were going to use the parent folder’s SPField object for each field to define the value. I started out using:

properties.AfterProperties[parentField.Title] = parentItem[parentField.id];

But is Title the right property to use? Well, having looked through a number of blog posts, this seems to be the subject of some confusion.

At first Title is okay to use. However, you can change the display name of the field. For example, we could change our field’s Title to ‘Tax Region’ – but we still need to use ‘Tax Area’ in our AfterProperties collection.

So, InternalName is the right property of the SPField to use – but there is a hiccup. The InternalName is encoded – Tax_x0020_Area – so you have to unescape it like I’ve talked about before.

The summary is, then, use the unescaped InternalName in your AfterProperties collection.

Event Properties AfterProperties – what should they be?