29 August 2009

Programmatically Adding a Control Adapter

I have a Control Adapter I use frequently and every time I start a new SharePoint WCM project, I have to rethink about how to use  it to my Web Application and how to deploy the compat.browser modifications. After some research on the net I came up with two solutions to add Control Adapters programmatically, so no browser.compat modifications are necessary any more.

  1. Add a Control Adapter to the current HttpContext.
  2. Add a Control Adapter to a particular Control.

I created some helper functions that can do the binding of the Control Adapter to a Control or Control-Type. In both cases the functions are used from within a custom (master)page class.

The first solution is using strate forward .NET code, but for the second I had to use some Reflection. (so beware!)

1. Adding a Control Adapter to the current HttpContext

You can use the Global.asax file or a masterpage to add Control Adapters to the current HttpContext.

In the global.asax implement the code in the Application_Start event to bind the Control Adapter.

<script runat="server">
    void Application_Start(Object sender, EventArgs e)
    {
        AddControlAdapterToType<Unive.Foundation.UI.ControlAdapters.SharePointWebPartZoneControlAdapter>(typeof(Microsoft.SharePoint.WebPartPages.WebPartZone));
    }

    private static void AddControlAdapterToType<T>(Type controlType) where T : ControlAdapter, new()
    {
        if (controlType == null)
        {
            throw new ArgumentNullException("controlType", "This argument can not be null!");
        }

        IDictionary adapters = HttpContext.Current.Request.Browser.Adapters;
        string key = controlType.AssemblyQualifiedName;
        if (!adapters.Contains(key))
        {
            string adapter = typeof(T).AssemblyQualifiedName;
            adapters.Add(key, adapter);
        }
    }
</script>

In a masterpage you must implement code in the constructor to bind the Control Adapter. This is necessary so the page will use the Control Adapter in the first run. Doing the binding in the OnInit or later is to late and all the child controls in the markup will already be instantiated.

using System;
using System.Web;
using System.Web.UI.Adapters;

public class MyMasterPage : System.Web.UI.MasterPage
{
    public MyMasterPage()
    {
        AddControlAdapterToType<SharePointWebPartZoneControlAdapter>(typeof(Microsoft.SharePoint.WebPartPages.WebPartZone));
    }

    private static void AddControlAdapterToType<T>(Type controlType) where T : ControlAdapter, new()
    {
        if (controlType == null)
        {
            throw new ArgumentNullException("controlType", "This argument can not be null!");
        }

        IDictionary adapters = HttpContext.Current.Request.Browser.Adapters;
        string key = controlType.AssemblyQualifiedName;
        if (!adapters.Contains(key))
        {
            string adapter = typeof(T).AssemblyQualifiedName;
            adapters.Add(key, adapter);
        }
    }
}

2. Add a Control Adapter to a particular Control

To bind a Control Adapter to a particular control, you must first find the control instance in the page and then you can do the binding.

using System;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.Adapters;

public class MyMasterPage : System.Web.UI.MasterPage
{
    protected override void CreateChildControls()
    {
        base.CreateChildControls();

        Control control = Page.FindControl("WebPartZone3");
        AddControlAdapterToControl<SharePointWebPartZoneControlAdapter>(control);
    }

    private static void AddControlAdapterToControl<T>(Control control) where T : ControlAdapter, new()
    {
        if (control == null)
        {
            throw new ArgumentNullException("control", "This argument can not be null!");
        }

        T adapter = new T();

        // Using reflection to bind controladapter to control
        control.GetType().GetField("_adapter", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(control, adapter);
        // Using reflection to bind control to controladapter
        adapter.GetType().GetField("_control", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(adapter, control);
    }
}

No comments:

Post a Comment