I already posted here about Tag Mapping and how helpful it can be, but naturally there's are a few improvements that I would like to see available in future framework release.
The one I expect the most is about the capability of mapping dynamic created controls using the same rules as Tag Mapping uses when interpreting the page markup.
Without this capability we can never use widely the tag mapping because whenever we need to create dynamic controls they will be strongly coupled to a specific control implementation.
Imagine this scenario:
- First you have built an web application that use standard ASP.NET TextBox control, some of them dynamically created.
- Now, imagine that you want to reuse that application, as is, but instead of ASP.NET Textbox control you want to use your own Textbox implementation.
This task could be easily accomplished using Tag Mapping if no dynamic controls were used, but in this scenario ASP.NET give us no solution, so the application cannot be reused without modifications.
Naturally, you can copy/paste your application and make the necessary changes, or even add a few if statements, but that will only increase complexity and maintenance effort.
Until the .NET team provide us such capability we must do the magic ourselves.
My proposal is an help class (DynamicControlBuilder) that provide us two methods: GetMappedType and CreateControl.
/// <summary>
/// Gets the mapped <see cref="System.Web.UI.Control"/> type.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <see cref="System.Type"/> object.</returns>
public static Type GetMappedType(Type type, string prefix)
{
if (!typeof(Control).IsAssignableFrom(type))
{ throw new ArgumentOutOfRangeException("type", "Must inherit from Control.");
}
Type mappedtype;
if (!string.IsNullOrEmpty(prefix))
{
TagPrefixInfo prefixinfo;
if (!m_prefixes.TryGetValue(prefix, out prefixinfo))
{
throw new ArgumentException("prefix", "No prefix found.");
}
else
{
type = BuildManager.GetType(string.Format("{0}.{1}, {2}", prefixinfo.Namespace, type.UnderlyingSystemType.Name, prefixinfo.Assembly), false);
if (type == null)
{
throw new ArgumentException("type", "Control not found within specified prefix.");
}
}
}
if (m_tagMappings.TryGetValue(type.UnderlyingSystemType, out mappedtype))
{
return mappedtype;
}
return type;
}
/// <summary>
/// Creates a dynamic mapped <see cref="System.Web.UI.Control"/>.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <paramref name="T"/> object.</returns>
public static Control CreateControl(Type type, string prefix)
{
Type mappedType = GetMappedType(type, prefix); ;
return (Control)Activator.CreateInstance(mappedType);
}
this.Page.Controls.Add(DynamicControlBuilder.CreateControl<System.Web.UI.WebControls.TextBox>("foo"));
this.Page.Controls.Add(DynamicControlBuilder.CreateControl(typeof(System.Web.UI.WebControls.TextBox), "foo"));
this.Page.Controls.Add(DynamicControlBuilder.CreateControl(typeof(System.Web.UI.WebControls.TextBox)));
Source:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.Configuration;
using System.Web.UI;
using System.Configuration;
using System.Web.UI.MobileControls;
using System.Web.Compilation;
namespace NG.Web.UI
{
/// <summary>
/// This class provides methods to create dynamically mapped controls, using the PageSection TagMappings and Prefix info.
/// </summary>
public class DynamicControlBuilder
{
#region Private Fields
private static Dictionary<Type, Type> m_tagMappings = null;
private static Dictionary<string, TagPrefixInfo> m_prefixes = null;
private static object m_syncobj = new object();
#endregion Private Fields
#region Constructors
/// <summary>
/// Static constructor
/// </summary>
static DynamicControlBuilder()
{
if (m_tagMappings == null)
{
lock (m_syncobj)
{
if (m_tagMappings == null)
{
Dictionary<Type, Type> tagmapping = new Dictionary<Type, Type>();
PagesSection section = (PagesSection)WebConfigurationManager.GetSection("system.web/pages");
foreach (TagMapInfo info in section.TagMapping)
{
Type type = BuildManager.GetType(info.TagType, true);
Type c = BuildManager.GetType(info.MappedTagType, true);
if (!type.IsAssignableFrom(c))
{
throw new ConfigurationErrorsException("Mapped type must inherit");
}
tagmapping[type] = c;
}
Dictionary<string, TagPrefixInfo> prefixes = new Dictionary<string, TagPrefixInfo>(StringComparer.OrdinalIgnoreCase);
foreach (TagPrefixInfo info in section.Controls)
{
prefixes[info.TagPrefix] = info;
}
m_prefixes = prefixes;
m_tagMappings = tagmapping;
}
}
}
}
#endregion Constructors
#region Public Methods
/// <summary>
/// Gets the mapped <see cref="System.Web.UI.Control"/> type.
/// </summary>
/// <typeparam name="T">The <see cref="System.Web.UI.Control"/> type to be mapped</typeparam>
/// <returns>A <see cref="System.Type"/> object.</returns>
public static Type GetMappedType<T>() where T : Control
{
return GetMappedType(typeof(T), null);
}
/// <summary>
/// Gets the mapped <see cref="System.Web.UI.Control"/> type.
/// </summary>
/// <typeparam name="T">The <see cref="System.Web.UI.Control"/> type to be mapped</typeparam>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <see cref="System.Type"/> object.</returns>
public static Type GetMappedType<T>(string prefix) where T : Control
{
return GetMappedType(typeof(T), prefix);
}
/// <summary>
/// Gets the mapped <see cref="System.Web.UI.Control"/> type.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <returns>A <see cref="System.Type"/> object.</returns>
public static Type GetMappedType(Type type)
{
return GetMappedType(type, null);
}
/// <summary>
/// Gets the mapped <see cref="System.Web.UI.Control"/> type.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <see cref="System.Type"/> object.</returns>
public static Type GetMappedType(Type type, string prefix)
{
if (!typeof(Control).IsAssignableFrom(type))
{
throw new ArgumentOutOfRangeException("type", "Must inherit from Control.");
}
Type mappedtype;
if (!string.IsNullOrEmpty(prefix))
{
TagPrefixInfo prefixinfo;
if (!m_prefixes.TryGetValue(prefix, out prefixinfo))
{
throw new ArgumentException("prefix", "No prefix found.");
}
else
{
type = BuildManager.GetType(string.Format("{0}.{1}, {2}", prefixinfo.Namespace, type.UnderlyingSystemType.Name, prefixinfo.Assembly), false);
if (type == null)
{
throw new ArgumentException("type", "Control not found within specified prefix.");
}
}
}
if (m_tagMappings.TryGetValue(type.UnderlyingSystemType, out mappedtype))
{
return mappedtype;
}
return type;
}
/// <summary>
/// Creates a dynamic mapped <see cref="System.Web.UI.Control"/>.
/// </summary>
/// <typeparam name="T">The <see cref="System.Web.UI.Control"/> type to be mapped</typeparam>
/// <returns>A <paramref name="T"/> object.</returns>
public static T CreateControl<T>() where T : Control, new()
{
return (T)CreateControl(typeof(T), null);
}
/// <summary>
/// Creates a dynamic mapped <see cref="System.Web.UI.Control"/>.
/// </summary>
/// <typeparam name="T">The <see cref="System.Web.UI.Control"/> type to be mapped</typeparam>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <paramref name="T"/> object.</returns>
public static T CreateControl<T>(string prefix) where T : Control, new()
{
return (T)CreateControl(typeof(T), prefix);
}
/// <summary>
/// Creates a dynamic mapped <see cref="System.Web.UI.Control"/>.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <returns>A <paramref name="T"/> object.</returns>
public static Control CreateControl(Type type)
{
return CreateControl(type, null);
}
/// <summary>
/// Creates a dynamic mapped <see cref="System.Web.UI.Control"/>.
/// </summary>
/// <param name="type">The <see cref="System.Web.UI.Control"/> type to be mapped</param>
/// <param name="prefix">The namespace prefix.</param>
/// <returns>A <paramref name="T"/> object.</returns>
public static Control CreateControl(Type type, string prefix)
{
Type mappedType = GetMappedType(type, prefix); ;
return (Control)Activator.CreateInstance(mappedType);
}
#endregion Public Methods
}
}