Programmatically adding Web Parts to Rich Content in SharePoint 2010

, , ,

'Add Web Part to Rich Content' button on the SharePoint 2010 Ribbon
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).

Publishing Page with Content Editor Web Part on it

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).

SharePoint 2010 Publishing Page with some sample content. Red arrow points to the | character which marks the place where the Web Part should be added.

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:

SharePoint 2010 Publishing Page with some sample content. Red arrows points to a Content Editor Web Part added 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.

Technorati Tags:

34 Responses to “Programmatically adding Web Parts to Rich Content in SharePoint 2010”

  1. Nick Larter Says:

    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?

  2. Waldek Mastykarz Says:

    @Nick Larter: to get the cursor position in the RTE you can use the JSOM RTE API RTE.Cursor.get_range().

  3. Cyril Berber Says:

    @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 ?

  4. Waldek Mastykarz Says:

    @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?

  5. farhan faiz Says:

    Nice article but i am getting exception because item["PublishingPageContent"] is returning null.

  6. Waldek Mastykarz Says:

    @farhan: then you need to add a check for null values before proceeding with adding Web Parts.

  7. Z Says:

    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?

  8. Waldek Mastykarz Says:

    @Z: That's because it's a Wiki Page. When working with Wiki Pages you should use the WikiField instead.

  9. Jake Jacobsen Says:

    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!

  10. Waldek Mastykarz Says:

    @Jake: I'm not aware of such limit, but I'm curious about your scenario. 13 seems pretty much to me.

  11. Andy Fitch Says:

    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

  12. Waldek Mastykarz Says:

    @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.

  13. Jigar Says:

    This is really good stuff. But how to achieve this kind of functionality in Powershell ?

  14. Waldek Mastykarz Says:

    @Jigar: It would work exactly the same way. The only difference is probably the language syntax, ie. PowerShell instead of c#.

  15. Jigar Says:

    I tried to convert in Powershell..But similar code does not work..

  16. Waldek Mastykarz Says:

    @Jigar: any specific piece?

  17. Jigar Says:

    [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()

  18. Waldek Mastykarz Says:

    @Jigar: and what's exactly not working?

  19. Jigar Says:

    Web part is not getting created.

  20. Waldek Mastykarz Says:

    @Jigar: are you getting any errors or simply no response and no Web Part?

  21. Jigar Says:

    No Error. Web part is not getting created.

  22. Waldek Mastykarz Says:

    @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.

  23. Jigar Says:

    Can you please provide me with a Powershell script ?

  24. Rebecca Says:

    i am getting exception because item["PublishingPageContent"] is returning null.On debugging i found that Column 'PublishingPageContent' does not exist.

  25. Hiren Says:

    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.

  26. Waldek Mastykarz Says:

    @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.

  27. Pavan Says:

    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?

  28. Waldek Mastykarz Says:

    @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.

  29. Gary Concepcion Says:

    Where exactly do you put the above codes in a SP project in VS 2010?

  30. Waldek Mastykarz Says:

    @Gary: Any place you could image. Most common would probably be a Console App or a Feature Receiver.

  31. Travis Jurgens Says:

    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
    }

  32. Travis Jurgens Says:

    Sorry, The Html didn't show up.

  33. KK Says:

    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"

  34. Paul Says:

    I'm in the same boat as KK. :(

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS
Copyright © 2007 - 2013 Waldek Mastykarz

Creative Commons License