I have spent the last week or so using kendo with a MVC3 project. While learning Kendo I have also been playing around with T4 templating.
I have written a template that uses the default List template but I have modified it to use the KendoUI grid.
I thought I would share it with others as hopefully it will some someone else time. It could do with a few tweaks so I might add it to github soon.
I have written a template that uses the default List template but I have modified it to use the KendoUI grid.
I thought I would share it with others as hopefully it will some someone else time. It could do with a few tweaks so I might add it to github soon.
<#@ 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 IEnumerable<#=
"<"
+ 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>
<title><#= mvcHost.ViewName #></title>
</head>
<body>
<#
PushIndent(
" "
);
}
#>
<p>
@Html.ActionLink(
"Create New"
,
"Create"
)
</p>
<table id=
"MYGRID"
>
<thead>
<tr>
<#
List<ModelProperty> properties = GetModelProperties(mvcHost.ViewDataType);
foreach
(ModelProperty property
in
properties) {
if
(!property.IsPrimaryKey && property.Scaffold) {
#>
<th data-field=
"<#= property.AssociationName #>"
>
<#= property.AssociationName #>
</th>
<#
}
}
#>
<th></th>
</tr>
</thead>
<tbody>
@
foreach
(var item
in
Model) {
<tr>
<#
foreach
(ModelProperty property
in
properties) {
if
(!property.IsPrimaryKey && property.Scaffold) {
#>
<td>
@Html.DisplayFor(modelItem => <#= property.ItemValueExpression #>)
</td>
<#
}
}
string
pkName = GetPrimaryKeyName(mvcHost.ViewDataType);
if
(pkName !=
null
) {
#>
<td>
@Html.ActionLink(
"Edit"
,
"Edit"
,
new
{ id=item.<#= pkName #> }) |
@Html.ActionLink(
"Details"
,
"Details"
,
new
{ id=item.<#= pkName #> }) |
@Html.ActionLink(
"Delete"
,
"Delete"
,
new
{ id=item.<#= pkName #> })
</td>
<#
}
else
{
#>
<td>
@Html.ActionLink(
"Edit"
,
"Edit"
,
new
{
/* id=item.PrimaryKey */
}) |
@Html.ActionLink(
"Details"
,
"Details"
,
new
{
/* id=item.PrimaryKey */
}) |
@Html.ActionLink(
"Delete"
,
"Delete"
,
new
{
/* id=item.PrimaryKey */
})
</td>
<#
}
#>
</tr>
}
</tbody>
</table>
<script>
$(document).ready(function () {
setTimeout(function () {
$(
"#MYGRID"
).kendoGrid({
dataSource: {
type:
"json"
,
transport: {
read:
"/GetJsonData"
},
schema: {
model: {
fields: {
item1:{type:
"string"
},
item2:{type:
"string"
},
item3:{type:
"string"
}
}
}
},
pageSize: 10
},
columns: [{
field:
"Id"
,
filterable:
false
},
"Column2"
,
"Column3"
]
});
});
});
</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.IsContentPage) {
#>
<#
}
else
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;
}
}
#>