CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Brendan Tompkins [MVP]

Blog First. Ask Questions Later.

Visual Studio Code Templates - Modifying Your Default Templates

I spent last couple of days looking into modifying the default templates that come with VS.NET.  It ended up being a strange journey into the guts of the VS.NET template architecture.  It's kind of messy down there, which was surprising, but I ended up learning a lot about modifying templates and adding new template types to VS.NET.  I'm hoping to post more about adding new template types later, but first I thought I'd share how I ended up modifying the default templates that ship with vstudio.

I'm not going to go into the multitude of reasons to do this you might want to do this, but suffice it to say that you can do all kinds of cool stuff, like enforce design patterns, implement custom inheritance, add default comments, etc. once you have the ability.  But, beware!  You're messing around with the guts of Visual Studio and you can easily screw things up (I can attest!). So if you're not sure of your skills and your ability to revert if you have to, don't try this.  Now, if you're ready, here's how to change the wizard for “Web User Control“ known as (CSharpAddWebUserControl):

Background: To add a custom codebehind .cs page to an ASCX page your wizard script must first Create the ASCX control, then delete the auto-generated .ASCX.cs file, and finally add back your custom cs file. VS.NET contains a JavaScript function called “AddFilesToCSharpProject”  The wizard uses this function to add the ASCX file.  Details aside, one issue with the implementation of this function is that it relies on the IDE to add the default codebehind .cs file and the template for this file is not editable. To get around this, you need to create a new .js function that will first delete the default .js file and then add in your custom file.  Specifically, my implementation looks for a custom codebehind file with the same name as the .ascx template file in the template directory for the wizard.  Whew! That was a mouthful.  So here's how to do it:

Step 1: Add the following function to the end of the  file “C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards\1033\Common.js“

/****************************************************************************
 Description: Add an ASCX or ASPX file with a custom Code-Behind file
     this will first create the new file, delete the code-behind
     and then add in the custom file. 
******************************************************************************/
function AddCustomFileToCSharpProject(oProj, strProjectName, strProjectPath, InfFile)
{
 try
 {
  dte.SuppressUI = false;
  var projItems = oProj;

  var strTemplatePath = wizard.FindSymbol("TEMPLATES_PATH");

  var strTpl = "";
  var strName = "";

  // if( Not a web project )
  if(strProjectPath.charAt(strProjectPath.length - 1) != "\\")
  strProjectPath += "\\"; 

  var strTextStream = InfFile.OpenAsTextStream(1, -2);
  while (!strTextStream.AtEndOfStream)
  {
   strTpl = strTextStream.ReadLine();
   if (strTpl != "")
   {
   
    strName = strTpl;

    var strTarget = wizard.FindSymbol("ITEM_NAME");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var TemporaryFolder = 2;
    var tfolder = fso.GetSpecialFolder(TemporaryFolder);
    var strTempFolder = fso.GetAbsolutePathName(tfolder.Path);

    var strFile = strTempFolder + "\\" + fso.GetTempName();

    var strClassName = strTarget.split(".");

    wizard.AddSymbol("SAFE_CLASS_NAME", strClassName[0]);
    wizard.AddSymbol("SAFE_ITEM_NAME", strClassName[0]);
    
    var strTemplate = strTemplatePath + "\\" + strTpl;
    
    wizard.RenderTemplate(strTemplate, strFile, false, true);
   
    var projfile = projItems.AddFromTemplate(strFile, strTarget);
    SafeDeleteFile(fso, strFile);

    // Delete the auto generated cs file
    var strTargetCs = strTarget + ".cs";
    SafeDeleteFile(fso, strProjectPath + strTargetCs)

    // Add back the right one       
    var strTemplateCsFile = strTemplate + ".cs";          

    wizard.RenderTemplate(strTemplateCsFile, strFile, false, true);
    var csprojfile = projItems.AddFromTemplate(strFile, strTargetCs);
    SafeDeleteFile(fso, strFile);
    
    // Open the window
    var window = projfile.Open(vsViewKindPrimary);
    window.visible = true;

   }
  }
  
  strTextStream.Close();
 }
 catch(e)
 {
  strTextStream.Close();
  throw e;
  }
}

Step 2: Modify the wizard javascript “C:\Program Files\Microsoft Visual Studio .NET 2003 VC#\VC#Wizards\CSharpAddWebUserControl\Scripts\1033\default.js“ to call our custom function.  Change line 30 (or thereabouts) starting with “goBack“ to call our custom function:

goBack = AddCustomFileToCSharpProject(selObj, strProjectName, strProjectPath, InfFile);

Step 3: Add your custom CodeBehind File to the “C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards\CSharpAddWebUserControl\Templates\1033“  This file has to be called “WebUserControl.ascx.cs“ because the function above looks for the cs file with the same name as the template.  Here's my custom codebehind file. Note that the wizard adds the proper namespace and class names where it finds  [!output SAFE_NAMESPACE_NAME] and [!output SAFE_CLASS_NAME].

namespace [!output SAFE_NAMESPACE_NAME]
{
 using System;
 using System.Collections;
 using System.Data;
 using System.Drawing;
 using System.Web;
 using System.Web.UI.WebControls;
 using System.Web.UI.HtmlControls;

 ///


 ///  Summary description for [!output SAFE_CLASS_NAME].
 ///

 public abstract class [!output SAFE_CLASS_NAME] : VIT.Web.UserControlBase
 {
  private void Page_Load(object sender, System.EventArgs e)
  {
   try
   {
    this.CheckSecurity();
    this.BindData();
    this.DataBind();
   }
   catch(System.Exception ex)
   {
    this.Publish(ex);
   }
  }
  public void CheckSecurity()
  {
   //TODO: Implement your security here, i.e. CurrentUser.IsInRole(role)
  }

  // Try to stick to this pattern for data binding
  public void BindData()
  {
   this.BindData(false);
  }

  public void BindData(bool refresh)
  {
   //TODO: Lazy load data from session or database, depending on the boolean refresh value

  }

  #region Web Form Designer generated code
  override protected void OnInit(EventArgs e)
  {
   //
   // CODEGEN: This call is required by the ASP.NET Web Form Designer.
   //
   InitializeComponent();   
   base.OnInit(e);
  }

  ///


  ///  Required method for Designer support - do not modify
  ///  the contents of this method with the code editor.
  ///

  private void InitializeComponent()
  {
   this.Load += new System.EventHandler(this.Page_Load);

  }
  #endregion
 
  public override void DataBind()
  {
   // TODO:  Add custom databind implementation
   base.DataBind();
  }

 } 
}

That's it!  You can use the same technique for the “New Web Form” wizard. Good luck!

-Brendan

 



Comments

latinbusca said:

:)
# November 25, 2004 3:04 PM

Karl B said:

I've been playing with this mess as well.

Additionally, as you mentioned - I also wondered how to stop getting pop-up boxes asking the user if they wanted to overwrite the existing file (because my code behind files were all added via the template.inf stream routine.

Obviously the correct solution is to modify the current version of the generic engine which seems to automatically create code behind for aspx pages.

Although for now, this solution seems like it will work - therefore, I'm going to use this interim.
# February 3, 2005 9:37 PM

Todd Anstis said:

I was noodling around with this for a while, and couldn't quite figure out why it wasn't working for me, but i think i realize why now. so that being said - lets say i made a custom wizard called MyWebAppWiz, and in my Templates.inf file, i wanted to add a series of Web Forms like Default.aspx and Users.aspx, and i wanted to use custom codebehinds rather then having the IDE generate them for me. does anyone know how i could accomplish this?
# September 22, 2005 1:28 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Brendan Tompkins

Brendan has been programming with .NET since the first public beta and is owner and operator of Port Technology Services, a consultancy company providing .NET application development services to the Maritime industry. In July, 2007, he was awarded the Microsoft MVP award for ASP.NET. He's also a proud co-founder of failed .COM startup Intrinsigo, and has had a hand in the failure of numerous other businesses. He currently runs CodeBetter.Com and Devlicio.us, and lives in Norfolk, Virgina with his wife Tiara and son Ian.

View Brendan's profile on LinkedIn

Check out Devlicio.us!

Our Sponsors