August 2010 - Posts

With the release of ASP.NET MVC 2 we got support for DisplayTemplates and EditorTemplates, which make it possible to create custom forms for the edit views. With this feature, we can easily take advantage of the new input fields in HTML5 such as date, color, email etc.

The first thing we need to do is to create a new ASP.NET MVC project and add a controller. After that we have to create a new folder, ~\Views\Shared\EditorTemplates. We will have out EditorTemplates here.

The controller will be very basic in this test:

using System.Web.Mvc;
 
namespace Html5Templates.Controllers
{
    public class Html5Controller : Controller
    {
        public ActionResult Create()
        {
            return View();
        }
    }
}

We will also add a very simple view:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Html5Templates.Models.Templates>" %>
 
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
    <h2>Create</h2>
    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>
 
        <fieldset>
            <legend>Fields</legend>
            
            <%: Html.EditorFor(_ => Model) %>
            
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
 
    <% } %>
</asp:Content>

As you can see in the Page directive, the model is of the type “Templates”, which we will have to create. In our model, we will take advantage of some attributes in the DataAnnotations namespace, DataType and UIHint. We will now create a property for every new type in HTML5 forms:

using System.ComponentModel.DataAnnotations;
using System;
 
namespace Html5Templates.Models
{
    public class Templates
    {
        [UIHint("Color")]
        public string Color { get; set; }
        
        [DataType(DataType.Date)]
        public DateTime Date { get; set; }
        
        [DataType(DataType.DateTime)]
        public DateTime DateTime { get; set; }
 
        [UIHint("DateTimeLocal")]
        public DateTime DateTimeLocal { get; set; }
        
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
 
        [UIHint("Month")]
        public string Month { get; set; }
 
        [UIHint("Number")]
        public string Number { get; set; }
 
        [UIHint("Range")]
        public int Range { get; set; }
        
        [DataType(DataType.PhoneNumber)]
        public string PhoneNumber { get; set; }
 
        [DataType(DataType.Time)]
        public DateTime Time { get; set; }
 
        [DataType(DataType.Url)]
        public string Url { get; set; }
 
        [UIHint("Week")]
        public string Week { get; set; }
    }
}

When use DataType when possible, and UIHint when we can´t. To be able to use this, we will have to create a new EditorTemplate for each input type. For example, we will have a DateTime.ascx file with this content:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.String>" %>
<%: Html.TextBoxFor(_ => Model, new { type = "datetime" }) %>

You can find a link to a sample project at the end of the post.

If we run the page in Opera, you will see this:

1 - Opera

Looks great! But what about Internet Explorer 8? If we open the same page in IE 8, we will get this:

2 - IE8

As you can see, the browser will still render normal textboxes if HTML5 forms isn´t supported, so it won´t break your page.

Thanks to the awesome open source library “jQuery Tools” with its Forms functionality, we can add actually get HTML5 forms support for older browsers such as Internet Explorer 8.

To load the library including jQuery, add this to your site:

<script src="http://cdn.jquerytools.org/1.2.4/full/jquery.tools.min.js"></script>
<script>
    $(document).ready(function () {
        $(":date").dateinput();
    });
</script>

This will add support for the date field, and you can also add support for Range and add validation using this tool. To know more about it and to download it, take a look at this site:

http://flowplayer.org/tools/release-notes/index.html#form

The source code for the project can be downloaded here:

http://bit.ly/bIflxw

Something that is very common on websites is small images that is used to make links and other things look better. These small images is either added as a background image for the link, or as a separate image. When you load these images, they are requested and downloaded separate. Since the browser only make a small number of requests at once, it can take a while before all images is loaded on the page. To solve this, you can use CSS Sprites.

When you use CSS Sprites, you use the same image file for all the images instead of having them in separate files. When you request the images, you only have to load one file to get them all. When you want to show a specific image, you use CSS to change focus to that image.

One example where it is used is at Google.com. They have one single image for the logo and all the small icons that are used. Because of that, you will only have to download one single image:

google

This is a common way to solve it, since it gives you better performance.

There are some cons using CSS Sprites though. It can be hard to update the images, and it can be hard to get a specific image in the sprite file.

To make it easier to do this without the need of extra work, you can use the new control created by Microsoft. This control takes all files in a directory, and mash them together in one new file. It also creates a new CSS file with classes which are used to get the right images.

To use this new control, we need to get the assemblies. The project is released under a “almost-open source license” since it´s still a beta, but will probably be open source when it´s released as RTW. The assemblies, the source code and a great documentation can be downloaded from CodePlex:

http://aspnet.codeplex.com/releases/view/50140

When we have the assemblies, we will have to create a new ASP.NET MVC project (I use a ASP.NET MVC 3 project with Razor), and add references to ImageOptimizationFramework and ImageSprite.

To be sure the images and the CSS is being generated, we must add this to web.config:

<httpModules>
  <add type="Microsoft.Samples.Web.ImageOptimizationModule" name="Microsoft.Samples.Web.ImageOptimizationModule"/>
</httpModules>

We now have everythin required. I am going to use three different images with this control (downloaded from http://www.iconaholic.com):

calendar card folder

These images will be transformed into one image, and we will use CSS Sprites to show the right one. To be able to use the Sprites control in ASP.NET, I create a new folder called App_Sprites in the root, and in that folder, I create a folder with the name “icons”. Every folder in App_Sprites creates it´s own image and css file, which means we don´t get one huge file with all images in all folders.

Normally, I would do this when displaying the images:

<ul>
    <li>
        <img src="/app_sprites/icons/calendar.png" />
        <a href="#">Link #1</a>
    </li>
    <li>
        <img src="/app_sprites/icons/card.png" />
        <a href="#">Link #2</a>
    </li>
    <li>
        <img src="/app_sprites/icons/folder.png" />
        <a href="#">Link #3</a>
    </li>
</ul>

These images will be downloaded separate, and three different requests will be made. Since I want to use Sprites I add this in the head:

@Microsoft.Samples.Web.ImageSprite.ImportStylesheet("~/App_Sprites/icons/")

And this in the body:

<ul>
    <li>
        @Microsoft.Samples.Web.ImageSprite.Image("~/App_Sprites/icons/calendar.png")
        <a href="#">Link #1</a>
    </li>
    <li>
        @Microsoft.Samples.Web.ImageSprite.Image("~/App_Sprites/icons/card.png")
        <a href="#">Link #2</a>
    </li>
    <li>
        @Microsoft.Samples.Web.ImageSprite.Image("~/App_Sprites/icons/folder.png")
        <a href="#">Link #3</a>
    </li>
</ul>

I use the Image helper here to send the path to the original image. The result for both methods is this:

rendered

The generated HTML for the Sprite control looks like this:

<link href="App_Sprites/icons/lowCompat.css" media="all" rel="stylesheet" type="text/css" />

And:

<ul>
    <li>
        <img class="icons_calendar-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
        <a href="#">Link #1</a>
    </li>
    <li>
        <img class="icons_card-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
        <a href="#">Link #2</a>
    </li>
    <li>
                <img class="icons_folder-png" src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
            <a href="#">Link #3</a>
    </li>
</ul>

We have got a css file with the name lowCompat.css. It contains the css classes which is needed for the images, and we also have a base64 encoded string for the src attribute, which shows a transparent 1x1 pixels image as a placeholder.

The difference when using sprites is that we only have a single request for all three images, instead of three separate requests. We have also got a new file, sprite0.png, which looks like this:

sprite0

These are the different images we want to show. We can now use this everywhere on the page.

But how can we modify what´s being rendered? What about if we want a jpg file with not so good quality, and with a background color?

To change the settings for the generated images, we need to create a settings.xml file, which we can add in /App_Sprites or /App_Sprites/Icons. If we add it in _App_Sprites, it will be used for all folders. To test this, we add the following xml to settings.xml:

<?xml version="1.0" encoding="utf-8" ?>
<ImageOptimizationSettings>
  <FileFormat>jpg</FileFormat>
  <Base64Encoding>false</Base64Encoding>
  <Quality>20</Quality>
  <BackgroundColor>ff00ff00</BackgroundColor>
</ImageOptimizationSettings>

We change the file format to jpg, change the quality to 20 (0-100 where 100 means best quality), and also change the background color to ff00ff00, which is green. The color is ARGB, which means 0000ff00 would make it transparent.

If we update the page, we get this:

sprite1

We got bad quality, but much smaller file size. With the standard settings it was 15 KB, but now it´s only 2 KB. The best thing to do here is to lower the quality as much as possible, without getting too bad quality.

We can also use this new functionality using ASP.NET Web Forms and ASP.NET Web Pages. If you use Web Forms, it includes a new ImageSprite control, which inherits the original Image control.

Razor, the new ASP.NET MVC View Engine from Microsoft, makes it possible to write much cleaner views for our ASP.NET MVC sites. But since the code is so clean, why not use it in other places? Gustavo Machado have written a great blog post where he uses the open APIs to write a console application that can compile Razor views without the need of IIS.

You can find the blog post about it here:
http://thegsharp.wordpress.com/2010/07/07/using-razor-from-a-console-application/

But how can we use this to be more productive? I took the sample code from his blog post, modified it a little bit to work better with my project and wrote a T4 Text Templating Engine with it.

A T4 Text Templating Engine is what is processing a T4 template. This means we can actually use Razor instead of the original syntax in our T4 templates, and generate ASP.NET MVC views and other things.

To test the T4 Text Templating Engine I created a T4 Template – Razor.tt – which looks like this:

@{
    var host = RazorHost.Host;
    var date = DateTime.Now.ToString();
}
@host.GetType().Name
 
@foreach(var name in host.Names) {
<text>Name: @name</text>
}
 
Date and time: <%="@date" %>

It is normal Razor code which is going to be used to render a view for the WebFormViewEngine. I have a RazorHost.Host here, which purpose is to give us access to other properties, like Names, which we got here. If you have created templates for ASP.NET MVC, you have probably used a host there. This is just the same.

So how can we use the engine to process the template? I have created a console application which looks like this:

using System;
using System.IO;
 
namespace RazorTemplating.App.ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
 
            string templateName = "Razor.tt";
            string[] names = new string[] { "Mikael", "Bill", "Steve" };
 
            Console.WriteLine("\n----------------------------------\nT4 Template\n----------------------------------\n");
 
            var host = new RazorTemplatingEngineHost(names);
            host.TemplateFile = templateName;
 
            string templateFile = File.ReadAllText(templateName);
 
            Console.WriteLine("\n{0}\n", templateFile);
 
            var engine = new RazorEngine();
            string generatedText = engine.ProcessTemplate(templateFile, host);
 
            Console.WriteLine("\n----------------------------------\nOutput\n----------------------------------\n");
            Console.WriteLine(generatedText);
 
            if (host.Errors != null)
            foreach (var item in host.Errors)
            {
                Console.WriteLine(item);
                Console.WriteLine();
            }
            
            Console.ReadKey();
        }
    }
}

This is the same code that we would have used with the normal T4 TextTemplatingEngineHost, but instead we use the RazorTemplatingEngineHost.

When we run the application we get this:

razor

We have actually used Razor to generate a view for .aspx files!

For more information about how to use Razor without IIS, take a look at Gustavo Machados blog post, and to download the source code for this project, go here:

http://bit.ly/a5qugN

More Posts