Building a Template Engine - Part 2

In part 1, I start to build a very simple template engine using regular expressions. In this article I want to add another trick which can be helpful for creating dynamic content programmatically - the use of the using System.CodeDom.Compiler namespace, and run-time compilation of code. In a project I did a couple of years ago, we wanted the ability for users to store parameters based on some 'expression'. This expression would then be parsed to produce text on a report. Typically, the expression would be to generate today's date or some date based on today (eg end of last month, start of last month etc). Rather than building a pre-defined list of suitable placeholders for these variables, I decided to use the power of the System.CodeDom.Compiler classes, so that the user could type in a .NET 'snippet' expression, and this expression would be compiled during execution of the program, and then evaluated, to return the required text value. Therefore, users would be able to enter in strings like: ...click here to read more
David Barone, 5 August, 2009, 9:21 pm
Last Modified: 5 August, 2009, 9:21 pm

Building a Template Engine - Part 1

Being able to master templates is a useful skill for a programmer, not only are templates a major part of many languages (XSLT, ASP.NET to name but two), but templates are also useful for code autogeneration and quick manipulation of code and data. Although there are templating tools available, I thought I'd investigate how simple it was to create a basic templating engine. One side goal of this is that I would like to eventually create a templating engine that I could use in my MVC framework instead of NVelocity (just for the challenge!). To begin with, I decided to see how far I could get using the regular expression library (RegEx) in .NET: ...click here to read more
David Barone, 5 August, 2009, 9:05 pm
Last Modified: 25 February, 2010, 8:00 pm

Game of Life in Silverlight

Every now and then you start on something that is so addictive that you just can't leave the code. I've experienced that today, and it was meant to be a day off work! ...click here to read more
David Barone, 23 July, 2009, 8:31 pm
Last Modified: 23 July, 2009, 8:31 pm

ASP.NET MVC - Adding Grids Using Flexigrid

I've decided the best way to learn ASP.NET MVC is to start building a 'sample' web site. I've decided to build a web app based on the Northwind database. I'm looking at building a toolkit of widgets and other libraries that I can then call on when constructing a real web application. I figured a good widget to start with was a grid. Grids are a really important aspect to most web application sites. Most CRUD operations originate from a web page containing some kind of grid or table to show the records. Grids are so important that ASP.NET provides a number of ways to create a grid (eg ListView, GridView, Repeater etc etc). I also want to use JQuery. JQuery seems to be the frontrunner when it comes to Javascript Libraries, and ASP.NET MVC has good integration with it. Looking around on Google, it appears there are a few 'Grid' plugins. I went for flexigrid on account of it being very light-weight (just one moderately small js file) whilst still looking professional. There are a couple of good articles that I also used to get started with flexigrids: If you look at the attached code, you'll see I've not added that much to my example. I have introduced an extension method on the HtmlHelper class to add a fully-formed Flexigrid. I've done this so that you get nice .NET intellisense when entering all the Flexigrid parameters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Text; using System.Web.Mvc; using System.Reflection; namespace mvc.Helpers { [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)] public class TextDelimiterAttribute : Attribute { public string Delimiter; public TextDelimiterAttribute(string delimiter) { Delimiter = delimiter; } } public enum Alignment { Left, Middle, Right } public class FlexigridButton { public FlexigridButton(string name, string bClass, string onPress) { Name = name; BClass = bClass; OnPress = onPress; } [TextDelimiter("\"")] public string Name {get; set;} [TextDelimiter("\"")] public string BClass { get; set; } public string OnPress { get; set; } } public class FlexiGridColumn { public FlexiGridColumn(string display, string name, int width, bool sortable, Alignment alignment) { Display = display; Name = name; Width = width; Sortable = sortable; Align = alignment; } public FlexiGridColumn(string display, string name, int width, bool sortable, Alignment align, string process) { Display = display; Name = name; Width = width; Sortable = sortable; Align = align; Process = process; } [TextDelimiter("\"")] public string Display {get; set;} [TextDelimiter("\"")] public string Name { get; set; } public int Width {get; set;} public bool Sortable {get; set;} [TextDelimiter("'")] public Alignment Align {get; set;} public string Process { get; set; } } public class FlexiGridParameters { public string TableContainerName {get; set;} public string Title {get; set;} public string Url {get; set;} public int Width {get; set;} public int Height {get; set;} public FlexiGridColumn[] Columns {get; set;} public FlexigridButton[] Buttons {get; set;} public FlexiGridSearchItem[] SearchItems {get; set;} public bool AllowPaging {get; set;} public int PageSize {get; set;} public bool ShowTableToggleButton { get; set; } } public class FlexiGridSearchItem { public FlexiGridSearchItem(string display, string name) { Display = display; Name = name; } [TextDelimiter("\"")] public string Display { get; set; } [TextDelimiter("\"")] public string Name { get; set; } } public static class ExtensionMethods { private static string ToJson(this object obj) { StringBuilder sb = new StringBuilder(); sb.Append("{"); Type objType = obj.GetType(); string delim = string.Empty; foreach (PropertyInfo pi in objType.GetProperties()) { string delimiter = string.Empty; if (Attribute.IsDefined(pi, typeof(TextDelimiterAttribute))) { delimiter = ((TextDelimiterAttribute)pi.GetCustomAttributes(typeof(TextDelimiterAttribute), false)[0]).Delimiter; } object value = pi.GetValue(obj, null); if (value !=null && value.GetType() == typeof(bool)) value = value.ToString().ToLower(); if (value != null) { sb.Append(delim + pi.Name.ToLower() + ":" + delimiter + value.ToString() + delimiter); delim = ","; } } sb.Append("}\n"); return sb.ToString(); } public static string FlexiGrid(this HtmlHelper helper,FlexiGridParameters parameters) { StringBuilder sb = new StringBuilder(); sb.Append("<table id=\"" + parameters.TableContainerName + "\" style=\"display:none\"></table>\n"); sb.Append("<script type=\"text/javascript\">\n"); sb.Append("$(document).ready(function(){\n"); sb.Append(" $(\"#" + parameters.TableContainerName + "\").flexigrid\n"); sb.Append(" (\n"); sb.Append(" {\n"); sb.Append(" url: '" + parameters.Url + "',\n"); sb.Append(" dataType: 'json',\n"); sb.Append(" colModel : [\n"); string delim = ""; foreach (FlexiGridColumn c in parameters.Columns) { sb.Append(delim + c.ToJson()); delim=","; } sb.Append(" ],\n"); sb.Append(" buttons : [\n"); delim = ""; foreach (FlexigridButton b in parameters.Buttons) { sb.Append(delim + b.ToJson()); //sb.Append(delim + " {name: '" + b.Name + "', bclass: '" + b.BClass + "', onpress : " + b.OnClick + "}\n"); delim=","; } sb.Append(" ],\n"); sb.Append(" searchitems : [\n"); delim = ""; foreach (FlexiGridSearchItem s in parameters.SearchItems) { sb.Append(delim + s.ToJson()); delim=","; } sb.Append(" ],\n"); sb.Append(" sortname: \"id\",\n"); sb.Append(" sortorder: \"asc\",\n"); sb.Append(" usepager: true,\n"); sb.Append(" title: '" + parameters.Title + "',\n"); /* sb.Append(" useRp: true,\n"); sb.Append(" rp: 10,\n"); sb.Append(" showTableToggleBtn: true,\n"); */ sb.Append(" width: 700,\n"); sb.Append(" height: 255\n"); sb.Append(" }\n"); sb.Append(" ); }) \n"); sb.Append("</script>\n"); return sb.ToString(); } } }
The amount of code written for the flexigrid using the extension method is about the same is if you just wrote the JQuery code yourself. Not sure if this is a worthwhile step. Time will tell. The actual markup code using the HtmlHelper is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<mvc.Models.Customer>>" %> <%@ Import Namespace="mvc.Helpers" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Customers </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <script type="text/javascript" language="javascript"> function makeLink(celDiv) { $(celDiv).html('<a href="Customer/Edit?customerId=' + $(celDiv).html() + '">' + $(celDiv).html() + '</a>'); } function test(com, grid) { if (com == 'Delete') { var itemsSelected = $('.trSelected', grid).length; if (itemsSelected > 0) { if (confirm('Delete ' + itemsSelected + ' items?')) { var items = $('.trSelected', grid); var itemList = ''; for (i = 0; i < items.length; i++) { itemList += items[i].id.substr(3) + ","; } $.ajax({ type: "POST", dataType: "json", url: "JSon/DeleteCustomer", data: "customerIds=" + itemList, success: function(data) { alert(data.Message); $("#tableCustomer").flexReload(); } }); } } else { return false; } } else if (com == 'Add') document.location = "/Customer/Create"; } </script> <div> <%=Html.FlexiGrid( new FlexiGridParameters { TableContainerName = "tableCustomer", Title = "Customers", Url = "JSon/GetCustomers", Width = 300, Height = 400, Columns = new FlexiGridColumn[] { new FlexiGridColumn("Customer Id","CustomerId",50,true,Alignment.Left, "makeLink"), new FlexiGridColumn("Company Name","CompanyName",100,true,Alignment.Left), new FlexiGridColumn("Contact Name","ContactName",100,true,Alignment.Left), new FlexiGridColumn("Address","Address",100,true,Alignment.Left), new FlexiGridColumn("City","City",100,true,Alignment.Left), new FlexiGridColumn("Contact Title","ContactTitle",100,true,Alignment.Left), new FlexiGridColumn("Country","Country",100,true,Alignment.Left), new FlexiGridColumn("Fax","Fax",100,true,Alignment.Left), new FlexiGridColumn("Phone","Phone",100,true,Alignment.Left), new FlexiGridColumn("Postal Code","PostalCode",100,true,Alignment.Left), new FlexiGridColumn("Region","Region",100,true,Alignment.Left) }, Buttons = new FlexigridButton[] { new FlexigridButton("Add","add","test"), new FlexigridButton("Delete","delete","test") }, SearchItems = new FlexiGridSearchItem[] { new FlexiGridSearchItem("Customer Id","CustomerId"), new FlexiGridSearchItem("Contact Name","ContactName") }, AllowPaging = true, PageSize = 10, ShowTableToggleButton = true } ) %> </div> </asp:Content>
I've added a JsonController class so I can start learning Json and play with AJAX calls. You can see how I get the customer list and delete customers through Ajax calls. Again, JQuery and ASP.NET MVC handle this seamlessly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Ajax; using Csa.Data.Entities; using mvc.Models; namespace mvc.Controllers { public class JsonController : Controller { NorthwindEntities context = new NorthwindEntities(); public ActionResult Index() { return View(); } public JsonResult DeleteCustomer(string customerIds) { try { string[] customerIdArray = customerIds.Split(','); int processed = 0; foreach (string customerId in customerIdArray) { if (!string.IsNullOrEmpty(customerId)) { var customer = context.Customers.First(c => c.CustomerID == customerId); context.DeleteObject(customer); context.SaveChanges(); processed++; } } return Json(new { Message = "Delete successful: " + processed + " records processed." }); } catch (System.Data.UpdateException ex) { return Json(new { Message = "Error: " + ex.InnerException.Message }); } catch { return Json(new { Message = "An error has occured!" }); } } public JsonResult GetCustomers() { NorthwindEntities context = new NorthwindEntities(); var customers = context.Customers.Take(10).Select(c => new { CustomerId = c.CustomerID, CompanyName = c.CompanyName, ContactName = c.ContactName, Address = c.Address, City = c.City, ContactTitle = c.ContactTitle, Country = c.Country, Fax = c.Fax, Phone = c.Phone, PostalCode = c.PostalCode, Region = c.Region }).ToList(); FlexigridDataSet dataset = new FlexigridDataSet("CustomerId", customers); JsonResult result = this.Json(dataset); return result; } } }
There's not much else to add at this point. The demo displays the customers from the Northwind database. You can add, edit and delete customers. The edit link is done using the 'process' parameter on the column model. The edit/create customer page is a bit bland (it's the auto-generated one). I think I might try and use an AJAX popup window for this as well (maybe the next article to write!). ...click here to read more
David Barone, 4 April, 2009, 11:17 pm
Last Modified: 4 April, 2009, 11:17 pm

The Future's Bright. The Future's MVC

With the recent RTM of the ASP.NET MVC framework, a new era has dawned on the web development landscape. This is probably as big a shift in development frameworks as ASP.NET was to classic ASP. It will need a similar level of adjustment as well, and those who don't embrace it may miss out. ...click here to read more
David Barone, 3 April, 2009, 9:40 pm
Last Modified: 3 April, 2009, 9:40 pm

Messaging Example

I've been going through the book 'Enterprise Integration Patterns' (The Addison-Wesley 'Martin Fowler' Signature Series). There is a good worked example in the book using Asynchronous messaging with C# and MSMQ. The accompanying web site page has all the information: http://www.enterpriseintegrationpatterns.com/ComposedMessagingMSMQ.html Unfortunately neither the web site nor book give the complete working solution. Although, as the book states, patterns should not be copied blindly (the circumstances that patterns are used in are not always exactly the same, and patterns can be 'tweaked' to solve individual problems), I still think that having complete working examples is always a good starting point for developers to learn new things. As a result, I've attempted to fill in the blanks, and the result is this project. ...click here to read more
David Barone, 27 March, 2008, 1:04 pm
Last Modified: 27 March, 2008, 1:04 pm

Numbers To Words

In my type of work, we occasionally need to be able convert a number string to an equivalent 'number as words' string. For example this is routinely required for check production. Due to the idioms of the english language, this is not quite as straight forward as it seems. For example numbers below twenty are treated differently from numbers 20 to 99. In addition, the use of the word 'and' can cause some problems. I've prepared a simple demo that illustrates a sample algorithm to convert numbers to their 'word' representations. ...click here to read more
David Barone, 4 March, 2008, 10:44 pm
Last Modified: 4 March, 2008, 10:44 pm

MVC Framework

Download the source code here. ...click here to read more
David Barone, 24 February, 2008, 7:32 pm
Last Modified: 24 February, 2008, 7:32 pm