ASP.NET WebParts Connections Transformers Tutorial
        
        
            Sept 2004
        
        
            Summary:
            This article explains how you can use the WebPart Connections Transformers feature
            to allow two incompatible WebParts to communicate between each other without having
            to modify their source code, building an intermediate class that transforms communication
            between them.
            
            
            Visits:
        
        
        Contents
        
            Download: Pending...
            
            Introduction
            Sample Scenario
            Summary
        
        
        
        
            Connections are a very powerful feature of WebParts that allows interchanging data
            between two webparts, allowing creating more interesting non-monolitic WebParts.
            
            A connection in WebParts is essentially the ability to expose an interface to a
            WebPart (Provider) that another WebPart (Consumer) can connect to and use it. This
            is an interesting feature that allows the creation of atomic units that are completely
            decoupled from each other. This allows end users to connect WebParts from different
            vendors to interact their web site even when the vendors were never aware of them.
            
            ASP.NET WebPart connections give a lot of flexibility allowing WebPart developers to expose
            any arbitrary interfaces that might include events, methods, properties or anything that 
            interfaces allow. This allows building interesting connections that can allow consumers notify
            providers using methods or events.
            
            However, with this flexibility comes a problem, each vendor might start defining their own interfaces
            which will make potentially imposible to end users inter-connect webparts seemlesly. 
            
            ASP.NET understand the problem and to mitigate it, it has two features:
            
                - It defines a pre-defined set of "standard" interfaces that different vendors could try to stick to, 
                so their webparts are more "connectable". Interfaces like IField, IParameters, IRow, etc try to 
                provide common patterns of data interchange.
- Transformers. This feature allows to build special "bridges" that allows to connect webparts
                that by definition are incompatible. This is what we will be covering in this article.
            The scenario that we will create is the simplest transformer possible.
            Lets assume we downloaded a DLL from a WebSite that "vendor A" created. 
            This webpart exposes a connection with an interface IFoo (FooProvider WebPart). 
            We also downloaded from another site a WebPart that knows how to consume an interface called IBar (BarConsumer WebPart).
            
            The problem that we have is that we would want to connect them to share data, but they are incompatible since they expose/consume
            different interfaces, so the ASP.NET WebPart Framework will not allow us to connect them. 
            So our job is to create the "bridge" that will tell the WebPart framework that they are "connectable", and that we know how to
            translate between one and the other.
            
        
        Create a new Project
        
        Create a new Web Application in Visual Web Developer or Visual Studio .NET 2005.
        
        
        
Create the WebParts
        
            Even though this sample is not meant to show how to do the WebParts and the connections, we will show the 
            code so you see how simple it is. 
 Just keep in mind that you do not need the source code of WebParts,
            or the interfaces to be able to build a transformer that converts between two different webparts.
            
            
        
        Create the Code Directory
        
            - Select the project node in the Solution Explorer.
- Right click it and select New Folder
- Type Code as the name of the folder
Note. the Code directory is new to ASP.NET 2.0 that allows developers to
        place code inside this directory with the .cs or .vb extension and the asp.net build
        infrastructure will compile the code on demand only when needed. In the Beta2 it might be
        renamed to be called Application_Code.
        
        
        
Add a new BarConsumer file to the directory.
        
            - Select the Code folder
- Right click it and select the option Add New Item
- Select the option Class and type as the name BarConsumer
- Replace the content of the generated class with the following code.
            C# Code:
namespace CarlosAg.Samples {
    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls.WebParts;
    /// <summary>
    /// Simple Interface that Shares an Integer property
    /// </summary>
    public interface IBar {
        int IntegerData { get;}
    }
    /// <summary>
    /// WebPart that consumes an IBar.
    ///     Displays the info
    /// </summary>
    public class BarConsumer : WebPart {
        private IBar _bar;
        /// <summary>
        /// Exposes the Consumer Connection Point
        /// </summary>
        [ConnectionConsumer("IBar Consumer")]
        public void SetProvider(IBar bar) {
            this._bar = bar;
        }
        /// <summary>
        /// Renders the Data
        /// </summary>
        /// <param name="writer"></param>
        protected override void RenderContents(HtmlTextWriter writer) {
            if (_bar != null) {
                writer.Write("Integer Data:" + _bar.IntegerData.ToString());
            }
            else {
                writer.Write("IBar is null");
            }
        }
    }
}
            
            VB.NET Code:
Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls.WebParts
Namespace CarlosAg.Samples
    ' <summary>
    ' Simple Interface that Shares an Integer property
    ' </summary>
    Public Interface IBar
        ReadOnly Property IntegerData() As Integer
    End Interface
    ' <summary>
    ' WebPart that consumes an IBar.
    '     Displays the info
    ' </summary>
    Public Class BarConsumer
        Inherits WebPart
        Private _bar As IBar
        ' <summary>
        ' Exposes the Consumer Connection Point
        ' </summary>
        <ConnectionConsumer("IBar Consumer")> _
        Public Sub SetProvider(ByVal bar As IBar)
            Me._bar = bar
        End Sub
        ' <summary>
        ' Renders the Data
        ' </summary>
        ' <param name="writer"></param>
        Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
            If (Not (_bar) Is Nothing) Then
                writer.Write(("Integer Data:" + _bar.IntegerData.ToString))
            Else
                writer.Write("IBar is null")
            End If
        End Sub
    End Class
End Namespace
        
        
            As you can see creating a consumer is extremely easy, just expose a public method that has no return value,
            that receives one object and that is tagged with the ConnectionConsumer attribute. 
            Once you do that, just make sure to save a reference of the object passed and use it later, say at PreRender, or Render methods,
            to ensure everything is connected correctly. 
        
        Create the Provider WebPart
        
        Create the FooProvider class
        
            - Select the Code folder node
- Right click it and select the option Add New Item
- Select the option Class
- Type FooProvider as the name and replace the content as follows
            C# Code
namespace CarlosAg.Samples {
    using System;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    /// <summary>
    /// Simple Interface that Shares a String property
    /// </summary>
    public interface IFoo {
        string StringData { get;}
    }
    /// <summary>
    /// WebPart that serves as an IFoo Provider.
    ///     Prompts the user using a TextBox
    /// </summary>
    public class FooProvider : WebPart, IFoo {
        private TextBox _dataTextBox;
        /// <summary>
        /// Public property to allow users to set the Data
        /// </summary>
        [Personalizable(PersonalizationScope.User)]
        public string Data {
            get {
                return TextBox.Text;
            }
            set {
                TextBox.Text = value;
            }
        }
        /// <summary>
        /// Text Box to allow end users to change the data
        /// </summary>
        protected TextBox TextBox {
            get {
                EnsureChildControls();
                return _dataTextBox;
            }
        }
        /// <summary>
        /// Creates a new instance of the class
        /// </summary>
        public FooProvider() {
        }
        /// <summary>
        /// Overrides the CreateChildControls to create the TextBox
        /// </summary>
        protected override void CreateChildControls() {
            Label _label = new Label();
            _label.Text = "Enter a Number:";
            _label.Font.Bold = true;
            this.Controls.Add(_label);
            _dataTextBox = new TextBox();
            _dataTextBox.AutoPostBack = true;
            _dataTextBox.TextChanged += new EventHandler(_dataTextBox_TextChanged);
            this.Controls.Add(_dataTextBox);
        }
        /// <summary>
        /// Event raised when the user changes the text
        /// </summary>
        private void _dataTextBox_TextChanged(object sender, EventArgs e) {
            Data = TextBox.Text;
        }
        // ***********************************************
        // Here starts the connection related code
        // ***********************************************
        /// <summary>
        /// Exposes the Provider Connection Point
        /// </summary>
        [ConnectionProvider("IFoo Provider")]
        public IFoo GetProvider() {
            return this;
        }
        /// <summary>
        /// Implementation of the interface
        /// </summary>
        string IFoo.StringData {
            get {
                return this.Data;
            }
        }
    }
}
            
            VB.NET Code
            
Imports System
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Namespace CarlosAg.Samples
    ' <summary>
    ' Simple Interface that Shares a String property
    ' </summary>
    Public Interface IFoo
        ReadOnly Property StringData() As String
    End Interface
    ' <summary>
    ' WebPart that serves as an IFoo Provider.
    '     Prompts the user using a TextBox
    ' </summary>
    Public Class FooProvider
        Inherits WebPart
        Implements IFoo
        Private _dataTextBox As TextBox
        ' <summary>
        ' Creates a new instance of the class
        ' </summary>
        Public Sub New()
            MyBase.New()
        End Sub
        ' <summary>
        ' Public property to allow users to set the Data
        ' </summary>
        <Personalizable(PersonalizationScope.User)> _
        Public Property Data() As String
            Get
                Return TextBox.Text
            End Get
            Set(ByVal value As String)
                TextBox.Text = value
            End Set
        End Property
        ' <summary>
        ' Text Box to allow end users to change the data
        ' </summary>
        Protected ReadOnly Property TextBox() As TextBox
            Get
                EnsureChildControls()
                Return _dataTextBox
            End Get
        End Property
        ' <summary>
        ' Implementation of the interface
        ' </summary>
        ReadOnly Property IFoo_StringData() As String _
                            Implements IFoo.StringData
            Get
                Return Me.Data
            End Get
        End Property
        ' <summary>
        ' Overrides the CreateChildControls to create the TextBox
        ' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim _label As Label = New Label
            _label.Text = "Enter a Number:"
            _label.Font.Bold = True
            Me.Controls.Add(_label)
            _dataTextBox = New TextBox
            _dataTextBox.AutoPostBack = True
            AddHandler _dataTextBox.TextChanged, AddressOf Me._dataTextBox_TextChanged
            Me.Controls.Add(_dataTextBox)
        End Sub
        ' <summary>
        ' Event raised when the user changes the text
        ' </summary>
        Private Sub _dataTextBox_TextChanged(ByVal sender As Object, _
                            ByVal e As EventArgs)
            Data = TextBox.Text
        End Sub
        ' ***********************************************
        ' Here starts the connection related code
        ' ***********************************************
        ' <summary>
        ' Exposes the Provider Connection Point
        ' </summary>
        <ConnectionProvider("IFoo Provider")> _
        Public Function GetProvider() As IFoo
            Return Me
        End Function
    End Class
End Namespace
        
        Creating the page
        
            Now we will create the page where we actually use both webparts.
            
        
        Create the Page
        
            - Select the Project node
- Right click it and select the option Add New Item
- Select the option Web Form
- Type Sample.aspx as the name for the Page
- Replace the content of the Page so that it looks as the following code 
            Code
            
<%@ Page Language="C#" %>
<%@ Register TagPrefix="sample" Namespace="CarlosAg.Samples" %>
<html>
<head runat="server">
    <title>Transformer Sample</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:WebPartManager ID="WebPartManager1" Runat="server" />
        <asp:WebPartPageMenu ID="WebPartPageMenu1" Runat="server" />
        <hr />
        <asp:WebPartZone ID="WebPartZone1" Runat="server" PartTitleStyle-BackColor="#99ccff">
            <ZoneTemplate>
                <sample:FooProvider Title="Foo Provider" Runat="server" ID="fooProvider1" Data="23" />
                <sample:BarConsumer Title="Bar Consumer" Runat="server" ID="barConsumer1" />
            </ZoneTemplate>
            <PartStyle BorderColor="SlateGray" BorderStyle="Solid" BorderWidth="4px" />
        </asp:WebPartZone>
        <br />
        <asp:ConnectionsZone ID="ConnectionsZone1" Runat="server" HeaderStyle-BackColor="#99ccff" />
    </form>
</body>
</html>
        
        
            If you try running the page now, you should be able to see both webparts working, you can use the WebPartPageMenu to switch to connect mode, and you will notice that
            no connection can be established, since as we said they are incompatible.
        
        Creating the Transformer
        
            Now we will create the actual Transformer class that will allow us to connect both webparts.
            
        
        Create the FooToBarTransformer class
        
            - Select the Code node
- Right click it and select the option Add New Item
- Select the option Class
- Type FooToBarTransformer as the name for the Class
- Replace the content so that it looks as the following code 
C# Code
namespace CarlosAg.Samples {
    using System;
    using System.Web.UI.WebControls.WebParts;
    [Transformer(typeof(IFoo), typeof(IBar))]
    public class FooToBarTransformer : Transformer, IBar {
        IFoo _foo;
        /// <summary>
        /// Transforms from IFoo to IBar
        /// </summary>
        public override object Transform(object providerData) {
            _foo = (IFoo)providerData;
            return this;
        }
        /// <summary>
        /// Implements IBar funtionality
        /// </summary>
        int IBar.IntegerData {
            get {
                if (_foo.StringData != null) {
                    try {
                        return int.Parse(_foo.StringData);
                    }
                    catch { }
                }
                return -1;
            }
        }
    }
}
VB.NET Code
Imports System
Imports System.Web.UI.WebControls.WebParts
Namespace CarlosAg.Samples
    <Transformer(GetType(IFoo), GetType(IBar))> _
    Public Class FooToBarTransformer
        Inherits Transformer
        Implements IBar
        Private _foo As IFoo
        ' <summary>
        ' Implements IBar funtionality
        ' </summary>
        ReadOnly Property IBar_IntegerData() As Integer _
                Implements IBar.IntegerData
            Get
                If (Not (_foo.StringData) Is Nothing) Then
                    Try
                        Return CInt(_foo.StringData)
                    Catch ex As System.Exception
                    End Try
                End If
                Return -1
            End Get
        End Property
        ' <summary>
        ' Transforms from IFoo to IBar
        ' </summary>
        Public Overrides Function Transform(ByVal providerData As Object) As Object
            _foo = CType(providerData, IFoo)
            Return Me
        End Function
    End Class
End Namespace
    If you try running the page again, you will still see you are not able to connect them. The reason for this is that in order
    for transformers to work you have to "publish" them in the web.config or machine.config.
Modifying the web.config
Open the web.config and add the webParts section that is shown below so that it is inside the system.web section.
Web.Config
<?xml version="1.0" ?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
    <system.web>
        <authentication mode="Windows" />
        <webParts>
            <transformers>
                <add name="Foo To Bar Transformer" 
                     type="CarlosAg.Samples.FooToBarTransformer"  />
            </transformers>
        </webParts>
    </system.web>
</configuration>
        Running the Sample
        
            To run the sample just browse to Sample.aspx.
            
            Select the Connect WebParts option in the drop down menu and select the Change button
            
 
            
            Click the Connect Verb from the Popup Menu that will appear in the Title of the Provider WebPart.
            

            At this point the WebPart framework will realize that this WebPart can only be used as a provider and will enable only a link to connect to a consumer.
            Click the Link "Create a Connection to a Consumer".
            
            The WebPart framework will look for all compatible consumer connections and will look for possible transformers as well.
            It will realize that the FooToBarTransformer is available and so it will display as a possible connection the Foo -> Bar.
            

            Select the Bar consumer from the drop down, and click Connect.
            
            At this point the webparts are connected, so type some numbers in the text box and press Tab, or move the focus out of the text box so the autopostback works and will update the second webpart.
            
        
        
        
            Connections are a very powerfull feature in WebParts. Transformers give the ability to connect webparts that otherwise would be incompatible.
            
            In this tutorial we just scratched the surface of Transformers, there are other methods that you could use to create a User interface to prompt for additional values or parameters for your transformer as well as some methods to load and save custom state that could be use during the transformation.