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
Posted
Mon, Jan 12 2004 1:28 PM
by
Brendan Tompkins