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.

Leave a Reply


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.

Leave a Reply