Navigation

Tuesday 9 August 2011

How to programmatically add the Related List Webpart to a list form

This is one thing that I have used a few times through the browser and always thought it was a feature missing from WSS 3.0 / MOSS 2007. But it was only when I tried to do this (relatively simple) thing as part of a list definition that I realised how oblique it was, and how little information there seems to be explaining how to do it... This blog post will try to address some of that.



So first off .. what is a Related List Webpart? This wasn't really a surprise but if you look under the hood you will notice that when you use the "Related List" ribbon button then it actually does 2 things:
  • Adds a new List View Webpart (in this case an XsltListViewWebPart) which is bound to the related list
  • Creates a web part connection from the (already present) ListFormWebPart which provides the ID which the related list will filter on.
In order to set this up programmatically we basically have to replicate this. In my example I am running this from a Feature Receiver which will setup the web parts. I have 2 lists which I will be referring to:
  • Main List (the main list which I will be modifying)
  • Related List (a list which contains a lookup field containing values from List A)
STEP 1 - Add a new List View Web Part to the List Form

In this step we will be modifying the DISPLAY form (DispForm.aspx). This is probably the most common approach although you can follow very similar steps for the Edit Form if you so wish)

// these are the 2 lists we will work with
SPList mainList = web.Lists["Main List"];
SPList relatedList = web.Lists["Related List"];

// this is the Display Form web part page
SPFile dispForm = web.GetFile(mainList.DefaultDisplayFormUrl);

// get the web part manage which we will use to interact
SPLimitedWebPartManager wpm =
dispForm.GetLimitedWebPartManager System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

// only execute this code if there is a single web part (the default for this list)
// we don't want to add the web parts over and over again ..
if (wpm.WebParts.Count == 1)
{
   // create our List View web part
   XsltListViewWebPart wp = new XsltListViewWebPart();

   // Hook up the list and the view
   wp.ListId = relatedList.ID;
   wp.ListName = relatedList.ID.ToString();
   wp.ViewGuid = relatedList.DefaultView.ID.ToString();
   wp.XmlDefinition = relatedList.DefaultView.GetViewXml();

   // set basic properties along with the title
   wp.AllowConnect = true;
   wp.Title = "Related items from " + relatedList.Title;
   wp.ChromeType = PartChromeType.TitleAndBorder;

   // add the web part to the Main zone in position 2
   wpm.AddWebPart(wp, "Main", 2);

   // save the page
   webPartPage.Update();
}

So you can see from the example code above we are adding a simple List View webpart for our related list items. We are binding it to the "Related List" using the ID, and also choosing the View we want to display (here we have just used the Default View for simplicity).

We need to make sure we set "AllowConnect" (as we will need to use Connections in Step 2) and also set the ChromeType to "Title and Border" (as the default for Web Parts in List Forms is "None").

Finally we add the web part and save the changes to the Web Part Page ...

now for the slightly less well documented bit ..

STEP 2  - Create Web Part Connections between ListForm and ListView webparts
In this step we are going to take the Web Part which we added in STEP 1 and use standard Web Part Connections to bind them together ... don't be scared, it isn't as hard as yoy might think :)

We are going to be doing several things here:
  1. We will get instances of both web parts which need to be connected
  2. We will create "Connection Point" objects for each web part
  3. Specify which fields we want to filter on
  4. We will create a "Transformer" object which is able to pass values between each web part
  5. We will use the Web Part Manager to create the connection, using all of that information above.
// get instances of our two web parts
System.Web.UI.WebControls.WebParts.WebPart consumer = wpm.WebParts[1];
System.Web.UI.WebControls.WebParts.
WebPart provider = wpm.WebParts[0];

// Create our two Connection Point objects
// These are specific to our two types of Web Part

ProviderConnectionPoint providerPoint = wpm.GetProviderConnectionPoints(provider)["ListFormRowProvider_WPQ_"];


ConsumerConnectionPoint consumerPoint = wpm.GetConsumerConnectionPoints(consumer)["DFWP Filter Consumer ID"];

// create our "Transformer"
// we also specify which Field names we want to "connect" together
// here I am connecting my Related List's "MyLookupField" and filtering it
// using the "ID" of my List Form's item.

SPRowToParametersTransformer optimus = new SPRowToParametersTransformer();
optimus.ProviderFieldNames = new string[] { "ID" };
optimus.ConsumerFieldNames = new string[] { "MyLookupField" };

// Connect the two web parts together, using our Connection Point objects
// along with our Transformer

wpm.SPConnectWebParts(provider, providerPoint, consumer, consumerPoint, optimus);

And that is it! You don't need to do any more "Update" or "Save" methods .. your Related List web part should now be finished :)

So just to be clear, we are using the standard SPLimitedWebPartManager object and calling the SPConnectWebParts method. The only slightly odd call is the reference to the "SPRowToParametersTransformer" which is specific to the type of Connection we are making (the List Form representing a "Row").

Where did this information come from?
This is the easy bit :) I added a Related Lists Webpart using the browser, then opened up DispForm.aspx using SharePoint Designer .. and found this: 

<WebPartPages:SPWebPartConnection ConsumerConnectionPointID="DFWP Filter Consumer ID" ConsumerID="g_dc64a1e0_c2f4_4302_86df_e4d184203bbd" ID="c225174896" ProviderConnectionPointID="ListFormRowProvider_WPQ_" ProviderID="g_ffb9e36b_bc6d_489e_b7fc_e93048c32f5c"><WebPartPages:SPRowToParametersTransformer ConsumerFieldNames="IMSDataSource" ProviderFieldNames="ID"></WebPartPages:SPRowToParametersTransformer>
I have highlighted the references to the 3 things which made all of the dots join up for me: )
so if you are ever stuck, make sure you look in SharePoint Designer for :

  • Consumer Connection Point ID ("DFWP Filter Consumer ID")
  • Provider Connection Point ID ("ListFormRowProvider_WPQ_")
  • Transformer Type (SPRowToParametersTransformer)
So thats it :) Hope this was useful, comments always welcome

6 comments:

  1. Great post on a not so well documented function! Saved me banging my head against a wall for any longer - thanks :)

    ReplyDelete
  2. By the way - a couple of spelling mistakes if anyone else is struggling to get this working.

    The line

    ProviderConnectionPoint providerPoint = manager.GetProviderConnectionPoints(provider)["ListFormRowProvider_WPQ_"];

    should be


    ProviderConnectionPoint providerPoint = wpm.GetProviderConnectionPoints(provider)["ListFormRowProvider_WPQ_"];

    Based on the web part manager object name in the code in step 1.

    Also right at the end of step 2 the transformer has been called optimusPrime when it was named optimus earlier in the code.

    Nothing big but thought I would point it out in case others are stuck (although they shouldn't be touching the SP Dev if they can't work that out for themselves!)

    ReplyDelete
  3. Richard,

    Thanks for the feedback and good spot. I've updated the code so hopefully no-one else should have issues ;)

    ReplyDelete
  4. Thanks for such a great post!

    ReplyDelete
  5. Hi Martin,

    I would like to thank you for posting this wonderful article, it really helped me.

    I am running in to an issue listed below and I was wondering if there is something that I am missing. I appreciate your help.

    Development Environment: Visual Studio 2010, SharePoint Server 2010
    Servers: 3 Front end servers, 2 application servers and 1 database server.

    Description:
    We have a site definition project which contains two list definitions, Minutes and Actions. We would like to create a connection between these two lists dynamically.

    We have added the code in the “ListAdded” event receiver of Minutes list.

    Issue:
    In the development environment(single server with database on it) the above code works fine. The connection is created successfully between the Minutes and Actions list.

    When the wsp is installed on production and staging environment at the root of site collection the connection fails. “providerPoint object is null” is the error in the code listed below.

    If we create subsites under the root site everything works fine. Connection fails at the root of each new site collection.

    // get instances of our two web parts
    System.Web.UI.WebControls.WebParts.WebPart consumer = wpm.WebParts[1];
    System.Web.UI.WebControls.WebParts.WebPart provider = wpm.WebParts[0];

    // Create our two Connection Point objects
    // These are specific to our two types of Web Part

    ProviderConnectionPoint providerPoint = wpm.GetProviderConnectionPoints(provider)["ListFormRowProvider_WPQ_"];

    Thanks,
    -Reetu

    ReplyDelete
  6. you re a king. Great post. Thank you

    ReplyDelete

This blog has been moved to www.martinhatch.com

Note: only a member of this blog may post a comment.