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.
- Main List (the main list which I will be modifying)
- Related List (a list which contains a lookup field containing values from List A)
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();
}
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:
- We will get instances of both web parts which need to be connected
- We will create "Connection Point" objects for each web part
- Specify which fields we want to filter on
- We will create a "Transformer" object which is able to pass values between each web part
- We will use the Web Part Manager to create the connection, using all of that information above.
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)
Great post on a not so well documented function! Saved me banging my head against a wall for any longer - thanks :)
ReplyDeleteBy the way - a couple of spelling mistakes if anyone else is struggling to get this working.
ReplyDeleteThe 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!)
Richard,
ReplyDeleteThanks for the feedback and good spot. I've updated the code so hopefully no-one else should have issues ;)
Thanks for such a great post!
ReplyDeleteHi Martin,
ReplyDeleteI 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
you re a king. Great post. Thank you
ReplyDelete