Andy Smith's Blog

Page.RegisterStartupScript('Andy', 'MetaBuilders_WebControls_GainKnowledge();');

A Simpler Way To Create A Designer For A Templated Control

When you make a control that has templates, you have to write a designer which enables the templates to be edited within the designer. And I’m not talking simple code here. It’s like a couple hundred lines. When I look at the code required to make the designer, it’s also almost completely the same except for a few control-specific things.

So to make things a little easier, I wrote a SimpleTemplatedControlDesigner, which derives from TemplatedControlDesigner. This class lets you get going very quickly for a simple design experience on a templated control. For most cases, you don’t even need to extend it at all. And at the very least, you can use it to see how to write a templated control designer.

using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.Reflection;
namespace WebControlLibrary1 {
 public class SimpleTemplatedControlDesigner : TemplatedControlDesigner {
  // Override this if you want to be able to render the control when not all the templates have been defined.
  protected virtual Boolean CanRenderWithTemplateEmpty( String templateName ) {
   return false;
  }
  public override bool AllowResize {
   get {
    return !this.TemplatesAreEmpty || this.InTemplateMode;
   }
  }
  #region Design-time HTML
  public override string GetDesignTimeHtml() {
   if ( !CanRenderWithCurrentTemplates() ) {
    return this.GetEmptyDesignTimeHtml();
   }
   String designTimeHtml = String.Empty;
   try {
    ((Control)this.Component).DataBind();
    designTimeHtml = base.GetDesignTimeHtml();
   } catch( Exception ex ) {
    designTimeHtml = this.GetErrorDesignTimeHtml( ex );
   }
   return designTimeHtml;
  }
  protected override string GetEmptyDesignTimeHtml() {
   return this.CreatePlaceHolderDesignTimeHtml( this.EditTemplateInstructions );
  }
  protected override string GetErrorDesignTimeHtml(Exception e) {
   return this.CreatePlaceHolderDesignTimeHtml( "There was an error rendering the control." );
  }
  #endregion
  #region Template-editing
  private TemplateEditingVerb[] _templateEditingVerbs;
  private String[] templateNames;
  private Boolean TemplatesAreEmpty {
   get {
    foreach( String templateName in this.TemplateNames ) {
     if ( GetTemplate( templateName ) == null ) {
      return false;
     }
    }
    return true;
   }
  }
  private Boolean CanRenderWithCurrentTemplates() {
   foreach( String templateName in this.TemplateNames ) {
    if ( GetTemplate( templateName ) == null ) {
     return this.CanRenderWithTemplateEmpty( templateName );
    }
   }
   return true;
  }
  private Boolean TemplateIsEmpty( String templateName ) {
   return GetTemplate( templateName ) == null;
  }
  protected virtual String EditTemplateInstructions {
   get {
    return "Right click to edit the templates for this control";
   }
  }
  private String[] TemplateNames {
   get {
    if ( templateNames == null ) {
     ArrayList names = new ArrayList();
     Type componentType = this.Component.GetType();
     foreach( PropertyInfo prop in componentType.GetProperties() ) {
      if ( prop.PropertyType.IsAssignableFrom( typeof ( ITemplate ) ) ) {
       names.Add( prop.Name );
      }
     }
     if ( names.Count > 0 ) {
      this.templateNames = new String[ names.Count ];
      names.CopyTo( this.templateNames );
     }
    }
    return templateNames;
   }
  }
  private ITemplate GetTemplate( String templateName ) {
   Type componentType = this.Component.GetType();
   PropertyInfo property = componentType.GetProperty( templateName );
   ITemplate value = property.GetValue( this.Component, null ) as ITemplate;
   return value;
  }
  private void SetTemplate( String templateName, ITemplate template ) {
   Type componentType = this.Component.GetType();
   PropertyInfo property = componentType.GetProperty( templateName );
   property.SetValue( this.Component, template, null );
  }
  protected override TemplateEditingVerb[] GetCachedTemplateEditingVerbs() {
   if ( _templateEditingVerbs == null ) {
    _templateEditingVerbs = new TemplateEditingVerb[ this.TemplateNames.Length ];
    for( Int32 i = 0; i < _templateEditingVerbs.Length; i++ ) {
     _templateEditingVerbs[ i ] = new TemplateEditingVerb( this.TemplateNames[ i ], i, this );
    }
   }
   return _templateEditingVerbs;
  }
  protected override ITemplateEditingFrame CreateTemplateEditingFrame(TemplateEditingVerb verb) {
   ITemplateEditingFrame frame = null;
   if ( ( _templateEditingVerbs != null ) ) {
    for ( Int32 i = 0; i < _templateEditingVerbs.Length; i++ ) {
     if ( _templateEditingVerbs[ i ] == verb ) {
      ITemplateEditingService teService = (ITemplateEditingService)GetService( typeof( ITemplateEditingService ) );
      if ( teService != null ) {
       frame = teService.CreateFrame( this, verb.Text, new String[] { this.TemplateNames[ i ] } );
      }
     }
    }
   }
   return frame;
  }
  public override string GetTemplateContent(ITemplateEditingFrame editingFrame, string templateName, out bool allowEditing) {
   String content = "";
   allowEditing = true;
   if ( ( _templateEditingVerbs != null ) ) {
    for ( Int32 i = 0; i < _templateEditingVerbs.Length; i++ ) {
     if ( _templateEditingVerbs[ i ] == editingFrame.Verb ) {
      ITemplate editedTemplate = this.GetTemplate( templateName );
      if ( editedTemplate != null ) {
       content = this.GetTextFromTemplate( editedTemplate );
      }
     }
    }
   }
   return content;
  }
  public override void SetTemplateContent(ITemplateEditingFrame editingFrame, string templateName, string templateContent) {
   if ( ( _templateEditingVerbs != null ) ) {
    for ( Int32 i = 0; i < _templateEditingVerbs.Length; i++ ) {
     if ( _templateEditingVerbs[ i ] == editingFrame.Verb ) {
    
      ITemplate newTemplate = null;
      if ( templateContent != null && templateContent.Length != 0 ) {
       newTemplate = this.GetTemplateFromText( templateContent );
      }
      this.SetTemplate( templateName, newTemplate );
     }
    }
   }
  }
  #endregion

  #region Dispose
  private void DisposeTemplateEditingVerbs() {
   if ( _templateEditingVerbs != null ) {
    for( Int32 i = 0; i < _templateEditingVerbs.Length; i++ ) {
     _templateEditingVerbs[ i ].Dispose();
    }
    _templateEditingVerbs = null;
   }
  }

  protected override void Dispose(bool disposing) {
   if ( disposing ) {
    DisposeTemplateEditingVerbs();
   }
   base.Dispose( disposing );
  }
  public override void OnComponentChanged(object sender, ComponentChangedEventArgs ce) {
   base.OnComponentChanged (sender, ce);
   if ( ce.Member != null ) {
    String name = ce.Member.Name;
    if ( name == "Font" || name == "ForeColor" || name == "BackColor" ) {
     DisposeTemplateEditingVerbs();
    }
   }
  }
  #endregion
 }
}

I know that's a whole heck of a lot of code... without a whole heck of a lot of comments... but you'll probably only really need to extend it at the CanRenderWithTemplateEmpty method. If nothing else, I hope this saves somebody some time.

Comments

TrackBack said:

# May 18, 2004 2:12 AM

DrFooMod2 said:

Is there a demo solution for d/l?
# May 18, 2004 12:03 PM

Andy Smith said:

No demo. If you have a control that has templates, simply apply this designer.
# May 18, 2004 1:23 PM

Shannon J Hager said:

Okay, I'll be That Guy...

How do I "simply apply this designer"? Do you have a link to the info handy or should I ask google?
# May 19, 2004 12:46 PM

Andy Smith said:

with an attribute on the class.

[
System.ComponentModel.Designer( typeof( SimpleTemplatedControlDesigner ) ),
]
public class MyControl : WebControl {
# May 19, 2004 3:03 PM

Scott Galloway said:

Thanks Andy, great stuff as usual - you saved me a few hours work with this - currently writing about 15 templated server controls for a CMS app...
# June 18, 2004 6:16 PM

TrackBack said:

# June 18, 2004 8:18 PM

TrackBack said:

# June 18, 2004 8:20 PM

Raymond Brink said:

Sounds great and I'd love to try it, but...

using System.Web.UI.Design; ???

Where do I get that library from?
# July 10, 2004 11:34 AM

Andy Smith said:

System.Web.UI.Design is in the System.Design.dll assembly.
# July 13, 2004 7:02 PM

TrackBack said:

# August 13, 2004 4:58 PM

TrackBack said:

# August 14, 2004 8:33 PM

TrackBack said:

# November 18, 2004 1:33 PM

TrackBack said:

<p>&lt;UL&gt; &lt;LI&gt;&lt;A href=&quot;http://weblogs.asp.net/express/&quot; target=_blank&gt;Blog az expressről&lt;/A&gt;&lt;/LI&gt; &lt;LI&gt;&lt;A href=&quot;http://weblogs.asp.net/asmith/articles/SimpleTemplatedControlDesigner.aspx&quot; target=_bl
# March 24, 2005 11:10 AM

Rob said:

Come on! No comments since 2004? I think this is great, thanks Andy. This is a solution that I've been looking for in a couple of my controls. Kudos.

# October 12, 2007 3:17 PM

Chris said:

Very nice piece of code you shared. Saved me some time as well. You should consider updating it to .net 3.5

Thanks again, and best wishes.

# October 22, 2008 10:40 AM

Buspar. said:

Does buspar work. Buying buspar online. Buspar.

# November 12, 2008 1:36 PM

Felipe Oliveira said:

You are the man!

Thanks for saving me some time.

# January 16, 2009 11:28 AM

Patrick said:

Can you please suggest me how to consume this class from a Template user control. Thank in advance

# February 2, 2009 6:45 AM

Prasanna W said:

I'm still new to the custom server controls and tried to add your designer in my custom server control. I'm using .Net 3.5 and VS2008

I tried to use this code as mentioned. But ended up getting a error in CanRenderWithCurrentTemplates()method and reason was TemplateNames string array was empty. I tried to fixed it by adding a condition to check whether TemplateNames array is empty. But that resulted an empty designer.

Really appriciate if someone can help me on this. thanks in advance.

# November 6, 2009 8:08 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)