Monday, September 15, 2008

SharePoint - using SPLimitedWebPartManager to create WebPart Connections

Scenario: A .wsp solution is deployed on SharePoint Server, which has publishing feature running on it. A new site is created based on a "site definition" as defined by Onet.xml (deployed in 12 hive directory by .wsp file). Doing so, number of new webparts available in WebPart gallery and hundereds of publishing pages based on different layout pages. 

Problem Statement: What I want is, weparts should be populated in all content pages (which in turn are derived from layout pages) along with connections binding them together. 

Solution: SPLimitedWebPartManager to rescue. There are multiple ways to achive the
desired result.

Method 1 : When provisioning a site
       or
Method 2 :  When activating a feature
      or
Method 3 : If content pages are based on layout pages, then add inline code/web control/user control in layout page

Method 1,2 and 3 are just delivery boys. What follows is the actual implementation of business case.

Comming right to the point, one should laverage functionality of SPLimitedWebPArtManger to add and associate webparts. Lets breakdown entire process in 3 simple steps
  1. Fetch WebParts
  2. Add WebParts
  3. Create WebPart Connections
1. Fetch WebPart

[code]
 private System.Web.UI.WebControls.WebParts.WebPart
FetchWebPart(
SPWeb web, String webPartName)
{
SPQuery query = new SPQuery();
  // CAML query fetch list item with name equal to webPartName
query.Query =
"<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>" +webPartName + "</Value></Eq></Where>";

SPList webPartGallery = null;
// webpart gallery is available at root web only
            if (null == web.ParentWeb)
{
// This is the root web.
webPartGallery = web.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
else
{
  SPWeb fetchRootWeb = web;
  // fetch root web
  while(fetchRootWeb.ParentWeb!=null)
  {
    fetchRootWeb = fetchRootWeb.ParentWeb;
  }                
                webPartGallery = fetchRootWeb.GetCatalog(
SPListTemplateType.WebPartCatalog);
}
// pass query object to item collection
SPListItemCollection webParts = webPartGallery.GetItems(query);

String typeName = webParts[0].GetFormattedValue("WebPartTypeName");
String assemblyName = webParts[0].GetFormattedValue("WebPartAssembly");
ObjectHandle webPartHandle = Activator.CreateInstance(
assemblyName, typeName);

System.Web.UI.WebControls.
WebParts.WebPart webPart =
(System.Web.UI.WebControls.WebParts.
WebPart)webPartHandle.Unwrap();
return webPart;
}
[/code]

Once we have the webpart, all manipulations with a web part on a page are governed by theSPLimitedWebPartManager class, which is a scaled down version of SPWebPartManager class designed to do the same thing but without HTTP context available.

2. Add WebPart

Once we have the webpart, code below then adds it to the specified page into the target web part zone
[code]
        public string AddWebPart(
SPWeb web,
String pageUrl,
String webPartName,
String zoneID,
int zoneIndex)
{
using (System.Web.UI.WebControls.WebParts.WebPart webPart =
FetchWebPart(web, webPartName))
{
using (SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(
pageUrl, PersonalizationScope.Shared))
{
manager.AddWebPart(webPart, zoneID, zoneIndex);
return webPart.ID;
}
}
}
[/code]

3. Create WebPart Connections

Finally all what's left is connecting the web parts. Again SPLimitedWebPartManager comes at help here. The method AddWebPartConnection() first locates consumer and provider web parts by their unique IDs, which were captured earlier at a time of placing the web parts on a page; next it iterates through provider and consumer connection points to find the ones specified in the XML configuration file. By the way, these are the same as the constructor parameters used with ConnectionProviderAttribute and ConnectionConsumerAttribute types, which are used to decorate methods participating in a web part connection. When both connection points are determined we can call the SPConnectWebParts() method of SPLimitedWebPartManager to create the connection. 

[code]
public void AddWebPartConnection(
SPWeb web,
string pageUrl,
string providerWebPartID,
string consumerWebPartID,
string providerConnectionPointName,
string consumerConnectionPointName)
{
using (SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(
pageUrl, PersonalizationScope.Shared))
{
System.Web.UI.WebControls.WebParts.WebPart provider =
manager.WebParts[providerWebPartID];
System.Web.UI.WebControls.WebParts.WebPart consumer =
manager.WebParts[consumerWebPartID];

ProviderConnectionPointCollection providerPoints =
manager.GetProviderConnectionPoints(provider);
ConsumerConnectionPointCollection consumerPoints =
manager.GetConsumerConnectionPoints(consumer);

ProviderConnectionPoint providerPoint =
null;

foreach (ProviderConnectionPoint point in providerPoints)
{
if (String.Equals(
providerConnectionPointName,
point.DisplayName,
StringComparison.OrdinalIgnoreCase))
{
providerPoint = point;
break;
}
}

ConsumerConnectionPoint consumerPoint =
null;

foreach (ConsumerConnectionPoint point in consumerPoints)
{
if (String.Equals(
consumerConnectionPointName,
point.DisplayName,
StringComparison.OrdinalIgnoreCase))
{
consumerPoint = point;
break;
}
}

manager.SPConnectWebParts(
provider,
providerPoint,
consumer,
consumerPoint);
}
}
[/code]

Hope this helps !

Cheers

3 comments:

Anonymous said...

Nice post, thanks

Anonymous said...

very nice post.
I used the code, and it works perfectly for the AddWebPart method.
I use the AddWebPart method to add two webparts. the two webparts implements IParametersOutProvider and IParametersOutConsumer respectively.


When i try the AddWebPartConnection i have an exception at :
"manager.SPConnectWebParts( provider, providerPoint, consumer, consumerPoint); "




the exception is :
***********
Provider connection point "MyParametersOutProviderInterface" on "g_d274fc72_9517_4fd3_8e68_2ba8c5a903a5" and consumer connection point "MyParametersOutConsumerInterface" on "g_7bda9ac0_bba4_4d88_9df2_c96c7dacbba0" are Microsoft Windows SharePoint Services 2.0 connection interfaces. Use Microsoft Windows SharePoint Services 2.0 Web Part Connection property instead.
***********

Have you any idea how to fix this error?
Thank you in advance.

Anonymous said...

I resolved the problem.
here is the solution:
http://www.sharepointblogs.com/jason/archive/2007/06/21/how-to-connect-web-parts-programmatically-in-wss-3.aspx