in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

jason's blog

How to connect Web Parts programmatically in WSS 3

Web parts are usually connected on the UI as part of customization. There are times better to connect Web parts using code on the fly. For example, we have a site template with a page containing two Web parts working together. Making the connection during site provisioning would be more user friendly. Actually it becomes a must for the template I am working on currently because huge number of sites will be instantiated based on it. Thanks for edhild's post giving me great hints and a good start. The following is what I have come up with for this topic.

 

Besides WSS 2 Web part, there are two WebPart base classes, ASP.NET WebPart and WSS 3 WebPart, and two sets of connection models, IWebPartXXX in ASP.NET 2/3 and IXXXProvider / IXXXConsumer in WSS 3. Choosing different Web part bases and interfaces will affect the way how to connect them programmatically and how to include them in the site definition. The general rule is using Web part manager to connect Web parts in ASP.NET connection model, using Web parts' Connections property to connect those in WSS connection model. Furthermore, ASP.NET based Web parts cannot be put into a SPWebPartZone in the site template (maybe just I don't know how).

 

Connecting Web parts in ASP.NET connection medel

As edhild's post shows, we need SPLimitedWebPartManager to connect the Web parts with ASP.NET style interfaces. The steps for this are:

  1. Get the SPLimitedWebPartManager instance on the page;
  2. Get the provider and consumer Web parts need to be connected;
  3. Get the connection points for the provider and consumer Web parts;
  4. Connect them using SPConnectWebParts method. If necessary, certain transforming needs to be in place for compatible interfaces.
  Code snippet:

            ……

SPWeb web = (SPWeb)properties.Feature.Parent;

 

            SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);

 

            System.Web.UI.WebControls.WebParts.WebPart addSearch = mgr.WebParts["OTAddressSearch"];

            System.Web.UI.WebControls.WebParts.WebPart addDisplay = mgr.WebParts["OTAddressDisplay"];

 

            ConsumerConnectionPoint addDisplayConnPoint = mgr.GetConsumerConnectionPoints(addDisplay)["AddressConsumer_ot"];

            ProviderConnectionPoint addSearchConnPoint = mgr.GetProviderConnectionPoints(addSearch)["AddressProvider_ot"];

 

            mgr.SPConnectWebParts(addSearch, addSearchConnPoint, addDisplay, addDisplayConnPoint);

 

Some thoughts:

  1. It is preferable to assign the Web part ID property in the template, so that it can be referenced by ID instead of index. Well, I don't how to assign a list view Web part an ID, but for a custom Web part, the following is a sample:

<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1">

      <![CDATA[<?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">

  <ID>OTAddressSearch</ID>

  <Title>Address Search</Title>

  ……

  1. The same for the connection point, it is better to reference it by ID. The ID is defined in Web part source code, such as:

        [ConnectionProvider("Address search criteria", "AddressProvider_ot")]

        public IWebPartField GetConnectionInterface()

        {

            return this;

        }.

  1. I haven't figured out how to put a Web part derived from ASP.NET WebPart class into the site template through onet.xml. It is possible to put this type of Web parts onto default.aspx (or other Web part pages), but the user won't be able to edit them by SharePoint page editing. My approach is to base my Web parts on SharePoint WebPart class, and implement ASP.NET connection interfaces.
  

Connect Web parts in WSS connection model

If the Web parts use WSS connection model, we should be using a different way to link them. The key idea is to set the Connections property of the consumer Web part, and generally it includes following steps:

  1. Get the SPLimitedWebPartManager instance of the page;
  2. Get the provider and consumer Web parts need to be connected, and cast them to WSS WebPart;
  3. Assign the Web parts ConnectionID if they don't have one yet. For a Web part never been connected, this property is an empty GUID.
  4. Set the Connections property of the consumer Web part;
  5. Save the changes.
 Code snippet:

            ......
            SPWeb web = (SPWeb)properties.Feature.Parent;

 

            SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);

 

            Microsoft.SharePoint.WebPartPages.WebPart imgSearch = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgSearch"];

            Microsoft.SharePoint.WebPartPages.WebPart imgDisplay = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgDisplay"];

 

            if (imgSearch.ConnectionID == Guid.Empty)

                imgSearch.ConnectionID = Guid.NewGuid();

            if (imgDisplay.ConnectionID == Guid.Empty)

                imgDisplay.ConnectionID = Guid.NewGuid();

 

            imgDisplay.Connections = imgDisplay.ConnectionID + "," +
                 imgSearch.ConnectionID + "," +
                "MyCellConsumerInterface_WPQ_" + "," +
                "MyCellProviderInterface_WPQ_" + "," +
                "MyCellConsumerInterface_WPQ_" + "," +
                "MyCellProviderInterface_WPQ_";

            mgr.SaveChanges(imgSearch);

            mgr.SaveChanges(imgDisplay);

 

Some thoughts:

  1. Refer the msdn for details about the Connections property. The above example only shows the simplest case, connecting Web parts that implement ICellProvider and ICellConsumer interfaces.
  2. We need the connection point ID for the two Web parts. They are defined in the RegisterInterface() method, e.g.:

RegisterInterface("MyCellConsumerInterface_WPQ_",   //InterfaceName
            InterfaceTypes.ICellConsumer,                    //InterfaceType
            WebPart.UnlimitedConnections,                  //MaxConnections
            ConnectionRunAt.ServerAndClient,            //RunAtOptions
            this,                                                               //InterfaceObject
            "CellConsumerInterface_WPQ_",               //InterfaceClientReference
            "Get String Value",                                      //MenuLabel
            "Just a simple ICellConsumer");   

 

Next step is to put the code into proper site provisioning event. If the Visual Studio site definition project template happens to be enough for your needs, then simply put them into the place MS reserved for developers – the OnActivated method. For those who make their own SharePoint solutions, just like me, we need to define a feature, include this feature in the site definition, and put the code in the feature receiver's FeatureActivated event. One thing I noticed is that, the default site home page (default.aspx) should be defined as an element file as this feature, not defined in the site definition (in site template folder together with the onet.xml file). Otherwise, I got the file not found error when opening the page to get the Web part manager instance. I am guessing this is because the page defined in site definition is instantiated after the feature activation. Anyway, making it as an element file does not bother me since I will have to define many other Web part pages along with this feature.

Comments

 

Sharepoint link love 06-22-2007 at Virtual Generations said:

Pingback from  Sharepoint link love 06-22-2007 at  Virtual Generations

June 22, 2007 2:27 AM
 

Mike Walsh's WSS and more said:

June 24, 2007 3:13 AM
 

Tinu said:

Hi,

I have the provider and consumer web part. Trying to establish the connection part in Sharepoint site.

One thing that I couldn't get is where to add this connection part.

I'm fairly new to sharepoint development.

I do not know if I could modify the master page code behind file. Can you please give your suggestion. This is for a very basic one. Appreciate your help.

Thanks,

Tinu

September 11, 2007 11:32 PM
 

Nick said:

Jason,

I have an issue when trying to connect two list view webparts to eachother.

I get an error when executing the SPConnectWebParts method:

"Provider connection point .... on .... and consumer connection point .... on .... are Microsoft Windows SharePoint Services 2.0 connection interfaces. User Microsoft Windows SharePoint Services 2.0 Web Part Connection property instead"

However, I have no idea how to use that ... Maybe you know?

Thanks

Nick

September 21, 2007 3:55 AM
 

kuper said:

Hi,

I reproduce your ASP.NET connection medel, but when I call SPConnectWebParts I receive exception: "The provider connection point "Row of Data" on "g_1f323d8d_af86_47a0_a398_be63a5ac9028" and the transformer do not use the same connection interface." What you recommend at this situation?

November 6, 2007 3:57 AM
 

gada said:

jgbkjshbgkbsjgd

November 19, 2007 4:40 AM
 

Connecting webparts at Object Model « where coding happens ! said:

Pingback from  Connecting webparts at Object Model &laquo; where coding happens !

February 8, 2008 3:26 AM
 

Adam Cox said:

I'm trying to do something similar to yourself but without any joy, I kinda hoping you can help because I'm pulling my hair out with this.

I am in the process of creating a site definition using VSeWSS. I need to define a list and have a QueryStringFilterWebPart set up on the AllItems.aspx page for when the site is provisioned.

I have tried to add the web part to the markup for the AllItems.aspx page in a number of different methods but an exception is always thrown so I've now gave up on this method.

I am now attempting to create the QueryStringFilterWebPart and connect it to the ListViewWebPart via the object model (this will eventually be perfomed in the site provisioning handler).

I can succesfully create the QueryStringFilterWebPart and the SPWebPartConnection. However when I navigate to the AllItems.aspx page I get:

"Web Part Error:  This page has exceeded its data fetch limit for connected Web Parts. Try disconnecting one or more Web Parts to correct the problem."

When I go into 'Site Actions' > 'Edit Page' I can see that both web parts exist and that they are connected but when I go to 'connections' on the 'edit' menu for either of the web parts and click on the connection the 'consumer Field Name' is defaulted to 'Title'. I need this to be set to 'RequestedFor' which is a user field within the list. When I set the field correctly the error goes away and the page works perfectly.

However I can't find where to set the 'Consumer Field Name' in the object model. Here is some test code which I'm using to create the web part

-------------------------------------------------------------------------

       public static void WebPartTest()

       {

           SPSite site = new SPSite("http://agb-aldis-asc/");

           SPWeb aldisWebSite = site.OpenWeb().Webs["ALDIS"];

           SPWeb trainingWebSite = aldisWebSite.Webs["Training"];

           SPList trainingNeeds = trainingWebSite.Lists["Training Needs"];

           SPView allitemsform = trainingNeeds.Views["All Items"];

           SPLimitedWebPartManager mgr = trainingWebSite.GetLimitedWebPartManager(allitemsform.Url, PersonalizationScope.Shared);

           ListViewWebPart listViewWebPart = mgr.WebParts[0] as ListViewWebPart;

           QueryStringFilterWebPart filterwebpart = new QueryStringFilterWebPart();

           filterwebpart.CatalogIconImageUrl = "/_layouts/images/wp_Filter.gif";

           filterwebpart.ZoneID = "Main";

           filterwebpart.QueryStringParameterName="RequestedFor";

           filterwebpart.AllowClose=false;

           filterwebpart.ConnectionID= new Guid("00000000-0000-0000-0000-000000000000");

           filterwebpart.HelpMode = WebPartHelpMode.Modeless;

           filterwebpart.TitleUrl = "";

           filterwebpart.TitleIconImageUrl = "/_layouts/images/wp_Filter.gif";

           filterwebpart.Description = "Filter the contents of web parts using values passed via the query string.";

           filterwebpart.ChromeType = PartChromeType.None;

           filterwebpart.Hidden = false;

           filterwebpart.AllowHide = true;

           filterwebpart.AllowZoneChange = true;

           filterwebpart.AllowEdit = true;

           filterwebpart.AllowMinimize = true;

           filterwebpart.HelpUrl = "";

           filterwebpart.ExportMode = WebPartExportMode.All;

           filterwebpart.Direction = ContentDirection.NotSet;

           filterwebpart.SuppressWebPartChrome = false;

           filterwebpart.ID = "g_14cfe10a_7684_4970_a5c8_12e13b5a96b7";

           filterwebpart.ImportErrorMessage = "Cannot import this Web Part.";

           filterwebpart.AuthorizationFilter = "";

           filterwebpart.FilterName = "QueryString RequestedFor Filter";

           filterwebpart.AllowConnect = true;

           filterwebpart.ChromeState = PartChromeState.Normal;

           filterwebpart.Title = "QueryString RequestedFor Filter";

           filterwebpart.Height = "";

           filterwebpart.Width = "";

           mgr.AddWebPart(filterwebpart, "main", 1);

           ConsumerConnectionPointCollection ccc = mgr.GetConsumerConnectionPoints(listViewWebPart);

           ProviderConnectionPointCollection ppp = mgr.GetProviderConnectionPoints(filterwebpart);

           TransformableFilterValuesToParametersTransformer filterToParameterTransformer = new TransformableFilterValuesToParametersTransformer();

           IWebPartParameters iWebPartParameter = ((IWebPartParameters)filterToParameterTransformer);

           PropertyDescriptorCollection descriptorCollection = iWebPartParameter.Schema;

           SPWebPartConnection conn = mgr.SPConnectWebParts(filterwebpart, ppp["ITransformableFilterValues"], listViewWebPart, ccc["ListViewFilterConsumer_WPQ_"], filterToParameterTransformer);

           mgr.SPWebPartConnections.Add(conn);

       }

-------------------------------------------------------------------------

From debugging this code I can see that there is a private property '_consumerFieldNames' on the TransformableFilterValuesToParametersTransformer object which is not set after I first add the filter to the page, but it then becomes set when I 'fix' the error through the UI, however as this is a private property I obviously can't set it myself! Does anyone know how this property gets set?

If you can help with the way I'm trying to do this or you can provide advice on an alternative solution I would be extremely grateful...

Thanks

Adam

March 11, 2008 11:45 AM
 

Becky Bertram said:

Great post, and very helpful. Thanks!

June 17, 2008 4:50 PM

Leave a Comment

(required )  
(optional )
(required )  
Add

Need SharePoint Training? Attend a SharePoint Bootcamp!

Posts (c) their respective authors. Everything else (c) 2007 SharePoint Experts