SharePoint 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 aListItemCollection
containing all the Contacts in the demo list.
Hide Shrink Copy Code
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
Hide Copy Code
<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 theListItem
to be updated, update the FieldValues
, then commit the changes.
Hide Copy Code
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 newListItem
to the List
is almost as easy as updating. One of the main differences though is the ListItemCreationInformation
object.
Hide Shrink Copy Code
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.
Hide Shrink Copy Code
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.
Hide Shrink Copy Code
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 aListItem
is the simplest of the operations. After obtaining the ListItem
, call it's DeleteObject
method and commit the changes with an ExecuteQuery
.
Hide Copy Code
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(); } }
Hide Shrink Copy Code
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.
Hide Shrink Copy Code
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.
Hide Shrink Copy Code
/// 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.
Hide Copy Code
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.
Hide Copy 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.
Hide Shrink Copy Code
/// 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.
Hide Copy Code
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 theset_item
method rather than using the indexer in Managed Code.
Hide Copy 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.
Hide Copy Code
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
Hide Copy Code
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 withListItem
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.
Hide Copy Code
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.
Hide Copy Code
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.
Hide Copy Code
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.
Hide Shrink Copy 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 theDeleteWebPart
method.
Hide Copy Code
// 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 theUserActions
menu to add items to the EditControlBlock menu of a list and the Site Actions menu.
Hide Copy Code
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.
Hide Copy Code
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.
Hide Copy Code
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 addFields
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.
Hide Copy Code
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.
Hide Copy Code
// 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.
Hide Copy Code
// 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.
Hide Copy Code
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation(); ListItem item = list.AddItem(itemCreateInfo); item["Title"] = "One"; item.Update();
Adding ListItem with Folder
In the previous examples allListItem
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.
Hide Copy Code
// 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
Hide Copy Code
// Now add item to folder itemCreateInfo = new ListItemCreationInformation(); itemCreateInfo.FolderUrl = "/Lists/CSOM List/New Folder"; item = list.AddItem(itemCreateInfo); item["Title"] = "Three"; item.Update();
Nice,.. We are very appreciable about your hard work for writing these SharePoint blogs.
ReplyDeleteSharePoint 2013 Online Training