5,699,997 members and growing! (19,823 online)
Email Password   helpLost your password?
Web Development » ASP.NET » Web Parts     Intermediate License: The Code Project Open License (CPOL)

Multi-Connected Consumer Web Parts

By Mark Trudgeon

Allow consumer web parts to have multiple providers in ASP.NET, WSS 3.0 and MOSS 2007
C# (C# 1.0, C# 2.0, C# 3.0, C#), Windows (Windows, Win2K, WinXP, Win2003, Vista), .NET (.NET, .NET 2.0), Visual Studio (VS2008, Visual Studio), ASP.NET, Dev

Posted: 23 Jul 2008
Updated: 23 Jul 2008
Views: 3,857
Bookmarked: 11 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
votes for this Article.
Popularity: 0.00 Rating: 0.00 out of 5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

Have you ever developed a provider / consumer web part pair that would allow a single consumer web part to be connected to multiple provider web parts only to find that ASP.NET / WSS 3.0 / MOSS 2007 only allows a single one-to-one connection? If so, then you have come to the correct location.

Background

A few weeks back I was developing some new web parts for a SharePoint project I am busy with and found myself running into a brick wall. The requirement was that when a link was clicked on a list item, then some help/information text would be displayed to the right of the list. The requirement also stated that more than one list could be on a single portal page and that the same help/info text web part would be used. I thought this would be easy as I knew the ConnectedConsumerAttribute class had a property called AllowMultipleConnection and all I would have to do is set this value to true, after all WSS 2.0/MOSS 2003 supported this functionality already. How wrong I was, so after wading through all the forums / blogs / wikis etc. came to the conclusion that many people are also experiencing the same pain as myself. Unfortunately I have had not been able to find a solution to the problem which has led me to this point, either the ASP.NET web part implementation is flawed in design or there is one big defect which needs to be repaired. So I think I have come up with one potential solution to the problem (although I thought up several others along the way, this is the slickest).

Using the code

Let me start straight off, this is not a step-by-step howto develop a bit of code using VS 2008, I will assume you already know this. I will describe the concepts of the solution using code. My development environment is the following:

  • Vista Enterpise
  • VS TS 2008
  • WSS 3.0 (installed on Vista using Bamboo Solutions install)
  • VS Extensions for SharePoint Svcs 1.2
  • + plenty other stuff (for this article the above is required)

NB: Please remember to start VS with elevated credentials if you are using Vista, and don't forget to change the WSS url in the project debug properties.

The first thing I did was create a new custom attribute class called ConnectedConsumerInterfaceAttribute which I use to mark the relevent method on the consumer web part. This method will be invoked by the provider web part.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ConnectConsumerInterfaceAttribute : Attribute
{
   private Type _interfaceType;
   public ConnectConsumerInterfaceAttribute(Type interfaceType)
   {
      _interfaceType = interfaceType;
   }
   public Type InterfaceType
   {
      get
      {
         return _interfaceType;
      }
   }
}

The next thing I did was to subclass the ASP.NET WebPart class and create a ProviderWebPart class. All providers that require to connect to muli-connect consumers will need to inherit from this new class. I added a new protected virtual method called ConnectedConsumerInvoke which has the following signature:

protected virtual void InvokeConnectedConsumer<TInterfaceType>(TInterfaceType graph)

This method has all the magic required to invoke the consumer method based on the interface type and whether it is tagged with the ConnectedConsumerInterfaceAttribute attribute. The method retrieves all the current web part connections using the WebPartManager object. It then loops through each connection until it can find the connections which belong to the current provider web part (which is this). Once the connection provider has been identified as the same object, the connection consumer is added to a list. Once the consumer web part list is fully populated, the code loops through each consumer web part and retrieves all the methods of the web part using reflection. It them checks to see if the methods are tagged with the ConnectedConsumerInterfaceAttribute attribute. If it is, then the TInterfaceType type is compared to the ConnectedConsumerInterfaceAttribute.InterfaceType, and if the same, the retrieved method is invoked and the graph object is passed in as a parameter. In the code sample attached, I have created a helper class called SPSHelper which does some of the work described above.

public class ProviderWebPart : WebPart
{
   public ProviderWebPart() : base()
   {
   }
   protected virtual void InvokeConnectedConsumer<TInterfaceType>(TInterfaceType graph)
   {
      // Retrieve all the consumer web parts using the SPSHelper class...
      WebPart[] consumerWebParts = SPSHelper.GetConnectedConsumerWebParts(this.WebPartManager, this, typeof(TInterfaceType));
      // Process all the consumer web parts...
      foreach (WebPart consumerWebPart in consumerWebParts)
      {
         // Retrieve all the methods of the consumer web part...
         MethodInfo[] methods = consumerWebParts.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
         // Loop through each method and check if any are marked with ConnectConsumerInterfaceAttribute...
         foreach (MethodInfo method in methods)
         {
            // Retrieve all the custom attributes of the method...
            object[] attributes = method.GetCustomAttributes(typeof(ConnectConsumerInterfaceAttribute), false);
            // Loop through until we find a valid ConnectConsumerInterfaceAttribute - there should only be 1...
            foreach (ConnectConsumerInterfaceAttribute attribute in attributes)
            {
              // Check if the interface types are the same...
              if (typeof(TInterfaceType) == attribute.InterfaceType)
              {
                 // Now invoke the method pass in the data...
                 method.Invoke(consumerWebPart, new object[] { graph });
                 // Now need to loop through the rest...
                 break;
              }
            }
         }
      }
   }
}

I then developed 2 web parts called InputWebPart (provider) and DisplayWebPart (consumer).

public class InputWebPart : ProviderWebPart
{
   private TextBox _txtInputText;
   private ITextData _textData;
   public InputWebPart()
   {
   }
   protected ITextData TextData
   {
      get
      {
         if (_textData == null)
         {
            _textData = new TextDataProvider();
         }
         return _textData;
      }
      set
      {
         _textData = value;
      }
   }
   protected override void CreateChildControls()
   {
      base.CreateChildControls();
      // Add a new text box...
      _txtInputText = new TextBox();
      this.Controls.Add(_txtInputText);
      // Add a new button...
      Button btnSend = new Button();
      btnSend.Text = "Send";
      // Wire up the click event...
      btnSend.Click += new EventHandler(btnSend_Click);
      this.Controls.Add(btnSend);
   }
   private void btnSend_Click(object sender, EventArgs e)
   {
      // this is required to take care of the normal SharePoint behaviour...
      this.TextData.Text = _txtInputText.Text;
      // this is where all the magic happens...
      this.InvokeConnectedConsumer<ITextData>(this.TextData);
   }
   // This is required for SharePoint to allow provider to connect to consumers...
   [ConnectionProvider("Text Data")]
   public ITextData SetTextData()
   {
      return _textData;
   }
}

public class DisplayWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
   private ITextData _textData;
   public DisplayWebPart()
   {
   }
   protected ITextData TextData
   {
      get
      {
         return _textData;
      }
      set
      {
         _textData = value;
      }
   }
   protected override void CreateChildControls()
   {
      base.CreateChildControls();
      if (this.TextData != null)
      {
         Label lblDisplayText = new Label();
         lblDisplayText.Text = this.TextData.Text;
         this.Controls.Add(lblDisplayText);
      }
   }
   // Required for SharePoint to allow multiple connections...
   [ConnectionConsumer("Text Data Consumer", AllowsMultipleConnections = true)]
   // Required for the provider to invoke multiple connected consumers...
   [ConnectConsumerInterface(typeof(ITextData))]
   public void GetTextData(ITextData textData)
   {
      this.TextData = textData;
      this.EnsureChildControls();
   }
}

That is the bulk of the code in the sample. Once you load the sample into VS 2008, it will make more sence. To deploy the solution into WSS 3.0, right click the solution file in the VS solution explorer and click the Deploy option in the context menu.

I hope this will make your lives a lot easier when developing web parts in the future.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mark Trudgeon


I have been in the I.T. industry for 17 years now, starting off on IBM S36/S38 and moving onto AS/400. I then got involved with networking (Vines/Netware) and eventually Microsoft products. I am a developer by heart and enjoy coding using C#. I am South African by birth and now live in the green fields of Ireland.
Occupation: Software Developer (Senior)
Location: Ireland Ireland

Other popular ASP.NET articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 23 Jul 2008
Editor:
Copyright 2008 by Mark Trudgeon
Everything else Copyright © CodeProject, 1999-2008
Web18 | Advertise on the Code Project