Creating an ITemplate from a String
- by Damon
I do a lot of work with control templates, and one of the pieces of functionality that I've always wanted is the ability to build a ITemplate from a string. Throughout the years, the topic has come up from time to time, and I never really found anything about how to do it. though I have run across a number of postings from people who are also wanting the same capability. As I was messing around with things the other day, I stumbled on how to make it work and I feel really foolish for not figuring it out sooner. ITemplate is an interface that exposes a single method named InstantiateIn. I've been searching for years for some magical .NET framework component that would take a string and convert it into an ITemplate, when all along I could just build my own. Here's the code: /// <summary> /// Allows string-based ITempalte implementations /// </summary> public class StringTemplate : ITemplate { #region Constructor(s) //////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Constructor /// </summary> /// <param name="template">String based version of the control template.</param> public StringTemplate(string template) { Template = template; } /// <summary> /// Constructor /// </summary> /// <param name="template">String based version of the control template.</param> /// <param name="copyToContainer">True to copy intermediate container contents to the instantiation container, False to leave the intermediate container in place.</param> public StringTemplate(string template, bool copyToContainer) { Template = template; CopyToContainer = copyToContainer; } //////////////////////////////////////////////////////////////////////////////////////////// #endregion #region Properties //////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// String based template /// </summary> public string Template { get; set; } /// <summary> /// When a StringTemplate is instantiated it is created inside an intermediate control /// due to limitations of the .NET Framework. Specifying True for the CopyToContainer /// property copies all the controls from the intermediate container into instantiation /// container passed to the InstantiateIn method. /// </summary> public bool CopyToContainer { get; set; } //////////////////////////////////////////////////////////////////////////////////////////// #endregion #region ITemplate Members //////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Creates the template in the specified control. /// </summary> /// <param name="container">Control in which to make the template</param> public void InstantiateIn(Control container) { Control tempContainer = container.Page.ParseControl(Template); if (CopyToContainer) { for (int i = tempContainer.Controls.Count - 1; i >= 0; i--) { Control tempControl = tempContainer.Controls[i]; tempContainer.Controls.RemoveAt(i); container.Controls.AddAt(0, tempControl); } } else { container.Controls.Add(tempContainer); } } //////////////////////////////////////////////////////////////////////////////////////////// #endregion } //class Converting a string into a user control is fairly easy using the ParseControl method from a Page object. Fortunately, the container passed into the InstantiateIn method has a Page property. One caveat, however, is that the Page property only has a reference to a Page if the container is located ON the page. If you run into this problem, you may have to find a creative way to get the Page reference (you can add it to the constructor, store it in the request context, etc). Another issue that I ran into is that the ParseControl creates a new control, parses the string template, places any controls defined in the template onto the new control it created, and returns that new control with the template on it. You cannot pass in your own container. Adding this directly to the container provided as a parameter in the InstantiateIn means that you end up with an additional "level" in the control hierarchy. To avoid this, I added code in that removes each control from the intermediate container and places it into the actual container. I am not, however, sure about the performance penalty associated with moving a bunch of control from one place to another, nor am I completely sure if doing such a move completely screws something up if you have a code behind, etc. It seems to work when it's just a template, but my testing was ever-so-slightly shy of thorough when it comes to other crazy scenarios. As a catch-all, I added a Boolean property called CopyToContainer that allows you to turn the copying on or off depending on your desires and needs. Technorati Tags: .NET,ASP.NET,ITemplate,Development,C#,Custom Controls,Server Controls