Friday 6 February 2009

Using Reflection in CodeSmith Templates to Generate your UI

We are currently evaluating whether to use The Text Template Transformation Toolkit (aka T4 - used by MS internally for MVC and Entity Frameworks and built into VS 2008 - see http://www.olegsych.com/2007/12/text-template-transformation-toolkit/) or CodeSmith for generation of our UI layer. This will save us time when getting started with a new application and remove a lot of the monkey work that needs to be done in the UI layer. This code generation will also be a selling point for the Oakton Application Framework.

Our planned process is to:

1) Create our Data Transfer Objects with the lists and properties we wish to display in the User Interface (ie not generate the UI directly from the database schema).
2) Quickly generate basic pages using Reflection on our Data Transfer Objects (Dtos)

This is a simplified example of using CodeSmith templates and the System.Reflection namespace to spit out html for your page based on the properties of your Dto classes (or any other classes that you wish to bind to your UI).

<%--
Name: GenerateUIFromDtoViaReflection_Draft0-1
Author: David Klein
Description: Using Reflection to Generate UI from Dto Assembly when has correct Mapping Attributes
Note: Make sure that referenced assemblies are in the GAC or in the CodeSmith /bin directory so they can be
resolved correctly.
--%>
<%@ CodeTemplate Language="C#" TargetLanguage="Text" Src="" Inherits="" Debug="True"
Description="Template description here." %>
<%@ Property Name="AssemblyPath"
Type="System.String" Default="c:\temp\MyDtoAssembly.dll" Optional="false"
Category="Strings" Description="The path to your dto."
Editor="System.Windows.Forms.Design.FileNameEditor" %>
<%@ Property Name="TypeName"
Type="System.String" Optional="false" Category="Strings"
Description="Fully qualified name for your Dto" %>
<%@ Assembly Name="System.Data" %>
<%@ Assembly Name="Oakton.Sample.Common.Dto" %>
<%@ Assembly Name="Oakton.Framework.Common" %>
<%@ Assembly Name="Oakton.Sample.Common" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="Oakton.Sample.Common.Dto" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="Oakton.Framework.Common" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Windows.Forms" %>

<%
//Debugger.Break();
MessageBox.Show(TypeName);
Assembly assembly = System.Reflection.Assembly.LoadFile(AssemblyPath);
Type objectType = assembly.GetType(TypeName);

//System.Type objectType = System.Type.GetType(TypeName);
//System.Windows.Forms.MessageBox.Show("Is null {0}", (objectType == null).ToString());

PropertyInfo[] properties = objectType.GetProperties();

foreach(PropertyInfo property in properties)
{

//Add Property of Dto to UI
string fieldName = property.Name;
%>
<div class="row">
<label>
<asp:Literal ID="<% =fieldName %>Literal" runat="server" Text='<%%$ Resources:CommonResources, CustomerDetail_<% =fieldName %> %%>' />
</label>
<span class="input">
<asp:TextBox runat="server" ID="<%=fieldName%>TextBox" Text='<%%# Bind("<% =fieldName %>")%%>'
SkinID="short" />
</span>
</div>
<%


}
%>



If you are interested in some of the Patterns (e.g. Dtos and how they should be populated by Transfer Object Assemblers), mentioned above, see (Gasp! It's not from Microsoft!) the Java J2EE patterns site at http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObjectAssembler.html.
The same principles apply regardless of your selected language.



No comments: