Exploring Sharepoint Connected Web Parts with IWebPartRow


With WSS 3.0 and MOSS 2007 you can develop web parts which can be connected together as providers and consumers. WSS 3.0 and MOSS 2007 now uses the .NET 2.0 connectable web part technology. This sample demonstrates a consumer webpart which will consume row data provided by OOTB web parts using the standard IWebPartRow interface. Custom developed web parts can implement their own connection interfaces of course, but the advantage of using the standard interfaces is that they can be integrated with the OOTB ones.

The IWebPartRow interface provides access to a row of data provided by the provider web part. Using the OOTB List View Webpart (the standard webparts which represent standard and custom lists / document libraries) as an example provider, these web parts view list data using one of the views attached to the list (and you can customise the web parts copy of that view also). So in terms of a row of data, a provider such as this will offer the currently selected row in the providers view. The contents of this row (i.e the columns) are the list columns selected for display in the web parts view. If the list is a Document or Form library their will also be a column called DocuUrl, which is the site relative url of the Document/Form library items SPFile. If the list is just a list, their will also be a column called Attachments.

Changing the columns displayed on the provider web parts view, then, obviously changes the data row columns offered to consumers.

As far as web part life cycle events go, things follow the normal web part life cycle model (see this post), however, the call to SetConnectionInterface() follows OnLoad(). This call connects the 2 web parts, but no data has been transferred as yet. It turns out that the data transfer action follows a pull model (from the consumers point of view) and it is the consumers resonsibility to pull the data from the provider as and when ready.

The consumer can pull the data from the provider whenever they require, except that it can obviously only be done after the SetConnectionInterface() method has been called. To retrieve the data, the consumer makes a call to _provider.GetRowData(callback_method). The callback_method is implemented in the consumer code, the single parameter to this callback method represents the row data and must be cast to the required type. Note, that the provider calls the consumers callback_method synchronously.

The consumer web part developed here will be connected to a Document library list view provider shown below, note the columns displayed on the view.

When connected to this provider web part, the consumer displays as follows.

The code for the consumer web part is show here.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Data;
using Microsoft.SharePoint;
namespace MbosSampleWebParts
{
 [Guid("4517a282-9681-4f7c-ac9f-b12fb200ab07")]
 public class MbosViewerWebPart : WebPart
 {
  private DataRowView _dataRow;
  private IWebPartRow _provider;
  private readonly StringBuilder _sb = new StringBuilder();
  
  public MbosViewerWebPart()
  {
   _sb.Append("- Ctor\n");
   PreRender += MbosViewerWebPart_PreRender;
  }
  protected override void OnLoad(EventArgs e)
  {
   base.OnLoad(e);
   _sb.Append(state("OnLoad"));
  }
  protected override void OnInit(EventArgs e)
  {
   base.OnInit(e);
   _sb.Append(state("OnInit"));
  }
  void MbosViewerWebPart_PreRender(object sender, EventArgs e)
  {
   _sb.Append(state("PreRender.1"));
//   if (_provider != null)
//    _provider.GetRowData(GetRowData);
   _sb.Append(state("PreRender.2"));
  }
  protected override void RenderContents(HtmlTextWriter writer)
  {
   base.RenderContents(writer);
   _sb.Append(state("RenderContents"));
   if (_provider != null)
   {
    try
    {
     PropertyDescriptorCollection schemaProps = _provider.Schema;
     if (schemaProps != null && schemaProps.Count > 0 && _dataRow != null)
     {
      DataRow providerRow = _dataRow.Row;
      // enumerate data columns
      _sb.Append("\n");
      foreach (DataColumn column in providerRow.Table.Columns)
       _sb.AppendFormat("- data column: {0} = {1}\n", column.ColumnName ?? "n/a", providerRow[column]);
      // enumerate schema
      _sb.Append("\n");
      foreach (PropertyDescriptor prop in schemaProps)
       _sb.AppendFormat("- schema entry: {0} is {1}\n", prop.Name, prop.PropertyType);
      try
      {
       // get the SPFile of the connected item (if doc/form library item)
       var colDocUrl = providerRow.Table.Columns["DocUrl"];
       if (colDocUrl != null)
       {
        var connectedFile = SPContext.Current.Web.GetFile(providerRow[colDocUrl].ToString());
        _sb.AppendFormat("\n- SPFile.Author.Name: {0}\n", connectedFile.Author.Name);
       }
      }
      catch (Exception e)
      { _sb.Append(e.Message + "\n"); }
     }
     else
      _sb.Append(" - No data\n");
    }
    catch (Exception ex)
    { _sb.Append(ex.Message +"\n"); }
   }
   else
    _sb.Append("- Not connected\n");
   writer.Write(_sb.ToString().Replace("\n", "<br>")); // replace LF with HTML line-break
  }
  protected override void Render(HtmlTextWriter writer)
  {
   _sb.Append(state("Render"));
   base.Render(writer);
  }
  protected override void CreateChildControls()
  {
   base.CreateChildControls();
   _sb.Append(state("CreateChildControls.1"));
   if (_provider != null)
    _provider.GetRowData(GetRowData);
   _sb.Append(state("CreateChildControls.2"));
  }
  private string state(string method)
  {
   var sb = new StringBuilder();
   sb.AppendFormat("- {0}", method);
   if (_provider != null)
    sb.Append(", _provider not null");
   else
    sb.Append(", _provider null");
   
   if (_dataRow != null)
    sb.Append(", _dataRow not null");
   else
    sb.Append(", _dataRow null");
   sb.Append("\n");
   return sb.ToString();
  }
  private void GetRowData(object rowData)
  {
   _sb.Append(state("GetRowData.1"));
   try
   {
    _dataRow = (DataRowView)rowData;
   }
   catch
   { _dataRow = null; }
   _sb.Append(state("GetRowData.2"));
  }
  
  [ConnectionConsumer("Row")]
  public void SetConnectionInterface(IWebPartRow provider)
  {
   _provider = provider;
   _sb.Append(state("SetConnectionInterface"));
  }
 }
}

Published by

Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

7 thoughts on “Exploring Sharepoint Connected Web Parts with IWebPartRow

  1. Hi,

    (I am an absolutely web part developer beginner !).

    This example is just what I have been looking for 🙂

    I have copied your code into a VS 2008 web part project, compiled and deplyoed – no apparent problems.

    I have connected the web part to an OOTB DataFormWebPart exposing items from a SP list.

    The first time the page is loaded I see:

    – Ctor
    – OnInit, _provider null, _dataRow null
    – OnLoad, _provider null, _dataRow null
    – SetConnectionInterface, _provider not null, _dataRow null
    – CreateChildControls.1, _provider not null, _dataRow null
    – CreateChildControls.2, _provider not null, _dataRow null
    – PreRender.1, _provider not null, _dataRow null
    – PreRender.2, _provider not null, _dataRow null
    – Render, _provider not null, _dataRow null
    – RenderContents, _provider not null, _dataRow null
    – Ingen rækker modtaget !, _provider not null, _dataRow null (added by me)
    – No data

    So far so good or ?

    But when I click an item in the DataFormWebPart one of the usual no-good error messages shows.

    Do you have any suggestions as how to fix this ?

  2. TR,

    I experienced the same problem with the unexpected error when trying to consume data from an OOB Data View Web Part. After much digging, I finally discovered that if you inherit from “Microsoft.SharePoint.WebPartPages.WebPart” instead of “System.Web.UI.WebControls.WebParts.WebPart” it works as expected.

    I can only speculate that there is some legacy reason for this.

  3. Hi
    I’m trying to add a Button to the consumer webpart. When this button is clicked I want to use data from the provider. But for some reason when I click the button in the myButton_Click method the object DataRow is allways null at this stage of the lifecycle. Do you have an Idea how this can be resolved?

    I Posted my entire Code here http://stackoverflow.com/questions/8527002/how-to-use-data-from-a-iwebpartrow-connection-when-button-is-clicked-provider-i

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s