Time to wrap up this series and we’re going to mostly talk about managing web parts using the client object model. First, however, we’re going to take a quick peek at something else you can do, which is create additional Edit Control Block (ECB) menu items. Yes, you can even create ECB items from the client OM. Here’s how we do that:
//get the connection
ClientContext ctx = new ClientContext("http://foo");
//get the list
List theList = ctx.Web.Lists.GetByTitle("My List Title");
//load the custom actions collection
ctx.Load(theList.UserCustomActions);
ctx.ExecuteQuery();
//*****************************************************
//here’s one way of finding a menu item and deleting it
foreach (UserCustomAction uca in theList.UserCustomActions)
{
if (uca.Title == "My New Menu")
{
uca.DeleteObject();
break;
}
}
//*****************************************************
//create the custom action
UserCustomAction act = theList.UserCustomActions.Add();
//set the properties and update
act.Location = "EditControlBlock";
act.Sequence = 100;
act.Title = "My New Menu";
act.Url = "/_layouts/settings.aspx";
act.ImageUrl = "/_layouts/images/availableworkflow.gif";
act.Update();
//execute the query to add everything
ctx.ExecuteQuery();
There you have it. Navigate to the site, click to get the ECB menu and you should see your new menu item with the icon you specified for the ImageUrl. Note that you can also use this same methodology to place an item on the Site Actions menu. The differences are:
1) Instead of getting the UserCustomActions collection from a list, get it from the Web
2) Instead of the location being “EditControlBlock”, it should be “Microsoft.SharePoint.StandardMenu”
3) You need to set the Group property to “SiteActions”
Try it out and you’ll see it works pretty well. Now, when we start working with web parts we have one class that probably looks pretty familiar and one new methodology that we haven’t seen yet. The familiar class is the LimitedWebPartManager, which is the same class that was used in SharePoint 2007 to manage the web parts on a page. The new methodology is the way in which we get access to the LimitedWebPartManager class – we’re going to do that through the File class in the client object model. The pattern will start out in the same familiar way – we’ll get our ClientContext. Next though we’ll instantiate our File class object using the relative path to a web part page. In a publishing site that will typically be something like “/pages/default.aspx”. For one of the new team sites it will be something like “/SitePages/Home.aspx”.
Once we have a reference to the web part page, then we can get an instance of the LimitedWebPartManager class and start working with web parts on the page in much the same way we did in SharePoint 2007. Here’s how that code might look when working with a team site home page:
//get the connection
ClientContext ctx = new ClientContext("http://foo");
//get the home page
File home = ctx.Web.GetFileByServerRelativeUrl("/SitePages/home.aspx");
//get the web part manager
LimitedWebPartManager wpm =
home.GetLimitedWebPartManager(PersonalizationScope.Shared);
IEnumerable<WebPartDefinition> wpds = null;
//create the LINQ query to get the web parts from
//the web part definition collection
wpds = ctx.LoadQuery(
wpm.WebParts.Include(
wp => wp.Id,
wp => wp.WebPart));
//load the list of web parts
ctx.ExecuteQuery();
//enumerate the results
if (wpds.Count() == 0)
//no web parts found on this page
else
{
foreach (WebPartDefinition wpd in wpds)
{
//do something with the web part definition,
//or the web part via its WebPart property
}
}
That’s how we can enumerate through all of the web parts on any web part page. Now let’s suppose that we want to change a property on the web part. We use a similar pattern to what has been demonstrated in the earlier posts in this series. We can get it by Id and then work with it. Here’s an example of doing that to change the Title property of the part. Note that the code below includes a checkout and checkin. It obviously isn’t usually necessary for a team site but is for a publishing site. If you’re not sure, you can call checkin and checkout and it won’t hurt if the list doesn’t require it.
//get the connection
ClientContext ctx = new ClientContext("http://foo");
//get the home page
File home = ctx.Web.GetFileByServerRelativeUrl("/SitePages/home.aspx");
//get the web part manager
LimitedWebPartManager wpm =
home.GetLimitedWebPartManager(PersonalizationScope.Shared);
//load the web part definitions
ctx.Load(wpm.WebParts);
ctx.ExecuteQuery();
//checkout in case it's needed; doesn't hurt if it doesn't
home.CheckOut();
//get the web part definition
WebPartDefinition wpd =
wpm.WebParts.GetById(MyWebPartId);
//set the title
wpd.WebPart.Title = "My Web Part Title";
//save changes
wpd.SaveWebPartChanges();
//checkin in case it's needed; doesn't hurt if it doesn't
home.CheckIn(string.Empty, CheckinType.MajorCheckIn);
//execute the query to save changes
ctx.ExecuteQuery();
Okay, now for my final trick we’ll look at how to add and delete web parts from a page. To add a web part I’m going to wimp out a little and just show an example of adding a web part by using a chunk of Xml. You can obviously do it other ways, such as providing the assembly and class as well. You would do that by creating a new instance of the WebPart class and then using the LimitedWebPartManager to add it to the page. This is essentially the same process I covered for SharePoint 2007 in my blog posting about customizing My Sites, which you can find at http://blogs.msdn.com/sharepoint/archive/2007/03/22/customizing-moss-2007-my-sites-within-the-enterprise.aspx. As a side note this process will be much easier in SharePoint 2010 by using the new site created event. But I digress; here’s the code to add a web part to a page using a chunk of Xml:
//get the connection
ClientContext ctx = new ClientContext("http://foo");
//get the home page
File home = ctx.Web.GetFileByServerRelativeUrl("/SitePages/home.aspx");
//checkout in case it's needed; doesn't hurt if it doesn't
home.CheckOut();
//get the web part manager
LimitedWebPartManager wpm =
home.GetLimitedWebPartManager(PersonalizationScope.Shared);
string webPartXml =
"<?xml version=/"1.0/" encoding=/"utf-8/"?><WebPart xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=/"http://www.w3.org/2001/XMLSchema/" xmlns=/"http://schemas.microsoft.com/WebPart/v2/"> <Title>My test web part</Title> <FrameType>Default</FrameType> <Description>Use for formatted text, tables, and images.</Description> <IsIncluded>true</IsIncluded> <ZoneID>Header</ZoneID> <PartOrder>0</PartOrder> <FrameState>Normal</FrameState> <Height /> <Width /> <AllowRemove>true</AllowRemove> <AllowZoneChange>true</AllowZoneChange> <AllowMinimize>true</AllowMinimize> <AllowConnect>true</AllowConnect> <AllowEdit>true</AllowEdit> <AllowHide>true</AllowHide> <IsVisible>true</IsVisible> <DetailLink /> <HelpLink /> <HelpMode>Modeless</HelpMode> <Dir>Default</Dir> <PartImageSmall /> <MissingAssembly>Cannot import this Web Part.</MissingAssembly> <PartImageLarge>/_layouts/images/mscontl.gif</PartImageLarge> <IsIncludedFilter /> <Assembly>Microsoft.SharePoint, Version=13.0.0.0, Culture=neutral, PublicKeyToken=94de0004b6e3fcc5</Assembly> <TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName> <ContentLink xmlns=/"http://schemas.microsoft.com/WebPart/v2/ContentEditor/" /> <Content xmlns=/"http://schemas.microsoft.com/WebPart/v2/ContentEditor/"><![CDATA[This is my test text! <DIV> </DIV><DIV>Blah blah blah</DIV><DIV> </DIV><DIV>And another blah</DIV>]]></Content> <PartStorage xmlns=/"http://schemas.microsoft.com/WebPart/v2/ContentEditor/" /></WebPart>";
//create the web part definition
WebPartDefinition newWpd = wpm.ImportWebPart(webPartXml);
//add the web part
wpm.AddWebPart(newWpd.WebPart, "Left", 1);
//checkin in case it's needed; doesn't hurt if it doesn't
home.CheckIn(string.Empty, CheckinType.MajorCheckIn);
//execute the query to add everything
ctx.ExecuteQuery();
And now, we delete a web part from the page:
//get the connection
ClientContext ctx = new ClientContext("http://foo");
//get the home page
File home = ctx.Web.GetFileByServerRelativeUrl("/SitePages/home.aspx");
//checkout in case it's needed; doesn't hurt if it doesn't
home.CheckOut();
//get the web part manager
LimitedWebPartManager wpm =
home.GetLimitedWebPartManager(PersonalizationScope.Shared);
//load the web part definitions
ctx.Load(wpm.WebParts);
ctx.ExecuteQuery();
//get the web part
WebPartDefinition wpd = wpm.WebParts.GetById(MyWebPartId);
//delete the part
wpd.DeleteWebPart();
//checkin in case it's needed; doesn't hurt if it doesn't
home.CheckIn(string.Empty, CheckinType.MajorCheckIn);
//execute the query to do the deletion
ctx.ExecuteQuery();
There you go folks, a pretty good run through the different features of the client object model. Hopefully you can appreciate now all of functionality this new set of classes provides, and it instills happy happy joy joy feelings in SharePoint developers everywhere. I hope you found this series useful and can start writing some killer client object model applications out there. I had a lot of fun putting this together and hope you enjoyed it too.