Als ich in den letzten Tagen in einem Projekt eine
statische Webpartverbindung so anpassen musste, dass diese individuell konfigurierbare
ist, habe ich festgestellt, dass dieses Thema erschreckend schlecht
dokumentiert ist und auch im Netz nur Wenig bis Garnichts zu finden ist. Deshalb
möchte ich in diesem Blog meine Erfahrungen und meinen Lösungsansatz teilen.

 

Der theoretische Aufbau:

 

 

 

Webpartverbindungen lassen sich über einen WebpartTransformer
konfigurieren. Der WebpartTransformer wird verwendet, um ein Feldmapping bzw. eine
Datenumwandlung vorzunehmen, wenn der Provider ein anderes Datenformat sendet
als der Consumer es erwartet.

Um das Feldmapping individuell für jede Verbindung auf
einer Seite zu konfigurieren, steht das ITransformerConfigurationControl-Interface
zur Verfügung. Über das Interface können die Konfigurationswerte für den
WebpartTransformer gesetzt und auf Wunsch über diesen auch an das Consumer-Webpart
weitergegeben werden.

 

Praxis:

Wir beginnen mit dem Aufbau der beiden benötigten
Webparts. Das Provider-Webpart soll Daten aus einer SharePoint-Liste in eine
CheckedListbox auslesen und die ausgewählten Daten an das Consumer-Webpart
übergeben. In der Verbindung soll konfiguriert werden können, wie das
Consumer-Webpart die empfangenen Daten anzeigen soll.

Dafür erstellen wir ein neues leeres SharePoint 2013
Projekt namens ConnectedWebparts als Farm Solution

 

 

 

Wenn das Projekt erstellt ist, fügen wir zwei Interfaceklassen
und zwei Visual Webparts zur Lösung hinzu. Die Interfaces nennen wir IConnectionProviderData
und IConnecionConsumerData und die Webparts nennen wir ConnectionProvider und
ConnectionConsumer.

 

 

Im ConnectionProvider-Webpart fügen wir ein
CheckBoxList-Control ein hinzu, vergeben diesem die ID cblDataSelector und
fügen eine Button-Control mit der ID btnSendData hinzu. Im ConnectionConsumer-Webpart
fügen wir ein Label-Control hinzu und weisen diesem die ID labReceivedData zu.

Jetzt kann der Source-Code der vier Dateien wie folgt
angepasst werden:

 

IConnectionProviderData

using System.Collections.Generic;

 

namespace ConnectedWebparts

{

    public interface IConnectionProviderData

    {

        List<string> Datalist { get; }

    }

}

 

IConnectionConsumerData

using System.Collections.Generic;

 

namespace ConnectedWebparts

{

    public interface IConnectionConsumerData

    {

        int DisplayType { get; }

        List<string> Datalist { get; }

    }

}

 

ConnectionProvider

namespace ConnectedWebparts.ConnectionProvider

{

    [ToolboxItemAttribute(false)]

    public partial class ConnectionProvider : WebPart, IConnectionProviderData

    {

        List<string> Datalist = new List<string>();

        protected void Page_Load(object sender, EventArgs e)

        {

            if (!Page.IsPostBack)

            {

                string listUrl = SPUtility

                    .ConcatUrls(SPContext.Current.Site.ServerRelativeUrl,
“Lists/SampleData”);

                SPList sourceList = SPContext.Current.Web.GetList(listUrl);

                foreach (SPListItem item in sourceList.Items)

                {

                    cblDataSelector.Items.Add(string.Format(“{0}-{1}”, item.ID, item[“Title”]));

                }

            }

        }

 

        protected void btnSendData_Click(object sender, EventArgs e)

        {

            Datalist.Clear();

            foreach (ListItem item in cblDataSelector.Items)

            {

                if (item.Selected)

                    Datalist.Add(item.Text);

            }

        }

 

        // Webpart als Connection Provider registrieren

        [ConnectionProvider(“Data Provider”)]

        public IConnectionProviderData GetProvider()

        {

            return this;

        }

 

           List<string> IConnectionProviderData.Datalist

        {

            get

            {

                return this.Datalist;

            }

        }

 

ConnectionConsumer

namespace ConnectedWebparts.ConnectionConsumer

{

    [ToolboxItemAttribute(false)]

    public partial class ConnectionConsumer : WebPart

    {

        private IConnectionConsumerData m_provider;

 

        // Verbindung aufbauen

        [ConnectionConsumer(“Data Consumer”)]

        public void SetProvider(IConnectionConsumerData provider)

        {

            this.m_provider = provider;

        }

        protected override void OnPreRender(EventArgs e)

        {

            EnsureChildControls();

            if (m_provider != null)

            {

                labReceivedData.Text = “<b>Received
Items:</b><br />”
;

                foreach (string item in m_provider.Datalist)

                {

                    switch (m_provider.DisplayType)

                    {

                        case 0:

                            labReceivedData.Text += item + “<br
/>”
;

                            break;

                        case 1:

                            labReceivedData.Text +=
((item.IndexOf(
“-“) >= 0) ?

                                item.Substring(0, item.IndexOf(“-“)) :

                                item) + “<br
/>”
;

                            break;

                        case 2:

                            labReceivedData.Text +=
((item.IndexOf(
“-“) >= 0) ?

                                item.Substring(item.IndexOf(“-“) + 1) :

                                item) + „<br />“;

                            break;

                    }

                }

            }

            else

                labReceivedData.Text = „Not Connected“;

        }

 

Nachdem wir unsere Webparts fertiggestellt haben, müssen
wir einen Übersetzer erstellen, mit dem wir eine Kommunikation zwischen den beiden
Webparts ermöglichen.

Hierfür fügen wir eine neue Klasse zu unserem Projekt hinzu.
Diese Klasse nennen wir ConnectionTransformer.

 

 

ConnectionTransformer

using System.Collections.Generic;

using System.Security.Permissions;

using System.Web;

using System.Web.UI.WebControls.WebParts;

 

namespace ConnectedWebparts

{

    [AspNetHostingPermission(SecurityAction.Demand, Level =

        AspNetHostingPermissionLevel.Minimal)]

    [AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level =

        AspNetHostingPermissionLevel.Minimal)]

    [WebPartTransformer(typeof(IconnectionProviderData), typeof(IconnectionConsumerData))]

    public class ConnectionTransformer : WebPartTransformer, IconnectionConsumerData

    {

        // Verbindung zum Provider herstellen

        private IconnectionProviderData m_provider;

        public override object Transform(object providerData)

        {

            m_provider = (IconnectionProviderData)providerData;

            return this;

        }

 

        // Transformierte Daten an Consumer senden

        List<string> IconnectionConsumerData.Datalist

        {

            get

            {

                if (m_provider != null && m_provider.Datalist != null)

                    return m_provider.Datalist;

                else

                    return new List<string>();

            }

        }

 

        int IconnectionConsumerData.DisplayType

        {

            get { return 0; }

        }

    }

}

 

Ab diesem Zeitpunkt können unsere Webparts, genau wie bei
einer einfachen, statischen Webpartverbindung mit einander kommunizieren.

Voraussetzung ist, dass der Transformer in der Web.config
eingetragen ist.

 

Web.config

<system.web>

    <webParts>

        <transformers>

            <add name=“MyWebpartTransformer“
type=“ConnectedWebparts.ConnectionTransformer, ConnectedWebparts,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=526cbad3d101423a“ />

        </transformers>

    </webParts>

<system.web>

 

 

 

Als letzten Schritt müssen wir unseren Transformer
konfigurierbar machen. Dafür steht uns das Interface ItransformerConfigurationControl
zur Verfügung.

Wir passen die Klasse ConnectionTransformer wie folgt.

 

ConnectionTransformer

        int IconnectionConsumerData.DisplayType

        {

            get

            {

                int returnValue;

                if (int.TryParse(this.DisplayType, out returnValue))

                    return returnValue;

                else

                    return 0;

            }

        }

        private string m_DisplayType;

        public string DisplayType

        {

            get { return m_DisplayType; }

            set { m_DisplayType = value; }

        }

 

        public override System.Web.UI.Control CreateConfigurationControl()

        {

            return new ConnectionTransformerConfigurationControl(this);

        }

 

        protected override object SaveConfigurationState()

        {

            return m_DisplayType;

        }

 

        protected override void LoadConfigurationState(object savedState)

        {

            try

            {

                m_DisplayType = (string)savedState;

            }

            catch (Exception ex)

            {

                System.Diagnostics.Trace.Write(„Fehler beim Laden
der Verbindungskonfiguration „
+ ex.Message);

            }

        }

 

        # region Konfigurationsdialog

 

        public partial class ConnectionTransformerConfigurationControl : UserControl, ItransformerConfigurationControl

        {

            ConnectionTransformer m_owner;

 

            Label infoLabel = new Label();

            DropDownList consumerTypeSelector = new DropDownList();

 

            public ConnectionTransformerConfigurationControl(ConnectionTransformer owner)

            {

                m_owner = owner;

            }

 

            protected void Page_Load(object sender, EventArgs e)

            {

                consumerTypeSelector.Items.Add(new ListItem(„Full“, „0“));

                consumerTypeSelector.Items.Add(new ListItem(„ID“, „1“));

                consumerTypeSelector.Items.Add(new ListItem(„Value“, „2“));

 

                infoLabel.Text = „Anzeigetyp: „;

 

                Label newLine = new Label();

                newLine.Text = „<br /><br />“;

 

                // Speichern und Abbrechen Button hinzufügen

                Button connect = new Button();

                connect.Text = „Verbinden“;

                connect.Click += connect_Click;

 

                Button cancel = new Button();

                cancel.Text = „Abbrechen“;

                cancel.Click += cancel_Click;

 

                // Inhalt auf Seite schreiben

                this.Controls.Add(infoLabel);

                this.Controls.Add(consumerTypeSelector);

                this.Controls.Add(newLine);

                this.Controls.Add(connect);

                this.Controls.Add(cancel);

                // Verbindungseinstellungen einlesen

                try

                {

                    if (!string.IsNullOrWhiteSpace(m_owner.DisplayType))

                    {

                        consumerTypeSelector.SelectedValue =
m_owner.DisplayType;

                    }

                }

                catch (Exception ex)

                {

                    System.Diagnostics.Trace.Write(„Fehler beim
Setzen der Verbindungseinstellungen „
+ ex.Message);

                }

            }

 

            void cancel_Click(object sender, EventArgs e)

            {

                this.OnCancelled(System.EventArgs.Empty);

            }

 

            void connect_Click(object sender, EventArgs e)

            {

                m_owner.DisplayType =
consumerTypeSelector.SelectedValue;

                this.OnSucceeded(System.EventArgs.Empty);

            }

 

            public event EventHandler Cancelled

            {

                add

                {

                    base.Events.AddHandler(ConnectionTransformerConfigurationControl.EventCancelled, value);

                }

                remove

                {

                    base.Events.RemoveHandler(ConnectionTransformerConfigurationControl.EventCancelled, value);

                }

            }

 

            public event EventHandler Succeeded

            {

                add

                {

                    base.Events.AddHandler(ConnectionTransformerConfigurationControl.EventSucceeded, value);

                }

                remove

                {

                    base.Events.RemoveHandler(ConnectionTransformerConfigurationControl.EventSucceeded, value);

                }

            }

 

            private static readonly object EventCancelled = new object();

            private static readonly object EventSucceeded = new object();

 

            private void OnSucceeded(System.EventArgs e)

            {

                System.EventHandler eventHandler =
(System.
EventHandler)base.Events[ConnectionTransformerConfigurationControl.EventSucceeded];

                if (eventHandler != null)

                {

                    eventHandler(this, e);

                }

            }

 

            private void OnCancelled(System.EventArgs e)

            {

                System.EventHandler eventHandler =
(System.
EventHandler)base.Events[ConnectionTransformerConfigurationControl.EventCancelled];

                if (eventHandler != null)

                {

                    eventHandler(this, e);

                }

            }

        }

 

        # endregion

 

Nach dieser Anpassung steht uns ein Konfigurationsdialog
für unsere Webpartverbindung zur Verfügung.

 

 

Über diesen Dialog, können wir mehrere Consumer-Webparts mit
unserem Provider verbinden und für jede Verbindung eine individuelle
Konfiguration hinterlegen.