Programmatically adding Web Parts to Rich Content in SharePoint 2010
Deployment, Development, SharePoint 2010, Structured and repeatable deployment
SharePoint 2010 ships with the great ability of adding Web Parts to content areas. This allows you to easily extend your content with dynamic elements providing your users with richer experience. Similarly to using Web Parts with Web Part Zones you should include Web Parts in Rich Content in your structured and repeatable deployment. There are however a few differences in how you provision Web Parts to Rich Content and knowing how it all works can make your life easier.
Programmatically adding Web Parts 101
Programmatically adding Web Parts in SharePoint 2010 is pretty simple. To add a Web Part from code you need two things a reference to the page to which you want to add the Web Part and a reference to the Web Part itself.
If you want to add a Web Part from a .webpart/.dwp file you have to import it first. You can do this using the SPLimitedWebPartManager.ImportWebPart method.
You can programmatically use a Web Part to a page using the following code snippet:
using (SPSite site = new SPSite("http://win2008/sites/publishing"))
{
SPWeb web = site.RootWeb;
SPFile page = web.GetFile("Pages/Lipsum.aspx");
page.CheckOut();
using (SPLimitedWebPartManager wpmgr = page.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
XmlElement p = new XmlDocument().CreateElement("p");
p.InnerText = "Hello World";
ContentEditorWebPart cewp = new ContentEditorWebPart
{
Content = p
};
wpmgr.AddWebPart(cewp, "Header", 0);
}
page.CheckIn(String.Empty);
}
First we get a reference to the web where the page is located (1-3). Then we get a reference to the page itself (4). In order to add a Web Part to a page we need a reference to the SPLimitedWebPartManager (7). Then we instantiate a new Content Editor Web Part (11), we set some sample content (13) and we add it to the Header Web Part Zone on the page (15).
Now we know how you can programmatically add Web Parts to pages, let’s take a look at the differences when adding Web Parts to Rich Content in SharePoint 2010.
Programmatically adding Web Parts to Rich Content
As mentioned before SharePoint 2010 allows you to edit Web Parts to Rich Content. Just like when adding Web Parts to Web Part Zones it’s possible to programmatically add Web Parts to Rich Content as well. Before we however start hacking the code let’s have a look how Web Parts in Rich Content actually work.
It’s a kind of magic…
If you know the ASP.NET Web Part architecture you know that in order for Web Parts to work like a Web Part, meaning supporting customization and personalization, it must live inside a Web Part Zone. If you add a Web Part outside a Web Part Zone it will be nothing more than a control with fixed settings. So how is it possible that Web Parts in SharePoint 2010 Rich Content work and behave like Web Parts while being added in content?
Web Parts in Rich Content are made possible in SharePoint 2010 using a trick. Under the hood SharePoint 2010 uses a hidden Web Part Zone which stores all Web Parts added to Rich Content. The exact location in the content, where the Web Part should be rendered is marked with a marker and during the page rendering process, the Web Part is being rendered and injected at the location specified by its marker. Thanks to this you can use all of the Web Part functionality together with the flexibility of enriching your content with dynamic functionality.
So how does the above changes the code that we need to programmatically add a Web Part to Rich Content?
Adding Web Parts to Rich Content using code
The code needed to programmatically add a Web Part to Rich Content is the same as when adding Web Parts to Web Part Zones for the most part. We still need a reference to the page and the Web Part. The differences are around adding the Web Part to the page since we want the Web Part to be rendered in content instead of inside a Web Part Zone.
To better show you how adding Web Parts to Rich Content works I added some sample content to the page. I marked the location where the Web Part should be added with a | (pipe).
You can a Web Part to Rich Content using the following code snippet:
using (SPSite site = new SPSite("http://win2008/sites/publishing"))
{
SPWeb web = site.RootWeb;
SPFile page = web.GetFile("Pages/Lipsum.aspx");
page.CheckOut();
using (SPLimitedWebPartManager wpmgr = page.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
Guid storageKey = Guid.NewGuid();
string wpId = String.Format("g_{0}", storageKey.ToString().Replace('-', '_'));
XmlElement p = new XmlDocument().CreateElement("p");
p.InnerText = "Hello World";
ContentEditorWebPart cewp = new ContentEditorWebPart
{
Content = p,
ID = wpId
};
wpmgr.AddWebPart(cewp, "wpz", 0);
string marker = String.Format(CultureInfo.InvariantCulture, "<div class=\"ms-rtestate-read ms-rte-wpbox\" contentEditable=\"false\"><div class=\"ms-rtestate-read {0}\" id=\"div_{0}\"></div><div style='display:none' id=\"vid_{0}\"></div></div>", new object[] { storageKey.ToString("D") });
SPListItem item = page.Item;
string content = item["PublishingPageContent"] as string;
item["PublishingPageContent"] = content.Replace("|", marker);
item.Update();
}
page.CheckIn(String.Empty);
}
The first part is the same: as mentioned before we still need a reference to the web, the page and the SPLimitedWebPartManager. Nothing new here. Before we can add the Web Part to the page we have to set its ID (9-10, 17). This ID will be used by SharePoint during the rendering process to render the Web Part in the right place in Rich Content.
As mentioned before Web Parts placed in Rich Content are physically located in a hidden Web Part Zone. This Web Part Zone is called wpz. It’s a fixed prefix so it can be hardcoded in the code. In order to put the Web Part in Rich Content it has to be added to the wpz Web Part Zone (19).
The last part is to tell SharePoint to render the Web Part in a specific place in the Rich Content. To do this we have to replace the marker we placed in content with markup that will tell SharePoint what Web Part should be rendered there. This markup contains the ID of the Web Part which we specified before (21). Once we have the Web Part marker in place, we can edit the content and replace the marker with the Web Part marker (22-25).
After running the above code snippet we will get a Content Editor Web Part added to the given place in Rich Content:
Summary
SharePoint 2010 allows to add Web Parts to Rich Content. This allows you to easily extend your content with dynamic functionality providing your users with richer functionality. Whether you’re using Web Parts in Web Part Zones or in Rich Content it’s important to have it both covered in your deployment story. Although in both cases you’re dealing with Web Parts, provisioning Web Parts in Rich Content is slightly different than adding them to Web Part Zones. Still it can be easily done in a supported fashion.




October 1st, 2010 at 12:09 pm
Thanks for a really informative article! It's got me 99% of where I need to be… My only difference however, is that I need to insert a web part into the Rich Content field of the page whilst it is in edit mode.
the reason I need to do this, is becuase I have provisioned a custom button onto the RTE ribbon which will allow the user to insert a web part into the page at the current cursor position.
Where your code looks for a pre-designated marker (pipe symbol), my code would need to search for the current sursor position within the RTE and insert the web part at that point. Do you have any idea how I might be able to do this?
October 2nd, 2010 at 6:35 pm
@Nick Larter: to get the cursor position in the RTE you can use the JSOM RTE API RTE.Cursor.get_range().
October 22nd, 2010 at 8:42 am
@Nick Larter: I need the same functionality (custom button in the ribbon to add a web part in RTE in edit mode), but I can't view the Web Part in RTE… how do you do this ?
October 24th, 2010 at 12:51 pm
@Cyril: What do you exactly mean with "I can't view the Web Part in RTE"? You mean that after clicking your custom button the Web Part doesn't appear where you've added it?
October 29th, 2010 at 3:54 pm
Nice article but i am getting exception because item["PublishingPageContent"] is returning null.
October 30th, 2010 at 8:22 am
@farhan: then you need to add a check for null values before proceeding with adding Web Parts.
November 23rd, 2010 at 1:07 am
Nice article and the best I could find out there but I am running into this issue. I am trying to remove all content from SitePages/Home.aspx and I get this error when I run above code for my page:
Column 'PublishingPageContent' does not exist. It may have been deleted by another user.
Any suggestions?
November 23rd, 2010 at 8:25 am
@Z: That's because it's a Wiki Page. When working with Wiki Pages you should use the WikiField instead.
January 19th, 2011 at 5:27 pm
Great article. One question… is there a maximum number of web parts or Rich Content Fields that I can add to a page? The reason I ask is that I am adding 13 web parts to 13 different Rich Content Fields. At around 11, the page starts behaving strangely with the chromes displaying when they are set to "None." Have you ever seen this? Thanks!
January 19th, 2011 at 6:35 pm
@Jake: I'm not aware of such limit, but I'm curious about your scenario. 13 seems pretty much to me.
March 31st, 2011 at 3:32 pm
Hi Waldek,
Great post.
Do you know if you can add a custom web part to a content page using this method? Do you have any code samples on how to do that?
Thanks
Andy
April 1st, 2011 at 4:28 am
@Andy: absolutely! In my example I used the Content Editor Web Part but any Web Part would do. In the above code (lines 14-18) you would have to replace the CEWP with your custom Web Part and you're done.
May 20th, 2011 at 11:59 am
This is really good stuff. But how to achieve this kind of functionality in Powershell ?
May 20th, 2011 at 12:22 pm
@Jigar: It would work exactly the same way. The only difference is probably the language syntax, ie. PowerShell instead of c#.
May 20th, 2011 at 12:31 pm
I tried to convert in Powershell..But similar code does not work..
May 20th, 2011 at 12:38 pm
@Jigar: any specific piece?
May 20th, 2011 at 12:42 pm
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site = Get-SPSite($url)
$web=Get-SPWeb($url)
$file = $web.GetFile("http://usctapd00133:1000/Pages/Test1.aspx")
$webpartmanager=$web.GetLimitedWebPartManager("http://usctapd00133:1000/Pages/Test1.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$storage = [guid]::NewGuid()
$storageKey = $storage.ToString().Replace('-', '_')
$wpId = $([System.String]::Format("g_{0}",$storageKey))
$webpart = new-object Microsoft.SharePoint.WebPartPages.ContentEditorWebPart
$webpart.ChromeType = [System.Web.UI.WebControls.WebParts.PartChromeType]::TitleOnly;
$webpart.Title = "Test WP"
$docXml = New-Object System.Xml.XmlDocument
$contentXml = $docXml.CreateElement("Content");
$contentXml.set_InnerText($content);
$docXml.AppendChild($contentXml);
$webpart.Content = $contentXml;
$webpartmanager.AddWebPart($webpart, "wpz", 0);
write-host $storageKey
$marker = $([System.String]::Format("",[System.String]::$storageKey))
$item = $file.Item
$content = $item["PublishingPageContent"]
$item["PublishingPageContent"] = $content.Replace("|", $marker)
$item.Update()
May 20th, 2011 at 12:53 pm
@Jigar: and what's exactly not working?
May 20th, 2011 at 12:55 pm
Web part is not getting created.
May 20th, 2011 at 1:57 pm
@Jigar: are you getting any errors or simply no response and no Web Part?
May 20th, 2011 at 2:14 pm
No Error. Web part is not getting created.
May 20th, 2011 at 2:22 pm
@Jigar: Have you checked if the Web Part is not being added to the wpz Zone? It is a hidden zone you can do it using PowerShell/code.
May 20th, 2011 at 2:25 pm
Can you please provide me with a Powershell script ?
June 1st, 2011 at 10:52 am
i am getting exception because item["PublishingPageContent"] is returning null.On debugging i found that Column 'PublishingPageContent' does not exist.
July 28th, 2011 at 11:27 am
When I insert a webpart in a RichTextEditor control on a page layout. It does not float around the text (like say an Image).
I checked the source and it seems to be emitting a table covering the web part.
Is there a way I could make the text wrap around the web part? i.e. make the webpart float left / right within the RTF control?
Thanks in advance.
July 28th, 2011 at 3:30 pm
@Hiren: you could use a Control Adapter for the Rich Text Editor to remove the table surrounding the Web Part. The only thing that would remain would be a div which you could float using CSS.
August 17th, 2011 at 4:13 am
I have tried using the same code and notice that the webpart isn't visible on the page. But when I do http:///SitePages/Home.aspx?contents=1 I see that the webpart is added. Even though I set visible = true for the webpart it doesn't show up. Any work arounds for this?
August 17th, 2011 at 6:16 am
@Pavan: The code above has been created for Publishing Pages hence the reference to the PublishingPageContent. In non-publishing pages like the one you have mentioned the field is called differently so you would have to find out how it's called first and then use that field instead.
February 22nd, 2012 at 5:16 pm
Where exactly do you put the above codes in a SP project in VS 2010?
February 23rd, 2012 at 5:17 pm
@Gary: Any place you could image. Most common would probably be a Console App or a Feature Receiver.
March 5th, 2012 at 10:57 pm
This may help Jigar, my webpart didn't display at first because my key was being wrapped by "{" "}" which as to deal with the storageKey.ToString("D").
Very good post, Thanks. It was very helpful.
I will post what I did for PowerShell users.
I needed to add web parts to display existing lists to a WikiPageHomePage via PowerShell.
I preloaded the $page.item["Wikifield"] with the html I found for the "Text layout" One Column with Sidebar and put in some text for me to replace
$html = " +
" +
'LEFTSIDE1 LEFTSIDE2 LEFTSIDE3 LEFTSIDE4' +
" +
" +
'RIGHTSIDE1 RIGHTSIDE2 RIGHTSIDE3' +
" +
'false,false,2'
I also found a good script for creating lists: http://sharepointryan.com/tag/powershellsharepoint-2010-new-splistsp2010-object-model/
Then I passed placeholder to the AddWebpart function: AddXlstListViewWebpart-WikiPage -Url $url -SPList $list -ListUrl "Announcements" -StringToReplace "LEFTSIDE3"
function AddXlstListViewWebpart-WikiPage($url, $SPList, $ListUrl, $stringToReplace)
{
Start-SPAssignment -Global
$SPWeb = Get-SPWeb -Identity $url
$page = $SPWeb.GetFile("SitePages/Home.aspx")
$page.CheckOut()
$webpart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$storagekey = [guid]::NewGuid()
$wpID = "g_" + $storagekey.ToString().Replace("-", "_")
$webpart.ID = $wpID
$webpart.ListUrl = $url + "/Lists/" + $ListUrl
$webpart.ListName = $SPList.Title
$webpart.ListDisplayName = $SPList.Title
$webpart.ChromeType = "Default"
$webpart.Visible = $true
$webpart.Hidden = $false
$webpartmanager = $page.GetLimitedWebPartManager([System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$webpartmanager.AddWebPart($webpart, "wpz", 0);
$webpartmanager.Dispose()
$html = " +
"
$content = [string] $page.item["WikiField"]
$page.item["WikiField"] = $content.Replace($stringToReplace, $html)
$page.Item.Update()
$page.Update()
$page.CheckIn("")
$SPWeb.Dispose()
Stop-SPAssignment -Global
}
March 5th, 2012 at 11:01 pm
Sorry, The Html didn't show up.
September 14th, 2012 at 11:48 am
Travis,
Tried the same code without the HTML part. The webpart appears in the Webpart maintenance page but doesnt appear in the page itself. Here is the code.
function AddXlstListViewWebpart-WikiPage($url, $SPList, $ListUrl)
{
Write-Host $url+$SPList.Title+$ListUrl
Start-SPAssignment -Global
$SPWeb = Get-SPWeb -Identity $url
$page = $SPWeb.GetFile("SitePages/Home.aspx")
$webpart = New-Object Microsoft.SharePoint.WebPartPages.XsltListViewWebPart
$storagekey = [guid]::NewGuid()
$wpID = "g_" + $storagekey.ToString().Replace("-", "_")
Write-Host $wpID
$webpart.ID = $wpID
$webpart.ListUrl = $url + "/Lists/" + $ListUrl
Write-Host $webpart.ListUrl
$webpart.ListName = $spList.ID
$webpart.ListDisplayName = $spList.Title
$webpart.ChromeType = "Default"
$webpart.Visible = $true
$webpart.Hidden = $false
$webpartmanager = $page.GetLimitedWebPartManager([System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$webpartmanager.AddWebPart($webpart, "wpz", 0);
$page.Item.Update()
$page.Update()
$webpartmanager.Dispose()
$SPWeb.Dispose()
Stop-SPAssignment -Global
}
$web = Get-SPWeb http://blrkec141899d:2012/sites/Bristow/
$spList = $web.Lists["Announcements"]
$list = $web.Lists["Announcements"]
AddXlstListViewWebpart-WikiPage $web.Url $spList "Announcements"
December 6th, 2012 at 2:23 am
I'm in the same boat as KK. :(