Skip to content

Commit

Permalink
Merge pull request #24 from dglaser/master
Browse files Browse the repository at this point in the history
Code Templates for Issue #17
  • Loading branch information
erichexter committed Dec 1, 2012
2 parents 88905ba + e5715db commit cc38968
Show file tree
Hide file tree
Showing 7 changed files with 1,504 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/App_Start/ExampleLayoutsRouteConfig.cs.pp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Web.Routing;
using NavigationRoutes;

namespace $rootnamespace
namespace $rootnamespace$
{
public class ExampleLayoutsRouteConfig
{
Expand Down
313 changes: 313 additions & 0 deletions src/CodeTemplates/AddView/CSHTML/Create.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
<#@ template language="C#" HostSpecific="True" #>
<#@ output extension=".cshtml" #>
<#@ assembly name="System.ComponentModel.DataAnnotations" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="System.Data.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.ComponentModel.DataAnnotations" #>
<#@ import namespace="System.Data.Linq.Mapping" #>
<#@ import namespace="System.Data.Objects.DataClasses" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="Microsoft.VisualStudio.Web.Mvc.Scaffolding.BuiltIn" #>
<#
MvcTextTemplateHost mvcHost = MvcTemplateHost;
#>
@model <#= mvcHost.ViewDataTypeName #>
<#
// The following chained if-statement outputs the file header code and markup for a partial view, a content page, or a regular view.
if(mvcHost.IsPartialView) {
#>

<#
} else if(mvcHost.IsContentPage) {
#>

@{
ViewBag.Title = "<#= mvcHost.ViewName#>";
<#
if (!String.IsNullOrEmpty(mvcHost.MasterPageFile)) {
#>
Layout = "<#= mvcHost.MasterPageFile#>";
<#
}
#>
}

<h2><#= mvcHost.ViewName#></h2>

<#
} else {
#>

@{
Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title><#= mvcHost.ViewName #></title>
</head>
<body>
<#
PushIndent(" ");
}
#>
<#
if (mvcHost.ReferenceScriptLibraries) {
#>
<#
if (!mvcHost.IsContentPage) {
#>
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<#
}
}
#>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
<legend><#= mvcHost.ViewDataType.Name #></legend>

<#
foreach (ModelProperty property in GetModelProperties(mvcHost.ViewDataType)) {
if (!property.IsPrimaryKey && !property.IsReadOnly && property.Scaffold) {
#>
<div class="control-group">
<#
if (property.IsForeignKey) {
#>
@Html.LabelFor(model => model.<#= property.Name #>, "<#= property.AssociationName #>")
<#
} else {
#>
@Html.LabelFor(model => model.<#= property.Name #>)
<#
}
#>

<div class="controls">
<#
if (property.IsForeignKey) {
#>
@Html.DropDownList("<#= property.Name #>", String.Empty)
<#
} else {
#>
@Html.EditorFor(model => model.<#= property.Name #>)
<#
}
#>
@Html.ValidationMessageFor(model => model.<#= property.Name #>, string.Empty, new { @class="help-inline" })
</div>
</div>

<#
}
}
#>
<div class="control-group">
<div class="controls">
<input type="submit" value="Create" class="btn" /> &nbsp; @Html.ActionLink("Cancel", "Index")
</div>
</div>
</fieldset>
}

<#
if(mvcHost.IsContentPage && mvcHost.ReferenceScriptLibraries) {
#>

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")

<script>

$.validator.setDefaults({
highlight: function (element) {
$(element).closest(".control-group").addClass("error");
},
unhighlight: function (element) {
$(element).closest(".control-group").removeClass("error");
}
});
</script>
}
<#
}
#>
<#
// The following code closes the asp:Content tag used in the case of a master page and the body and html tags in the case of a regular view page
#>
<#
if(!mvcHost.IsPartialView && !mvcHost.IsContentPage) {
ClearIndent();
#>
</body>
</html>
<#
}
#>
<#+
// Describes the information about a property on the model
class ModelProperty {
public string Name { get; set; }
public string AssociationName { get; set; }
public string ValueExpression { get; set; }
public string ModelValueExpression { get; set; }
public string ItemValueExpression { get; set; }
public Type UnderlyingType { get; set; }
public bool IsPrimaryKey { get; set; }
public bool IsForeignKey { get; set; }
public bool IsReadOnly { get; set; }
public bool Scaffold { get; set; }
}

// Change this list to include any non-primitive types you think should be eligible for display/edit
static Type[] bindableNonPrimitiveTypes = new[] {
typeof(string),
typeof(decimal),
typeof(Guid),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
};

// Call this to get the list of properties in the model. Change this to modify or add your
// own default formatting for display values.
List<ModelProperty> GetModelProperties(Type type) {
List<ModelProperty> results = GetEligibleProperties(type);

foreach (ModelProperty prop in results) {
if (prop.UnderlyingType == typeof(double) || prop.UnderlyingType == typeof(decimal)) {
prop.ModelValueExpression = "String.Format(\"{0:F}\", " + prop.ModelValueExpression + ")";
}
else if (prop.UnderlyingType == typeof(DateTime)) {
prop.ModelValueExpression = "String.Format(\"{0:g}\", " + prop.ModelValueExpression + ")";
}
}

return results;
}

// Call this to determine if property has scaffolding enabled
bool Scaffold(PropertyInfo property) {
foreach (object attribute in property.GetCustomAttributes(true)) {
var scaffoldColumn = attribute as ScaffoldColumnAttribute;
if (scaffoldColumn != null && !scaffoldColumn.Scaffold) {
return false;
}
}
return true;
}

// Call this to determine if the property represents a primary key. Change the
// code to change the definition of primary key.
bool IsPrimaryKey(PropertyInfo property) {
if (string.Equals(property.Name, "id", StringComparison.OrdinalIgnoreCase)) { // EF Code First convention
return true;
}

if (string.Equals(property.Name, property.DeclaringType.Name + "id", StringComparison.OrdinalIgnoreCase)) { // EF Code First convention
return true;
}

foreach (object attribute in property.GetCustomAttributes(true)) {
if (attribute is KeyAttribute) { // WCF RIA Services and EF Code First explicit
return true;
}

var edmScalar = attribute as EdmScalarPropertyAttribute;
if (edmScalar != null && edmScalar.EntityKeyProperty) { // EF traditional
return true;
}

var column = attribute as ColumnAttribute;
if (column != null && column.IsPrimaryKey) { // LINQ to SQL
return true;
}
}

return false;
}

// This will return the primary key property name, if and only if there is exactly
// one primary key. Returns null if there is no PK, or the PK is composite.
string GetPrimaryKeyName(Type type) {
IEnumerable<string> pkNames = GetPrimaryKeyNames(type);
return pkNames.Count() == 1 ? pkNames.First() : null;
}

// This will return all the primary key names. Will return an empty list if there are none.
IEnumerable<string> GetPrimaryKeyNames(Type type) {
return GetEligibleProperties(type).Where(mp => mp.IsPrimaryKey).Select(mp => mp.Name);
}

// Call this to determine if the property represents a foreign key.
bool IsForeignKey(PropertyInfo property) {
return MvcTemplateHost.RelatedProperties.ContainsKey(property.Name);
}

// A foreign key, e.g. CategoryID, will have a value expression of Category.CategoryID
string GetValueExpressionSuffix(PropertyInfo property) {
RelatedModel propertyModel;
MvcTemplateHost.RelatedProperties.TryGetValue(property.Name, out propertyModel);

return propertyModel != null ? propertyModel.PropertyName + "." + propertyModel.DisplayPropertyName : property.Name;
}

// A foreign key, e.g. CategoryID, will have an association name of Category
string GetAssociationName(PropertyInfo property) {
RelatedModel propertyModel;
MvcTemplateHost.RelatedProperties.TryGetValue(property.Name, out propertyModel);

return propertyModel != null ? propertyModel.PropertyName : property.Name;
}

// Helper
List<ModelProperty> GetEligibleProperties(Type type) {
List<ModelProperty> results = new List<ModelProperty>();

foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
Type underlyingType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
if (prop.GetGetMethod() != null && prop.GetIndexParameters().Length == 0 && IsBindableType(underlyingType)) {
string valueExpression = GetValueExpressionSuffix(prop);

results.Add(new ModelProperty {
Name = prop.Name,
AssociationName = GetAssociationName(prop),
ValueExpression = valueExpression,
ModelValueExpression = "Model." + valueExpression,
ItemValueExpression = "item." + valueExpression,
UnderlyingType = underlyingType,
IsPrimaryKey = IsPrimaryKey(prop),
IsForeignKey = IsForeignKey(prop),
IsReadOnly = prop.GetSetMethod() == null,
Scaffold = Scaffold(prop)
});
}
}

return results;
}

// Helper
bool IsBindableType(Type type) {
return type.IsPrimitive || bindableNonPrimitiveTypes.Contains(type);
}

MvcTextTemplateHost MvcTemplateHost {
get {
return (MvcTextTemplateHost)Host;
}
}
#>
Loading

0 comments on commit cc38968

Please sign in to comment.