SP2010: Programmatically creating a web part page with connected webparts

In my quest of creating dynamic sites in code, I stumbled upon a problem. What to do with the content of sites you’re creating dynamically? You can create lists fairly easy, creating things like views is a little more complicated but still doable. But when it comes to things like pages, things get a little bit more tricky. As long as your content is static, you can create a feature to deploy an aspx page to either ghosted or unghosted stored (in the database or in the filesystem). But if you want to show list data, for instance, or even more advanced: what about BCS data from a LOB system, based upon the site the page is located in? Well I’ve got it all figured out now, so I guessed that would be something to share!

First station: creating webpart pages dynamically. This is a fairly easy thing to do. You need to copy one of the templates which are located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\1033\STS\DOCTEMP\SMARTPGS. These are the same templates you can choose from when creating a webpart page from within SharePoint or SharePoint Designer. You can copy these and use them to create your own templates from; actually something we’ll need to do later on.

Here’s the code you need to create the page:

FileStream stream = new
FileStream(Path.Combine(hive, templateFilename), FileMode.Open);

SPFolder libraryFolder = web.GetFolder(foldername);

SPFileCollection files = libraryFolder.Files;

SPFile file = files.Add(pagename, stream);

One thing which might need explaining is the hive variable, not that hard but important:

static
string hive = SPUtility.GetGenericSetupPath(“TEMPLATE\\1033\\STS\\DOCTEMP\\SMARTPGS\\”);

Ok, so this should set you up with a nice and clean webpartpage, based upon one of the templates. Now for adding the webparts to it.

The easiest thing to do is to setup the webparts on an existing page in SharePoint (you could use the one you just created, but anyone will do). You can use SharePoint or SharePoint designer, that doesn’t make a difference. Once you’ve got the webpart setup the way you want it to (don’t bother to make connections, that’ll be useless), export it. This can be done in SharePoint designer by selecting the webpart, choosing the “Web Part” ribbon menu and clicking “To file”

Now once you’ve saved your webpart file, you can add it to your new webpartpage with the following code:

SPLimitedWebPartManager wpmngr = file.Web.GetLimitedWebPartManager(file.ServerRelativeUrl, scope);

string error = string.Empty;

FileStream file = new
FileStream(webpartFileLocation, FileMode.Open);

System.Web.UI.WebControls.WebParts.WebPart webpart = wpmngr.ImportWebPart(XmlReader.Create(file), out error);

wpmngr.AddWebPart(webpart, zone, 0);

Couple of things to explain… You need to get the SPLimitedWebPartManager object to add the part to the page. You can feed the manager the XML from the webpart file which is loaded in a FileStream which is then fed into a XmlReader. The out parameter will contain any error messages, I’m not sure why that approach was chosen, I like exceptions more so you could just wrap the whole thing to throw an exception when something goes wrong:

try

{


FileStream file = new
FileStream(webpartFileLocation, FileMode.Open);

System.Web.UI.WebControls.WebParts.WebPart webpart = wpmngr.ImportWebPart(XmlReader.Create(file), out error);

wpmngr.AddWebPart(webpart, zone, 0);


return webpart;

}

catch (Exception ex)

{


if (!String.IsNullOrEmpty(error))


throw
new
Exception(error);


else


throw;

}

If you now view your page, you webpart should be on there. A little tip: when you use a webpart which is styled with XSLT (like most BCS parts for instance), you can edit the XML of the webpart, strip the XSL code and use the XslLink property to link the webpart to an actual XSLT file. That way, if you deploy the page multiple times; all parts link to the same XSLT and updating the look-and-feel becomes real easy.

Now for the last step: connecting the webparts. I’ll share the complete method which I use for this purpose:

///
<summary>

/// Connects two webparts to each other

///
</summary>

///
<param name=”consumerWebPart”>The webpart which will consume the data</param>

///
<param name=”consumerInterfaceType”>The type of interface the consumer endpoint should have</param>

///
<param name=”providerWebPart”>The webpart which will provide the data</param>

///
<param name=”providerInterfaceType”>The type of interface the provider endpoint should have</param>

public
void ConnectBusinessDataWebParts(

System.Web.UI.WebControls.WebParts.WebPart consumerWebPart,


string consumerConnectionPoint,

System.Web.UI.WebControls.WebParts.WebPart providerWebPart,


string providerConnectionPoint,


WebPartTransformer transformer)

{


// Get the connection point for the consumer.

System.Web.UI.WebControls.WebParts.ConsumerConnectionPointCollection consumerConnections =

wpmngr.GetConsumerConnectionPoints(consumerWebPart);


ConsumerConnectionPoint consumerConnection = null;


foreach (ConsumerConnectionPoint cpoint in consumerConnections)

{


if (cpoint.ID == consumerConnectionPoint)

{

consumerConnection = cpoint;


break;

}

}


// Get the connection point for the provider.

System.Web.UI.WebControls.WebParts.ProviderConnectionPointCollection providerConnections =

wpmngr.GetProviderConnectionPoints(providerWebPart);


ProviderConnectionPoint providerConnection = null;


foreach (ProviderConnectionPoint ppoint in providerConnections)

{


if (ppoint.ID == providerConnectionPoint)

{

providerConnection = ppoint;


break;

}

}


// when a transformer was used; create a new connection based on it; otherwise without one


if (transformer != null)

{

Microsoft.SharePoint.WebPartPages.SPWebPartConnection newConnection =

wpmngr.SPConnectWebParts(

providerWebPart, providerConnection,

consumerWebPart, consumerConnection, transformer);

wpmngr.SPWebPartConnections.Add(newConnection);

}


else

{

Microsoft.SharePoint.WebPartPages.SPWebPartConnection newConnection =

wpmngr.SPConnectWebParts(

providerWebPart, providerConnection,

consumerWebPart, consumerConnection);

wpmngr.SPWebPartConnections.Add(newConnection);

}

}

Ok, lets go through the parameters: both webparts are passed in since we want to connect them. A webpart can have multiple connection points, so we need to specify which point we want to use. I do that by string, which you need to find out just by debugging. The naming differs from webpart to webpart, so don’t guess: debug! The last parameter is an optional WebPartTransformer. When both endpoints of the webparts have a different interface type, a transformer is needed to convert the value of one to match the type of the other. There are a couple of transformers:

TransformableFilterValuesToEntityInstanceTransformer

TransformableFilterValuesToFieldTransformer

TransformableFilterValuesToFilterValuesTransformer

TransformableFilterValuesToParametersTransformer

With some of them (TransformableFilterValuesToParametersTransformer for instance) you need to specify the fieldnames of provider and/or consumer. You’ll get an exception if you don’t sometimes, but other times it just doesn’t work without an error; so keep an eye open for that.

Ok, you now have all the building blocks to create a new webpartpage, add webparts to it and connect them with each other.

One last tip: depending on the settings of your library, you may need to either Publish or Check-in your page. Both actions can be done on your SPFile object.

Enjoy!

Leave a Reply

Your email address will not be published. Required fields are marked *