SharePoint 2010 Client Object Model, Part 2






4.55/5 (11 votes)
An investigation of SharePoint 2010 Client Object Model and how to use it
- Download ConsoleDemo - 11.72 KB
- Download SPDemo - 511.3 KB
- Download SilverlightCSOM - 18.83 KB
- Download WPFClient - 24.91 KB
SharePoint 2010 Client Object Model
In Part 1 of this article I took an in-depth look at the SharePoint Client Object Model; how it works and how to form queries and work with objects. Now I'll take a practical look at using the Client OM with examples using .NET Managed code, using WPF, JavaScript used in a Application page, and finally with Silverlight in a SharePoint webpart.
Setup
To facilitate the examples the first thing was to create a SharePoint project that creates a simple Contact list and populates it with a few Contacts to begin with. This project is included in the downloads for this article. For the Silverlight example I created a Silverlight project and added the output to a Document Library on the site.
The demo apps may not be the best designed, but they are functional and effectively demonstrate using the Client Object Model in a somewhat pratical manner.
Using Client OM with .NET Managed Code

This application first gets a ListItemCollection
containing all the
Contacts in the demo list.
public static ListItemCollection GetList() { using(ClientContext ctx = new ClientContext(SITE)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); CamlQuery query = new CamlQuery(); query.ViewXml = "<View>" + "<Query>" + "<OrderBy>" + "<FieldRef Name='Title'/>" + "<FieldRef Name='FirstName'/>" + "</OrderBy>" + "</Query>" + "<ViewFields>" + "<FieldRef Name='ID'/>" + "<FieldRef Name='Title'/>" + "<FieldRef Name='FirstName'/>" + "<FieldRef Name='WorkAddress'/>" + "<FieldRef Name='WorkCity'/>" + "<FieldRef Name='WorkState'/>" + "<FieldRef Name='WorkZip'/>" + "</ViewFields>" + "</View>"; ListItemCollection listItems = list.GetItems(query); ctx.Load(listItems); ctx.ExecuteQuery(); return listItems; } }
Since this article is more about usage of the Client Object Model I won't delve into the XAML too much. Just to show, however, the ListBox
is bound to the ListItemCollection
returned from the above code
and the TextBlock
s in the DataTemplate
are bound to the indexer property of the ListItem
for
the specified FieldValue
<ListBox x:Name="Contacts" ItemsSource="{Binding}" Width="225" Height="300" SelectionChanged="Contacts_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=[FirstName]}" Margin="0,0,5,0"/> <TextBlock Text="{Binding Path=[Title]}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
Part 1 of this article covers the details for retrieving ListItem
s, ClientContext
, CamlQuery
and other objects and methods being used so I won't go into them here.
Updating ListItems

Clicking on the Edit button in the application causes the edit fields to be
displayed, once again I'm not focusing on the WPF, you can explore the download
files for more details on that aspect. After clicking the Save button it's simply
a matter of finding the ListItem
to be updated, update the FieldValues
, then commit the changes.
public static void Update(int id, string firstName, string lastName, string address, string city, string state, string postalCode) { using(ClientContext ctx = new ClientContext(SITE)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); // Get the item being updated ListItem item = list.GetItemById(id); // Update the FieldValues item["Title"] = lastName; item["FirstName"] = firstName; item["WorkAddress"] = address; item["WorkCity"] = city; item["WorkState"] = state; item["WorkZip"] = postalCode; // Must make sure to call this item.Update(); // Commit the change ctx.ExecuteQuery(); } }
One thing to note here the lack of any calls to Load
or LoadQuery
as in the examples
in Part 1. Since nothing is being returned in this case there is no need to use these methods. Think of it as the equivalent of the ADO.NET ExecuteNonQuery
method.
Another thing to note is although it may seem tempting to cache the ListItem
and update it directly,
without the GetItemById
call, this won't work. The item must be
retrieved within the context of the ClientContext
object, similar to how the Entity Framework functions for those familiar with it. It is not recommended to cache the ClientContext
since it does hold resources
and may cause degradation of your application. Best to follow the database
connection model; open late, use and close.
Adding ListItems

Adding a new ListItem
to the List
is almost as easy as updating. One of the main differences though is the ListItemCreationInformation
object.
public static void AddContact(string firstName, string lastName, string address, string city, string state, string postalCode) { using(ClientContext ctx = new ClientContext(SITE)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); // Create the Listitem // ListItemCreationInformation can bee null for root folder ListItemCreationInformation createInfo = null; // Or for adding item to a folder //ListItemCreationInformation createInfo = new ListItemCreationInformation(); //createInfo.FolderUrl = "site/lists/listname/folder"; ListItem item = list.AddItem(createInfo); // Set the FieldValues item["Title"] = lastName; item["FirstName"] = firstName; item["WorkAddress"] = address; item["WorkCity"] = city; item["WorkState"] = state; item["WorkZip"] = postalCode; // Save changes item.Update(); // Commit ctx.ExecuteQuery(); } }
Just as with the methods demonstrated in Part 1 when the ExecuteQuery
method is invoked the Client OM API will create an XML string and POST it to the client.svc
Web Service.
As you can see below this is very similar to the "get" methods but you'll notice
the Method
element specifying which method is to be called and the Parameter
elements passing the type and value.
POST http://mySite/_vti_bin/client.svc/ProcessQuery HTTP/1.1 X-RequestDigest: 0xE8312251E4B[truncated for space]0000 Content-Type: text/xml X-RequestForceAuthentication: true Host: mySite Content-Length: 2461 Expect: 100-continue <Request AddExpandoFieldTypeSuffix="true" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"> <Actions> <ObjectPath Id="25" ObjectPathId="24" /> <ObjectPath Id="27" ObjectPathId="26" /> <ObjectPath Id="29" ObjectPathId="28" /> <ObjectPath Id="31" ObjectPathId="30" /> <ObjectIdentityQuery Id="32" ObjectPathId="30" /> <ObjectPath Id="34" ObjectPathId="33" /> <Method Name="SetFieldValue" Id="35" ObjectPathId="33"> <Parameters> <Parameter Type="String">Title</Parameter> <Parameter Type="String">Betic</Parameter> </Parameters> </Method> <Method Name="SetFieldValue" Id="36" ObjectPathId="33"> <Parameters> <Parameter Type="String">FirstName</Parameter> <Parameter Type="String">Alpha</Parameter> </Parameters> </Method> <Method Name="SetFieldValue" Id="37" ObjectPathId="33"> <Parameters> <Parameter Type="String">WorkAddress</Parameter> <Parameter Type="String">Main street </Parameter> </Parameters> </Method> <Method Name="SetFieldValue" Id="38" ObjectPathId="33"> <Parameters> <Parameter Type="String">WorkCity</Parameter> <Parameter Type="String">Anytown</Parameter> </Parameters> </Method> <Method Name="SetFieldValue" Id="39" ObjectPathId="33"> <Parameters> <Parameter Type="String">WorkState</Parameter> <Parameter Type="String">PA</Parameter> </Parameters> </Method> <Method Name="SetFieldValue" Id="40" ObjectPathId="33"> <Parameters> <Parameter Type="String">WorkZip</Parameter> <Parameter Type="String">12345</Parameter> </Parameters> </Method> <Method Name="Update" Id="41" ObjectPathId="33" /> <Query Id="42" ObjectPathId="33"> <Query SelectAllProperties="false"> <Properties> <Property Name="Title" ScalarProperty="true" /> <Property Name="FirstName" ScalarProperty="true" /> <Property Name="WorkAddress" ScalarProperty="true" /> <Property Name="WorkCity" ScalarProperty="true" /> <Property Name="WorkState" ScalarProperty="true" /> <Property Name="WorkZip" ScalarProperty="true" /> </Properties> </Query> </Query> </Actions> <ObjectPaths> <StaticProperty Id="24" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" /> <Property Id="26" ParentId="24" Name="Web" /> <Property Id="28" ParentId="26" Name="Lists" /> <Method Id="30" ParentId="28" Name="GetByTitle"> <Parameters> <Parameter Type="String">SPUG Demo Contacts</Parameter> </Parameters> </Method> <Method Id="33" ParentId="30" Name="GetItemById"> <Parameters> <Parameter Type="Int32">4</Parameter> </Parameters> </Method> </ObjectPaths> </Request>
Although nothing is returned, such as with Load
or LoadQuery
, there is a response from the Client.svc
Web Service call. As you see below the response simply contains the object that was updated, however not the _ObjectVersion_ property which incremented with the version of the object returned.
HTTP/1.1 200 OK Cache-Control: private Content-Type: application/json Server: Microsoft-IIS/7.5 SPRequestGuid: 472b1d44-cc1e-4295-a7b0-c38cf3b68a62 Set-Cookie: WSS_KeepSessionAuthenticated={4e4cffb3-203e-4857-8f5a-90a4b18789a4}; path=/ X-SharePointHealthScore: 0 X-Content-Type-Options: nosniff X-AspNet-Version: 2.0.50727 X-Powered-By: ASP.NET MicrosoftSharePointTeamServices: 14.0.0.6029 Content-Length: 672 [ { "SchemaVersion":"14.0.0.0","LibraryVersion":"14.0.6106.5001","ErrorInfo":null },25,{ "IsNull":false },27,{ "IsNull":false },29,{ "IsNull":false },31,{ "IsNull":false },32,{ "_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]3964:list:9bf3[truncated for space]d045" },34,{ "IsNull":false },42,{ "_ObjectType_":"SP.ListItem", "_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]964:list:9bf[truncated for space]d045:item:4,1", "_ObjectVersion_":"2", "Title":"Betic", "FirstName":"Alpha", "WorkAddress":"Main street ", "WorkCity":"Anytown", "WorkState":"PA", "WorkZip":"12345" } ]
Deleting ListItems
Deleting a ListItem
is the simplest of the operations. After obtaining the ListItem
, call it's DeleteObject
method and commit the changes with an ExecuteQuery
.
public static void Delete(int id) { using(ClientContext ctx = new ClientContext(SITE)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); // Get the item being deleted ListItem item = list.GetItemById(id); item.DeleteObject(); // Commit ctx.ExecuteQuery(); } }
POST http://mySite/_vti_bin/client.svc/ProcessQuery HTTP/1.1 X-RequestDigest: 0x2BD0E4[truncated for space]0000 Content-Type: text/xml X-RequestForceAuthentication: true Host: mySite Content-Length: 994 Expect: 100-continue <Request AddExpandoFieldTypeSuffix="true" SchemaVersion="14.0.0.0" LibraryVersion="14.0.4762.1000" ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"> <Actions> <ObjectPath Id="54" ObjectPathId="53" /> <ObjectPath Id="56" ObjectPathId="55" /> <ObjectPath Id="58" ObjectPathId="57" /> <ObjectPath Id="60" ObjectPathId="59" /> <ObjectIdentityQuery Id="61" ObjectPathId="59" /> <ObjectPath Id="63" ObjectPathId="62" /> <Method Name="DeleteObject" Id="64" ObjectPathId="62" /> </Actions> <ObjectPaths> <StaticProperty Id="53" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" /> <Property Id="55" ParentId="53" Name="Web" /> <Property Id="57" ParentId="55" Name="Lists" /> <Method Id="59" ParentId="57" Name="GetByTitle"> <Parameters> <Parameter Type="String">SPUG Demo Contacts</Parameter> </Parameters> </Method> <Method Id="62" ParentId="59" Name="GetItemById"> <Parameters> <Parameter Type="Int32">6</Parameter> </Parameters> </Method> </ObjectPaths> </Request>
Once again, although nothing is returned a response is sent from the Web Service. This time though there is no object since it has been deleted.
HTTP/1.1 200 OK Cache-Control: private Content-Type: application/json Server: Microsoft-IIS/7.5 SPRequestGuid: 28594025-6d96-4798-ba72-06a6b81f0c3e Set-Cookie: WSS_KeepSessionAuthenticated={4e4cffb3-203e-4857-8f5a-90a4b18789a4}; path=/ X-SharePointHealthScore: 0 X-Content-Type-Options: nosniff X-AspNet-Version: 2.0.50727 X-Powered-By: ASP.NET MicrosoftSharePointTeamServices: 14.0.0.6029 Content-Length: 343 [ { "SchemaVersion":"14.0.0.0","LibraryVersion":"14.0.6106.5001","ErrorInfo":null },54,{ "IsNull":false },56,{ "IsNull":false },58,{ "IsNull":false },60,{ "IsNull":false },61,{ "_ObjectIdentity_":"740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:6bb[truncated for space]964:list:9bf[truncated for space]d045" },63,{ "IsNull":false } ]
Client OM with JavaScript
Working with the Client OM in JavaScript is quite similar to Managed Code. The objects are essentially the same, except the method names conform to JavaScript coding standards.

One major difference, however, is the Client OM implementation in JavaScript uses asynchronous methods where the Managed Code implementation uses synchronous methods. In Managed Code though this doesn't prevent you from using Asynchronous methods when implementing your code as the Silverlight example will show.
/// Retrieve all contacts in the list function loadContactsList() { var ctx = SP.ClientContext.get_current(); var web = ctx.get_web(); var list = web.get_lists().getByTitle(LIST_NAME); var camlQuery = new SP.CamlQuery(); var queryXml = "<View>" + "<Query>" + "<OrderBy>" + "<FieldRef Name='Title'/>" + "<FieldRef Name='FirstName'/>" + "</OrderBy>" + "</Query>" + // Can specify fields here // "<ViewFields>" + // "<FieldRef Name='ID'/>" + // "<FieldRef Name='Title'/>" + // "<FieldRef Name='FirstName'/>" + // "</ViewFields>" + "</View>"; camlQuery.set_viewXml(queryXml); this.listItems = list.getItems(camlQuery); // Can use this or LoadQuery //ctx.load(this.listItems); // Fields can be included here rather than in CAML ctx.load(this.listItems, 'Include(ID, Title, FirstName)'); ctx.executeQueryAsync( Function.createDelegate(this, onLoadContactsSuccess), Function.createDelegate(this, onFail)); }
As you can see in the above code querying for the ListItem
s is the same as in the WPF example; get a ClientContext
, get the objects necessary, form a CamlQuery
and call executeQueryAsync
. As you'll notice, getting the ClientContext
with JavaScript is slightly different.
Just as with the Managed Code implementation you can construct a ClientContext
object by passing a URL. However, in JavaScript this is
a relative URL, rather than a full URL, since the code is executing with a SharePoint site already.
var ctx = SP.ClientContext("serverRelativeUrl");
The get_current
method is particular to the JavaScript Client OM implementation and, as you can see below, constructs the ClientContext
object from the get_webServerRelativeUrl
method which has been filled in from other SharePoint JavaScript code.
SP.ClientContext.get_current = function() {ULS5Vl:; if (!SP.ClientContext.$1S_1) { SP.ClientContext.$1S_1 = new SP.ClientContext(SP.PageContextInfo.get_webServerRelativeUrl()); } return SP.ClientContext.$1S_1; }
After executeQueryAsync
has been called successfully the delegate specified in the first parameter, onLoadContactsSuccess
in this case, will be called.
/// Function to be called after executeQueryAsync /// in loadContactsList has been successful function onLoadContactsSuccess(sender, args) { $("#contactList").empty(); if(this.listItems.get_count() == 0) { $("#contactList").append("<span>No contacts found</span>"); } else { // Get Enumerator and iterate through it var listEnumerator = this.listItems.getEnumerator(); while (listEnumerator.moveNext()) { var item = listEnumerator.get_current(); // Properties on FieldValues will match // Fields returned by query var title = item.get_fieldValues().Title; var firstName = item.get_fieldValues().FirstName; var id = item.get_fieldValues().ID; // Create the html element var contact = "<span önclick='displayContactDetails(" + id + ")'>" + firstName + " " + title + "</span>"; // Append to "list" $("#contactList").append(contact + "<br/>"); } } }
Getting an individual ListItem
is again almost the same as previous examples. The load
method, however, is a little different. Since JavaScript doesn't support Lambda expression
you can pass an array of strings that specify the columns to be returned in the
object.
function displayContactDetails(id) { selectedItemId = id; var ctx = SP.ClientContext.get_current(); var web = ctx.get_web(); var list = web.get_lists().getByTitle(LIST_NAME); listItem = list.getItemById(id); // Returns all fields //ctx.load(this.listItem); // Returns only fields specified ctx.load(this.listItem, "Title", "FirstName", "WorkAddress", "WorkCity", "WorkState", "WorkZip"); ctx.executeQueryAsync( Function.createDelegate(this, onContactDetailsSuccess), Function.createDelegate(this, onFail)); }
Updating ListItem
No real surprises here. The difference with JavaScript is using the set_item
method rather than using the indexer in Managed Code.
function saveContact() { $(".field").each(function () { var id = $(this).attr("ID"); var value = $(this).next("input").val(); listItem.set_item(id,value); }); listItem.update(); var ctx = SP.ClientContext.get_current(); ctx.executeQueryAsync( Function.createDelegate(this, onSaveContactSuccess), Function.createDelegate(this, onFail)); }
Adding ListItems
By now you should be getting the hang of this.
function onAdd() { var ctx = SP.ClientContext.get_current(); var web = ctx.get_web(); var list = web.get_lists().getByTitle(LIST_NAME); // can be null when adding item to root folder var createInfo = null; //var createInfo = new SP.ListItemCreationInformation(); var item = list.addItem(createInfo); $(".field").each(function () { var id = $(this).attr("ID"); var value = $(this).val(); item.set_item(id, value); }); item.update(); ctx.executeQueryAsync( Function.createDelegate(this, onAddContactSuccess), Function.createDelegate(this, onFail)); }
Deleting ListItem
Just to complete the example
function onDelete() { var ctx = SP.ClientContext.get_current(); var web = ctx.get_web(); var list = web.get_lists().getByTitle(LIST_NAME); var listItem = list.getItemById(selectedItemId); listItem.deleteObject(); ctx.executeQueryAsync( Function.createDelegate(this, onDeleteSuccess), Function.createDelegate(this, onFail)); }
Client OM with Silverlight
By now you are probably bored working with ListItem
s using the Client OM. Since there isn't much difference with the previous methods I'll
use Silverlight to focus on other things that can be done with the Client OM; in Managed code, JavaScript or Silverlight.
Specifically, with Silverlight I'll demonstrate working with WebParts on a page.

Upon loading, this Silverlight WebPart will read the WebParts on the designated page, default.aspx in this example, and display the titles in the ListBox.
private void LoadWebParts() { SelectedItem = null; wpList.Items.Clear(); using(ClientContext ctx = new ClientContext(SITE)) { try { // Get the default page for the site File file = ctx.Web.GetFileByServerRelativeUrl(PAGE); // Get the WebPart manager to locate all the WebParts LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared); // Load all WebPart definitions found on the page WPDefinitions = ctx.LoadQuery(wpm.WebParts.Include(w => w.Id, w => w.WebPart)); ctx.ExecuteQueryAsync(OnLoadSucceeded, OnFail); } catch(System.Exception ex) { MessageBox.Show(ex.Message); } } }
As you can see, like all of the other examples, you first get a CLientContext
to work with, using a server relative URL since the page is operating within a SharePoint site already.
Next is to get the File
to work with by using the well named method GetFileByServerRelativeUrl
. After this is getting the LimitedWebPartManager
to obtain a collection of WebPartDefinition
s from via the LoadQuery
method.
As with other examples I'm using a Lambda expression to limit the results to
only what is necessary, the ID
and WebPart
properties in this case. At this point using the Client OM with Silverlight will diverge from the other environments.
Since this code is being run from the main thread of the application you'll need
to use the asynchronous method, ExecuteQueryAsync
to make the query to prevent blocking the UI thread. This method takes delegates to ClientRequestSucceededEventHandler
and ClientRequestFailedEventHandler
that will be called when the method succeeds or fails.
private void OnLoadSucceeded(object sender, ClientRequestSucceededEventArgs e) { // Create delegate to update ListBox in UI thread Action updateList = () => { if(WPDefinitions.Count() != 0) { foreach(WebPartDefinition def in WPDefinitions) { wpList.Items.Add(new ListItemHelper() { ID = def.Id, Title = def.WebPart.Title }); } } }; this.Dispatcher.BeginInvoke(updateList); }
In this method I create an anonymous delegate to pass to the BeginInvoke
method that will iterate through the WebPartDefinition
collection populated by the LoadQuery
call.
Adding WebPart
Using the Client OM to add WebParts to a page is relatively straight forward.
private void OnUpdate(object sender, System.Windows.RoutedEventArgs e) { // Get the title and zone that may have been updated SelectedItem.Title = wpTitle.Text; SelectedItem.Zone = ((ComboBoxItem)wpZone.SelectedItem).Content.ToString(); // Start a worker thread to do the processing ThreadPool.QueueUserWorkItem(new WaitCallback(CSOMHelper.UpdateTitle), SelectedItem); // Update the controls wpList.Items[SelectedItem.ItemIndex] = wpTitle.Text; wpTitle.Text = ""; }
In this example I'll use a slightly different method to call the Client OM methods. Once again since this code is being executed in the main UI thread you can't use any blocking methods. In this case I use a worker thread to do the processing since there isn't anything that needs to be updated on the UI from the Client OM code.
public static void UpdateTitle(Object state) { ListItemHelper item = (ListItemHelper)state; using(ClientContext ctx = new ClientContext(SITE)) { // Get the default page for the site File file = ctx.Web.GetFileByServerRelativeUrl(PAGE); // Get the WebPart manager to locate all the WebParts LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared); // Load all WebPart definitions found on the page IEnumerable<WebPartDefinition> definitions = ctx.LoadQuery(wpm.WebParts.Include(w => w.Id, w => w.WebPart)); // Use the synchronous method here since the // method is being called in separate thread ctx.ExecuteQuery(); // Find the definition for the webpart to be updated WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID); if(def != null) { def.WebPart.Title = item.Title; def.MoveWebPartTo(item.Zone.ToLower(), 0); // Save the changes def.SaveWebPartChanges(); // Commit ctx.ExecuteQuery(); } } }
This method is just like the load method, get a ClientContext
and collection of WebPartDefinition
s. After finding the WebPart being updated you set the Title
property
and call the handy and well named method MoveWebPartTo
to move the WebPart to a new WebPartZone. Again, the well named method SaveWebPartChanges
saves the updates that have been made and the changes are committed by the call to ExecuteQuery
. Since
this is being executed within the worker thread you can use the synchronous
method. This shows that both synchronous and asynchronous methods are available
in the Silverlight implementation of the Client OM, unlike Managed Code where
only synchronous methods are available and JavaScript where only asynchronous
methods are available.
Deleting WebParts
Deleting WebParts is just as easy. Follow the same steps as above and call the DeleteWebPart
method.
// Find the definition for the webpart to be deleted WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID); if(def != null) { def.DeleteWebPart(); // Save the changes def.SaveWebPartChanges(); // Commit ctx.ExecuteQuery(); }
UserAction Menus
For the final example I'll demonstrate working with the UserActions
menu to add items to the EditControlBlock menu of a list and the Site Actions menu.
public static void AddUserActions() { using(ClientContext ctx = new ClientContext(URL)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); UserCustomActionCollection actions = list.UserCustomActions; UserCustomAction action = actions.Add(); action.Location = "EditControlBlock"; action.Sequence = 100; action.Title = "SPUG Custom Action"; action.Url = URL + @"/default.aspx"; action.Update(); ctx.Load(list, l => l.UserCustomActions); ctx.ExecuteQuery(); } }
Adding a menu item to the Site Actions menu is same as the above example, only the Location
and Group
properties need to be changed.
UserCustomAction action = actions.Add(); action.Location = "Microsoft.SharePoint.StandardMenu"; action.Group = "SiteActions"; action.Sequence = 101; action.Title = "SPUG Custom Action"; action.Url = URL + @"/default.aspx"; action.Update();
Removing the menu item just a matter of iterating through the UserCustomAction
s, finding the correct one and calling the DeleteObject
.
This code will remove menu items from both the EditControlBlock and Site Actions
menus since it is only comparing the title. Obviously, you'll want to check the Location
property to be more specific.
public static void RemoveUserActions() { using(ClientContext ctx = new ClientContext(URL)) { Web web = ctx.Web; List list = web.Lists.GetByTitle(LIST_NAME); UserCustomActionCollection actions = list.UserCustomActions; ctx.Load(list, l => l.UserCustomActions.Include(a => a.Title)); ctx.ExecuteQuery(); for(int x = 0; x < actions.Count; x++) { if(actions[x].Title == "SPUG Custom Action") { actions[x].DeleteObject(); ctx.ExecuteQuery(); break; } } } }
Creating a List
So far all of the examples have used previously created lists. Now I'll show how to create one and add Fields
to it.
Before creating a List
you should, of course, check if it already exists.
One somewhat awkward part of this is the Client OM will throw a ServerException
when the List
can't be found rather than just returning a null object.
List list = web.Lists.GetByTitle("CSOM List"); try { ctx.ExecuteQuery(); if(list != null) { list.DeleteObject(); ctx.ExecuteQuery(); } } catch { // ServerException is thrown if list does not exist }
After the check has been made and the List deleted if necessary, you can create the List
using the ListCreationInformation
. Just as with the ListItemCreationInformation
shown earlier, this objects applies information about the List
that is to be created.
// Set info about this list ListCreationInformation createInfo = new ListCreationInformation(); createInfo.Title = "CSOM List"; createInfo.QuickLaunchOption = QuickLaunchOptions.On; createInfo.TemplateType = (int)ListTemplateType.GenericList; // Add list to the web list = web.Lists.Add(createInfo); list.EnableFolderCreation = true; // Make sure to call Update to apply settings list.Update();
After the List
has been created you add the Fields
. The XML supplied to the AddFieldAsXml
method is the same as you would use in an Elements.xml to define Fields
being added to your by a SharePoint project.
// Add fields to the list Field field = list.Fields.AddFieldAsXml("<Field Type='Text' DisplayName='SomeField'></Field>", true, AddFieldOptions.DefaultValue); field.DefaultValue = "Hello, World"; // Don't forget to Update after setting values field.Update(); field = list.Fields.AddFieldAsXml(@"<Field Type='Choice' DisplayName='IsThisFun' Format='RadioButtons'> <Default>Yes<</Default> <CHOICES> <CHOICE>Yes</CHOICE> <CHOICE>No</CHOICE> <</CHOICES> </Field>", true, AddFieldOptions.DefaultValue);
Now that the List
and Fields
have been created you add ListItem
s as in previous examples.
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation(); ListItem item = list.AddItem(itemCreateInfo); item["Title"] = "One"; item.Update();
Adding ListItem with Folder
In the previous examples all ListItem
s have been added to the root folder of the list. However, there are cases when the ListItem
should be placed in folder.
In this example I've left out checking of the folder exists since the List is
being created here also but you should be able to understand from the previous
examples how to make that check.
// Create the folder first itemCreateInfo = new ListItemCreationInformation(); itemCreateInfo.LeafName = "New Folder"; itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder; item = list.AddItem(itemCreateInfo); item.Update();
Here I set the UnderlyingObjectType
property to FileSystemObjectType.Folder
and the LeafName
property to the name of the folder. Afterward, I use the FolderUrl
property
to the relative address of the folder that was created in this list before
adding the ListItem
// Now add item to folder itemCreateInfo = new ListItemCreationInformation(); itemCreateInfo.FolderUrl = "/Lists/CSOM List/New Folder"; item = list.AddItem(itemCreateInfo); item["Title"] = "Three"; item.Update();
Conclusion
The SharePoint 2010 Client Object Model is a great improvement over previous editions of SharePoint and opens SharePoint to wide array of applications and possibilities. Just like with SharePoint itself the Client OM is vast with many more features then I have time to cover here. Hopefully both of these articles have given you a good background on what the SharePoint Client Object Model is, how it works and a hint of the possibilities it presents.
History
Initial posting: 10/13/11