Category: SPField

Updating the title within a SPItemEventReceiver with AfterProperties

Recently I had a problem setting the title field of a Page within the pages library. My requirement was to set the title with the value from the PageTitle field of the current item.

An ItemEventReceiver, which is executed synchronously to prevent save conflict exception or problems with published items, was supposed to do exactly that. But when I set the title property of the item via AfterProperties, the value did not get stored. I tried other fields, and they got written to the item just fine. After some trial-and-error and consulting the MSDN I found a solution.

The ItemEventReceiver needs to update the vti_title field instead. This field exists on a SPFile. And since a Page is a SPFile, you’ll need to modify that field instead.

With this code, the title of my page was changed as supposed to.

Custom field and UpdateFieldValueInItem()

Recently I was developing a custom field. To store modified values, the UpdateFieldValueInItem method has to be overwritten.

In a normal way of clicking the submit/save button, the method is called and I can adjust the value for the field within the current item. The changes are submitted to the database later.

But what if you want to modify items outside of the current item? Sure, you can do so would you think. But you’ll need to consider the scenario that the user does not click submit/save. The method is called on every postback. The PeoplePicker will cause a postback, when it validates its values. There might be other controls as well, which behave this way.

My problem was that I could not modify items, other then the current, without checking if submit/save was clicked. I ended up checking the form for the control, that triggered the postback. If this value is “SaveItem”, I am good.

So if you need to know if the item is currently being saved or if you are within a regular postback, look at your form 🙂

Creating a lookup field via elements.xml

This is another post to help me remember. And as a reference for all of you, who cannot remember how to create a SPFieldLookup via XML.

When you provision a SPField via features, do not forget to add Overwrite=”TRUE”! Otherwise you’ll get an exception like this:

<nativehr>0x8107058a</nativehr><nativestack></nativestack>Fehler beim Binden des Inhaltstyps ‘0x010200C7A18EB120BB4A00892E9E1EE9481C9B0067E475B6FDD54048B347370871443CAD’ an die Liste ‘/sites/rhtest/Lists/LookupList’ für ‘http://rhdevsp2013/sites/rhtest’. Ausnahme ‘<nativehr>0x80070057</nativehr><nativestack></nativestack>’.

Unfortunately the MSDN is not very specific about the Overwrite property:

Optional Boolean. Specifies whether the field definition for a new field that is activated on a site (SPWeb) overwrites the field definition for an existing field, in cases where the new field has the same field ID as the existing field. True if the new field overwrites the existing field with the same field ID; otherwise false. The default is false.

Note, however, that if the existing field is read-only, or if it is sealed, then it will not be overwritten by the field that is being activated, even if this attribute is set to true

Hopefully I’ll think about this the next time a lookup field needs to be provisioned…

StaticName != InternalName

Recently I was trying to fetch a SPField from a SPWeb object. I had SharePoint 2010, so I decided to use the new SPFieldCollection.TryGetFieldByStaticName() Method.

image

You can imagine how surprised I was, that I couldn’t get the field I was looking for. What do we learn? Well, the StaticName of an SPField is not necessarily the InternalName!

Here is a link to the MSDN about SPField.StaticName: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfield.staticname.aspx

Issues in WSS V2

Responses to items in an Issue list (SPListIssue) are new version in WSS V3. WSS V2 lacks the ability to use versions for lists. So what did Microsoft do that an Issue list behaves like it is using versions?

In WSS V2 items in a single “thread” all have different ItemIDs. To group them together, all have the same IssueID. This is the ItemID from the original item.

A single issue in WSS V2 could look like this:

image 

In WSS V3 it should look like this:

 image

But how can we move/copy the item from V2 to V3? How is the data stored in the old SharePoint?

WSS V2 WSS v3
ItemIDIssueIDData enteredData stored ItemIDVersionData stored
11AA+B+C+D 14D
21BA 13C
31CA+B 12B
41DA+B+C 11A

Versioning in WSS V3

Each new version stores the data, which was entered for that particular version. SharePoint will merge the data from the current version with all previous versions.

Versioning in WSS V2

There was no versioning for lists in WSS V2. So Microsoft decided to add new items for a version, and map versions by an IssueID. The problem is, that they did not aggregate the data entered like they do in WSS V3.

The data entered was “A”. Later it was changed to “B”, “C” and finally “D”. WSS V3 stores the data in the same order as it was entered, and aggregates it for displaying. WSS V2 cycles the data, so it will show for each version the data aggregated from all previous versions. Only the current item (the one which was the first item for the issue) shows the newest data.

SPQuery, ViewFields and empty fields

If you want your query to return empty columns, you have to add Nullable=’TRUE’ to the viewfields.

<FieldRef Name=’Field1′ Nullable=’TRUE’/>

If you do not add the Nullable attribute, accessing the results of the query like this:

oListItemAvailable["Field1"] will give an Exception.

I’ve made posted a comment about this on the page in the MSDN http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spquery.viewfields.aspx

SharePoint Bug with MultiValue Fields

imageImagine you have a list (or document library) which has lots of items. Nothing fancy here. Now add a lookup or user field and allow it to contain multiple values.

Inside a view for this list you can filter e.g. for the title column. The filter dropdown shows all possible values for the column. image

 

 

 

 

 

Here comes the clue. If the item count of the list reaches a number somewhere between 400 and 500 items, the filter dropdown changes. You will get an option to show all filter values. This is for performance reasons.

image

Clicking on the “Show Filter Choices” should bring up the filter dropdown like you see it above. Instead you will get a <!–#RENDER FAILED–>.

image

Why that? It looks like the SQL query is broken, because you get this error in the application log.

5586 Unknown SQL Exception 4104 occured. UserData.tp_ID SELECT DISTINCT

The ULS Log will show an entry like this:

System.Data.SqlClient.SqlException: The multi-part identifier "UserData.tp_ID" could not be bound. ORDER BY items must appear in the select list if SELECT DISTINCT is specified. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlDataReader.ConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.SharePoint.Utilities.SqlSession.ExecuteReader(SqlCommand command, CommandBehavior behavior)

SqlError: ‘The multi-part identifier "UserData.tp_ID" could not be bound.’ Source: ‘.Net SqlClient Data Provider’ Number: 4104 State: 1 Class: 16 Procedure: ” LineNumber: 1 Server: ‘vmmoss’

10/22/2008 19:54:48.36 w3wp.exe (0x0BD4) 0x1178 Windows SharePoint Services Database 880j High SqlError: ‘ORDER BY items must appear in the select list if SELECT DISTINCT is specified.’ Source: ‘.Net SqlClient Data Provider’ Number: 145 State: 1 Class: 15 Procedure: ” LineNumber: 1 Server: ‘vmmoss’

10/22/2008 19:54:48.36 w3wp.exe (0x0BD4) 0x1178 Windows SharePoint Services Database 5586 Critical Unknown SQL Exception 4104 occured. Additional error information from SQL Server is included below. The multi-part identifier "UserData.tp_ID" could not be bound. ORDER BY items must appear in the select list if SELECT DISTINCT is specified…

There are some KB articles around the RENDER FAILED problem:

Update 24.10.2008

After communicating with Microsoft, it has been confirmed that the behavior is a bug. The really bad news is, that there will be no fix for this version of SharePoint!

Here is an answer from Microsoft:

Fix request has been rejected because of the following reasons:

1. The 500 unique item limit was picked as a threshold to switch from the V3 style filter menus to the V2 menus because above 500 unique items, the performance of the V3 filter menus degrades.

2. I think we can’t have a specific error msg for this error, because this is a general sql query execute error.

Proposed Workarounds are:

List all known workarounds:

W1: edit the ows.js in …\…\12 remove the line var L_FilterMode_Text="Show Filter Choices" reboot the server;and now the option ‘Show Filter Choices’ is no more available.

W2: Reducing the number of fields to a number inferior to 500

If there is any news or if I find another way, I will let you know.

Update:

The problem is fixed with the WSS Cumulative Update from June 30, 2009. Thank you Microsoft.

You have a SharePoint list that has more than 500 items. The list has a column of "People and Groups" type with the "Allow multiple selections" setting set to "Yes". If you click Show Filter Choices in this list, you receive the following error message:

#RENDER FAILED

Programmatically creating a SPFieldCalculated

As you might already know, you can create new fields with SPFieldCollection.AddFieldAsXml(string schema). The schema contains the formula for the calculated field. There are some points to take care of, before you can add the field:

  1. make sure your referenced fieldnames are the display names and not the internal field names
  2. the formula has to be in the English format

Changing the fieldnames is an easy task. If you read the schema from a field and want to create a new one with the same formula, you will get something like "=if(fieldA,1,2)".

But what do you do if you want to create the new calculated field in a SPWeb which is not using English as its regional settings? You will get an error that the formula has errors and the field will not be created.

In my case the web was using German regional settings. The formula should look like "=wenn(fieldA;1;2)". Rewriting the formula was not an option.

Here comes the solution:

   1: SPWeb web = SPContext.Current.Web;
   2: SPFieldCollection fields = SPContext.Current.List.Fields;
   3:  
   4: int webLocaleId = web.Locale.LCID;
   5: web.Locale = new CultureInfo(1033);
   6: web.Update();
   7: try
   8: {
   9:     string newInternalFieldName = fields.AddFieldAsXml(newSchema);
  10: }
  11: catch (Exception ex)
  12: {
  13:     // do something
  14: }
  15: finally
  16: {
  17:     web.Locale = new CultureInfo(webLocaleId);
  18:     web.Update();
  19: }

The trick is to set the regional settings of the web to English (1033) prior to creating the field!

Remember to change the locale back to the original one after adding the field.

Tags:

Adding a custom field type via code

Your custom field type can be added to a list in a browser easily. But how do you add a custom field type via code?

Here is my way:

  1. add a new field with the field type from which your custom field type derived
  2. change the field type of the new field to your own custom field type

In my case my custom field type derived from a SPFieldLookup.

 
  1:   // create new lookup field 
   2:  string newFieldName = fields.AddLookup("fieldname", list.ID, web.ID, false); 
   3:  var newField = fields.GetFieldByInternalName(newFieldName); 
   4:  // change field type to our own 
   5:  newField.SchemaXml = newField.SchemaXml.Replace("Lookup", "yourFieldType");

 

Update 4. Feb 2008

The above solution will bring you a field with the type. But a much smoother way is to create a new field, which has the selected type. This way you don’t need to modify the schema of the field.

1:  CustomFieldClass field = list.Fields.CreateNewField("CustomFieldClass", "The name of the field");

2:  list.Fields.Add(field);

 

Technorati Tags: ,

HowTo create an object if you only have its type as string

Sometimes you have only the type of an object, which you want to create. Meaning you want to create an object dynamically. This is how you would achieve your goal:

In my case I wanted to create a SPField from its typename.

   1:  string typename = "Microsoft.SharePoint.SPFieldText, Microsoft.SharePoint, "+
   2:      "Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c";
   3:  Type t = Type.GetType(typename, true, true);
   4:  object newObject = System.Activator.CreateInstance(t, 
   5:      BindingFlags.SetProperty, new object[] { fields, "Title" });

I know one constructor for a SPField is to create a new object with the SPFieldCollection and its name.

The problem was that the constructor could be found.

You can view all available constructors with:

   1:  ConstructorInfo[] constructors = t.BaseType.GetConstructors();

After looking back into the SDK, I noticed that SharePoint can be so easy. You can create a SPField like this:

   1:  SPField newField = new SPField(fields, "SPFieldText", "new Field Name");

Next time I will remember 🙂

Keywords: Create SPField from typename, Erstellen eines spfield von seinem typ namen

How to get LookupField Information from a listItem

If you want the ID or the value form a LookupField, you can get it easily with this code snippet:

SPListItem item = getitsomewhare...
SPFieldLookupValue lf =
    (SPFieldLookupValue)
     item.ParentList.Fields.GetField(_FieldName).GetFieldValue(
     item.GetFormattedValue(_FieldName));

if you got the field, fetch its properties via

if (lf == null)
{
    int itemID = lf.LookupId;
    string itemValue = lf.LookupValue;
}
Have fun ;-)

Write a SPFieldUser

If you have a list which contains a SPFieldUser field (with multiple selection), you can add users too it with the following code:

using (SPSite site = new
SPSite("http://site"))

{

using (SPWeb web = site.AllWebs["Web"])

    {

    SPList list = web.Lists["List"];

    SPListItem item = list.Items[0];

    SPFieldUserValueCollection values = (SPFieldUserValueCollection)item["Users"];

    SPUserCollection users = web.AllUsers;

    foreach (SPUser user in users)

    {

        values.Add(new
SPFieldUserValue(web, user.ID, user.Name));

    }

    item["Users"] = values;

    item.Update();

    }

}

In this example the list "List" would contain a field with the name "Users", which takes users. All web users are added to the field "Users" of the first list item, which is then updated.