A How-To Guide to Make your Custom WebPart a Consumer.
I will be using Performance Point Filter as a basis for this Guide. As i am working on 2013, i am unsure if this solution as whole will work with 2010 and pretty sure this will not work with 2007. But will need verification with both types of SharePoint Services.
Full Disclosure:
Step 1 - Fresh Pot/Thermos of Coffee
First and foremost is to understand what is needed for this to execute properly.
- Find the Interface that is used to Expose the Selected Value to Consumers.
- Make sure you have the necessary DLL's to use that Interface.
- Put Protective Head-gear on, so your already receding hair-line, will not recede any further.
Step 2 - Lets plan this out
Identify what control will be the Provider WebPart (ProviderPart), in this case Performance Point Filter.
Identify what information, from said ProviderPart, will be pertinent to Consume.
i only had one value of information, which happened to also be what was being displayed.
How will this acquired information be used in my WebPart
i was using it to send to a SQL Stored Procedure that was doing some further behind the
scenes processing for display.
How will the result be displayed?
I was taking the Selected Value, sending it to a SQL SProc, the return
result was simply a string of html formatted text to be pushed into a
Paragraph Tag <p>...</p>
Step 3 - Time to Press the Ivory's (as my piano teacher told me)
as each environment is its own special child i will show my Using statements but i would suggest not using them unless you need to. There are other processes i am using but not showing that need some of them.
I have redacted the sections that do not pertain to this Answer/Guide, with ...
characters.
Using Statements:
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
WebPart Code-Behind Design (SQLTextWebPart.ascx.cs):
[ToolboxItemAttribute( false )]
public partial class SQLTextWebPart : WebPart {
public SQLTextWebPart () { }
protected void Page_Load ( object sender , EventArgs e ) {
...
}
protected override void OnPreRender ( EventArgs e ) {
...
}
protected override void OnInit ( EventArgs e ) {
base.OnInit( e );
InitializeControl();
}
protected override void CreateChildControls () {
...
base.CreateChildControls();
}
}
WebPart Visual Design (SQLTextWebPart.ascx):
<asp:Panel ID="pnlView" Visible="true" runat="server">
....
<p id="txtContent" class="..." runat="server"></p>
</asp:Panel>
<asp:Panel ID="pnlEdit" Visible="false" runat="server">
...
</asp:Panel>
Now that the basic design has been setup, lets get to the nitty gritty.
So you know what ProviderPart you will be using as your Source of information and you know how your going to be utilizing that information. Here is how I implemented the over-all logic. After this section, i will describe what each component does, or at least should do.
namespace ... {
[ToolboxItemAttribute( false )]
public partial class SQLTextWebPart : WebPart {
public SQLTextWebPart () { }
#region Fields
....
IWebPartParameters _provider = null;
IDictionary _data = null;
private string _filtervalue;
#endregion
#region Properties
...
public string FilterValue { get { return _filtervalue; } set { _filtervalue = value; } }
public IWebPartParameters ProviderPart { get { return this._provider; } set { this._provider = value; } }
#endregion
#region Methods
[ConnectionConsumer( "Filter Value" )]
public void FilterValuesConsumer ( IWebPartParameters providerPart ) {
PropertyDescriptor[] props = { TypeDescriptor.GetProperties( this )[ "FilterValue" ] };
PropertyDescriptorCollection schema = new PropertyDescriptorCollection( props );
providerPart.SetConsumerSchema( schema );
ProviderPart = providerPart;
}
public void SetProviderData ( IDictionary dict ) {
this._data = dict;
if ( ProviderPart != null ) {
if ( this._data != null ) {
UpdateContent( this._data[ "FilterValue" ] );
}
}
}
...
public void UpdateContent ( object clientid = null ) {
...
try {
conn.Open();
string userid = null;
if ( this.Context.User.Identity.IsAuthenticated ) {
userid = ...;
}
var cmd = new SqlCommand( ... , ... ) {
CommandType = System.Data.CommandType.StoredProcedure
};
if ( clientid != null )
cmd.Parameters.AddWithValue( "@clientid" , clientid );
if ( userid != null )
cmd.Parameters.AddWithValue( "@userid" , userid );
using ( var rdr = cmd.ExecuteReader() ) {
while ( rdr.Read() ) {
txtContent.InnerHtml += rdr[ "..." ] + "<br/>";
}
}
} catch ( Exception ex ) {
...
} finally {
conn.Close();
}
}
#endregion
#region Event Handlers
protected void Page_Load ( object sender , EventArgs e ) {
if ( WebPartManager.DisplayMode == WebPartManager.BrowseDisplayMode ) {
pnlEdit.Visible = false;
pnlView.Visible = true;
//Make sure ALL properties have a value before trying to execute.
if ( ... ) {
txtContent.InnerText = "WebPart Not Configured!";
...
} else {
UpdateContent();
}
} else if ( WebPartManager.DisplayMode == WebPartManager.DesignDisplayMode ) {
pnlView.Visible = false;
pnlEdit.Visible = true;
...
}
}
#endregion
#region Overrides
#region Event Handlers
...
protected override void OnPreRender ( EventArgs e ) {
if ( ProviderPart != null ) {
ProviderPart.GetParametersData( new ParametersCallback( SetProviderData ) );
}
base.OnPreRender( e );
}
...
#endregion
#endregion
}
}
For this example, you do not need to Implement IWebPartParameters
directly off the Class. You can, but it is unnecessary as the inherited objects will never be used.
Main Focus should be with this:
[ConnectionConsumer( "Filter Value" )]
public void FilterValuesConsumer ( IWebPartParameters providerPart ) {
PropertyDescriptor[] props = { TypeDescriptor.GetProperties( this )[ "FilterValue" ] };
PropertyDescriptorCollection schema = new PropertyDescriptorCollection( props );
providerPart.SetConsumerSchema( schema );
ProviderPart = providerPart;
}
public void SetProviderData ( IDictionary dict ) {
this._data = dict;
if ( ProviderPart != null ) {
if ( this._data != null ) {
UpdateContent( this._data[ "FilterValue" ] );
}
}
}
It does not matter what you name the method, FilterValuesConsumer
, just that the method that will consume the Provider Data have the [ConnectionConsumer( "Filter Value" )]
Attribute attached to it. The value "Filter Value", must be filled out and is suggest that it describe the associated property that will be attached to but again unnecessary for it to be ultimately meaningful, for the most part.
This line PropertyDescriptor[] props = { TypeDescriptor.GetProperties( this )[ "FilterValue" ] };
is the key hook. "FilterValue"
Must be the name of the Property that will be receiving the information. Even though you wont actually be using the property directly, it must be the same name as the Property that will be receiving the value. As well, the reference this._data[ "FilterValue" ]
must have the same reference as the Property Receiver. So in both cases the value FilterValue
must be referred to in both the GetProperties
use and the underlying IDictionary
variable.
The SetProviderData ( IDictionary dict )
method, is the callback method the ProviderPart will execute, on the pages first load or when the selection is changed. The reason for storing the dict
value into a Control field/property, is to make sure that if the value doesnt change that it will persist during the event life-cycle. Regardless, it is always a good idea, in case the Connection between the two WebParts is terminated for some reason, to evaluate their values. This is evident in SetProviderData
, where i am check ProviderPart
and this._data
for null
value. If you do not check and assume you will get some squirrely Exceptions reported back from SharePoint.
The line UpdateContent( this._data[ "FilterValue" ] );
refers to a method i created for the WebParts specific purpose, but allowed me to execute the same methodology whether the WebPart was Connected or not. Adjust for your specific purpose.
Finally
This is only a basic design for interaction between a ProviderPart & ConsumerPart, but should get you to a successful connection state. Once you are able to make the connection and see results, then start your customization for you specific needs.