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);
ListItem item = list.GetItemById(id);
item["Title"] = lastName;
item["FirstName"] = firstName;
item["WorkAddress"] = address;
item["WorkCity"] = city;
item["WorkState"] = state;
item["WorkZip"] = postalCode;
item.Update();
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);
ListItemCreationInformation createInfo = null;
ListItem item = list.AddItem(createInfo);
item["Title"] = lastName;
item["FirstName"] = firstName;
item["WorkAddress"] = address;
item["WorkCity"] = city;
item["WorkState"] = state;
item["WorkZip"] = postalCode;
item.Update();
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:
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);
ListItem item = list.GetItemById(id);
item.DeleteObject();
ctx.ExecuteQuery();
}
}
POST http:
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.
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>" +
"</View>";
camlQuery.set_viewXml(queryXml);
this.listItems = list.getItems(camlQuery);
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 onLoadContactsSuccess(sender, args)
{
$("#contactList").empty();
if(this.listItems.get_count() == 0)
{
$("#contactList").append("<span>No contacts found</span>");
}
else
{
var listEnumerator = this.listItems.getEnumerator();
while (listEnumerator.moveNext())
{
var item = listEnumerator.get_current();
var title = item.get_fieldValues().Title;
var firstName = item.get_fieldValues().FirstName;
var id = item.get_fieldValues().ID;
var contact = "<span önclick='displayContactDetails(" + id + ")'>" + firstName + " " + title + "</span>";
$("#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);
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);
var createInfo = null;
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
{
File file = ctx.Web.GetFileByServerRelativeUrl(PAGE);
LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
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)
{
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)
{
SelectedItem.Title = wpTitle.Text;
SelectedItem.Zone = ((ComboBoxItem)wpZone.SelectedItem).Content.ToString();
ThreadPool.QueueUserWorkItem(new WaitCallback(CSOMHelper.UpdateTitle), SelectedItem);
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))
{
File file = ctx.Web.GetFileByServerRelativeUrl(PAGE);
LimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
IEnumerable<WebPartDefinition> definitions = ctx.LoadQuery(wpm.WebParts.Include(w => w.Id, w => w.WebPart));
ctx.ExecuteQuery();
WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID);
if(def != null)
{
def.WebPart.Title = item.Title;
def.MoveWebPartTo(item.Zone.ToLower(), 0);
def.SaveWebPartChanges();
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.
WebPartDefinition def = definitions.FirstOrDefault(d => d.Id == item.ID);
if(def != null)
{
def.DeleteWebPart();
def.SaveWebPartChanges();
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
{
}
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.
ListCreationInformation createInfo = new ListCreationInformation();
createInfo.Title = "CSOM List";
createInfo.QuickLaunchOption = QuickLaunchOptions.On;
createInfo.TemplateType = (int)ListTemplateType.GenericList;
list = web.Lists.Add(createInfo);
list.EnableFolderCreation = true;
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.
Field field = list.Fields.AddFieldAsXml("<Field Type='Text' DisplayName='SomeField'></Field>",
true, AddFieldOptions.DefaultValue);
field.DefaultValue = "Hello, World";
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.
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
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