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 modelclass 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/editstatic 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 enabledbool 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.CategoryIDstring 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 Categorystring GetAssociationName(PropertyInfo property) { RelatedModel propertyModel; MvcTemplateHost.RelatedProperties.TryGetValue(property.Name, out propertyModel); return propertyModel != null ? propertyModel.PropertyName : property.Name;}// HelperList<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;}// Helperbool IsBindableType(Type type) { return type.IsPrimitive || bindableNonPrimitiveTypes.Contains(type);}MvcTextTemplateHost MvcTemplateHost { get { return (MvcTextTemplateHost)Host; }}#>