Month List

RecentComments

Comment RSS

ASP.NET MVC, HTML5, CSS3, JQuery templates, IEnumerable binding, knockoutjs and offline cache in a demo. A brave new world

by Ioannis 16. December 2011 18:08

In one of my previous posts(appox. a year ago) I have demonstrated how to bind to IEnumerables in ASP.NET MVC. I have shown several ways of dynamically adding/removing elements from the list through the use of Javascript and then jQuery-templates. In that postI have used the traditional ASP.NET MVC literals <% %> and of course the whole site operated in HTML4 and jQuery.

 

Today a lot of things have changed. From HTML4 we are moving to HTML5, we start using CSS3 for our styles, we use Razor for our views in ASP.NET MVC and we can take advantage of the knockoutjslibrary that has managed to bring the world of MVVM patterns closer to the world of Javascript and it did it with some impressive results. Therefore, in this post we are going to re-implement the same project adopting all those new (or not so new) great technologies.

 

We need to implement a simple site where we have a list of clients with a first and last name and we need to give to our users the ability to manage that list (insert/remove/edit elements). The initial list and all changes are communicated to the server using AJAX calls. We need to make the site friendly to computer and mobile browsers and provide the maximum possible quality in terms of user experience in both environments. Finally we need our site to know when it is online, in order to inform the user and prevent any changes.

 

(Device awareness) CSS3 Styling with media-queries and other goodies

The first change is the fact that we want to differentiate the view depending on whether we look the demo from a mobile device or from our pc. Therefore we will use CSS3 media queries to change the .css file used depending on the situation detected. In the _Layout.cshtml file we add the following:

 

<link href="@Url.Content("~/Content/mobileDevice.css")" 
      rel="stylesheet" type="text/css" media="screen and (max-width:480px)" />
<link href="@Url.Content("~/Content/computer.css")" 
      rel="stylesheet" type="text/css" media="screen and (min-width:481px)" />

 

Where the important attribute is the “media” attribute which differentiates the stylesheet based on the width of the device.

 

Another element that enables us to define specifically how things will be presented on a mobile device is the viewport meta tag as follows:

 

<meta name="viewport" content="user-scalable=no,width=device-width" />

 

This says to the mobile browser that we do not want to allow the user to be able to scale the webpage with his/her fingers and also that the viewport of the browser (the width of the mobile browser) is the same as the width of the device (as opposed to the more traditional approach where the mobile browser allows us to pan and zoom). Just adding this line makes our page look more like a “mobile app” to the device’s browser.

 

We also want to use a specific kind of fonts (Segoe) that are not the “standard” and therefore need to be embedded to the page. We can do that with CSS3 starting by finding the .ttf file we need from our computer and submitting that to the fontsquillerpage in order to get the font files in various formats recognized by most browsers and the .css definitions of our new font. We get the files, we put them in a special “Fonts” folder in our page and include a reference to the .css file holding their definitions (newFonts.css file in our project). Below is a screenshot of the website’s home page as seen in a browser more than 480px wide and in a browser less that 480px wide with the embedded fonts:


 

Capture2    Capture1


Note the new fonts achieved as previously mentioned and the rounded corners of the links in the mobile version. Those are achieved by the new elements in css as follows combined with the advanced selectors in CSS3 (in specific those ones apply the border rounded corner rules only to the first and last li elements of the unordered list (ul) that holds the navigation:

 

<nav>
    <ul>
        <li>@Html.ActionLink("jump to the demo","Simple","Clients")</li>
        <li>@Html.ActionLink("about","About","Home")</li>
    </ul>
</nav>

 

and the CSS applying the rounded borders:

 

nav ul li:first-child a 
{
    -webkit-border-top-left-radius: 8px; 
    -webkit-border-top-right-radius: 8px; 
    border-top-right-radius: 8px; 
    border-top-left-radius: 8px; 
    -moz-border-top-right-radius: 8px; 
    -moz-border-top-left-radius: 8px;
}
nav ul li:last-child a 
{
    -webkit-border-bottom-left-radius: 8px; 
    -webkit-border-bottom-right-radius: 8px; 
    border-bottom-right-radius: 8px; 
    border-bottom-left-radius: 8px; 
    -moz-border-bottom-right-radius: 8px; 
    -moz-border-bottom-left-radius: 8px;
}

 

We have also applied some shadow to the header of the mobile version with: text-shadow:1px 1px #fff; Also note the new semantic markup for HTML5. The “nav” element indicates that there is a site navigation set of links in this part of the webpage. The same difference in styling is applied also to the “demo” page as shown in the images below":


image     image


Again the two different stylesheets have definitions that achieve the differentiation.

 

(Easier unobtrusive Javascript) Data binding with knockoutjs and jQuery templates

And now we move on to the UI in terms of the client’s list. The methods shown in the previous postare all nice but they rely on a page “POST” to save the values and a page “GET” to get the values from the controller’s actions. We want to turn the page fully AJAX based and provide the maximum quality of user experience. For starters, let us imagine that we have “magically” received the list of clients and simulate this by adding in the page’s JavaScript the following list:

 

var clientsList = [{ id :1, FirstName: 'Ioannis', LastName: 'Panagopoulos' },                   
                   { id: 2, FirstName: 'Kostas', LastName: 'Papadopoulos' },                   
                   { id: 3, FirstName: 'Petros', LastName: 'Georgiadis' },                   
                   { id: 4, FirstName: 'Maria', LastName: 'Apostolou' }];

 

This is hardwired and will be later substituted with an AJAX call. To use knockoutjsand jQuery templateswe need to download and include in the _Layout.cshtml file the following:

 

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.tmpl.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-1.2.1.js")" type="text/javascript"></script>

 

The first step is to define an element in the page where the list will be displayed:

 

<section id="clientsList" data-bind='template: "clientTemplate"'></section>

 

The data-bind attribute is called a custom data attribute in HTML5. Custom data attributes are all attributes prefixed with “data-“. After the “-“ you can put whatever you like. HTML5 defines that custom data attributes are to be used to assist the programmer in any way (meaning that the word after the – may be anything) in developing the page. We know that knockoutjs searches for the data-bind custom data attribute to do its magic. In the previous declaration we tell through the custom data attribute that within the “clientsList” section (section is another HTNL5 element, it could easily be a div in this case) we want to render a jQuery template named “clientTemplate”. This template is as follows:

 

<script type="text/html" id="clientTemplate">    
    {{each(i,client) clients}}
    <div id="Clientidx">
        <input class="clientNameField" data-bind="value: FirstName" />
        <input class="clientNameField" data-bind="value: LastName"  />
        <span class="removeButton" data-bind="click: function(){ removeClient(client) }">x</span>
    </div>
    {{/each}}
</script>

 

This template says that we need to iterate a list named “clients” consisting of objects which we will call “client” (the name is just a way to have a reference to the object in each iteration). For each object, we need an input element to present the value of its FirstName property and an input element for the value of its LastName property. Finally for each object an “x” will appear that has a handler to be executed on its click event. The handler says that we need to call the function removeClient passing as argument the object of “x”’s line.

 

In another place of our code we need to be showing the total number of clients in the list:

 

<h1>clients list (<span data-bind="text: clients().length">&nbsp;</span>)</h1>

 

Where we bind to the total number of elements in the clients list. And in another place we have the following:

 

<button class="defaultButton" data-bind="click: addClient">add a client</button>

 

So the template finds the template definition, FirstName and LastName find their object within the clients list but who supplies the clients list and the removeClient and addClient methods? Well those are supplied by an object we call ViewModel and is defined as follows:

 

var ViewModel = {
            clients: ko.observableArray(clientsList),
            removeClient: function (clientToRemove) {
                ViewModel.clients.remove(clientToRemove);
            },
            addClient: function () {
                ViewModel.clients.push({id:-1, FirstName: "", LastName: ""});             
            }
        };

 

In other words, the ViewModel holds everything our UI has in terms of functionality and data. The ko.observableArray that is used makes the array be able to notify about its changes to every element attached to its properties (including the one we defined showing the total number of clients in the list). This makes it possible for the dynamic change of the number of clients reported whenever we press “add client”. This is the equivalent to INotifyPropertyChanged! The final step is to attach the ViewModel to the UI:

 

$(document).ready(function () {ko.applyBindings(ViewModel);});

 

and all the bindings find their way within the ViewModel. To complete our example we need to be able to get this information from an ASP.NET MVC Controller and submit the results to one through AJAX calls. Provided that we implement a controller to get all the clients as follows:

 

public JsonResult GetClients()
{
    List<Client> Clients = new List<Client>();
    _populateClientsList(Clients);
    return Json(Clients,JsonRequestBehavior.AllowGet);
}

 

The JavaScript is changed to get the client’s list as follows:

 

$(document).ready(function () {
    $.getJSON("@Url.Action("GetClients","Clients")", null, clientsReceived);
});
function clientsReceived(res) {
    ViewModel.clients = ko.observableArray(res);
    ko.applyBindings(ViewModel);
}

 

(there is no need anymore for the clientsList)

 

The same applies for posting the results back to the server when the user has finished editing the list we call:

 

saveClientsList: function () {
    $("#ajaxCallWait").toggle();
        $.post("@Url.Action("SaveClients","Clients")", $.toDictionary({ Clients: ko.toJS(ViewModel.clients) }), 
                function (data) {
                   $("#ajaxCallWait").toggle();
                   alert(data.message);
                })
}

 

A few things to note here. First we use the ko.toJS function to get the “raw” list without the ko.ObservableArray embelishments. Second we use a wonderful plugin from here(toDictionary) to enable the correct generation of the elements required for binding with the ASP.NET MVC controller. The controller now is as follows:

 

[HttpPost]
public JsonResult SaveClients(IEnumerable<Client> Clients)
{
    //Your saving logic here
    return Json(new { result = "success",
                      message = String.Format("{0} clients received",Clients.Count()) });
}

 

So this is it! We have managed to bind everything with considerable few lines of code compared to what we have achieved.

 

Knowing when we are offline and reacting accordingly

When the user is offline he will not be able to receive any list elements but he will be still be able to add clients and press the save button. We want to prevent this by indicating with a message that the user is offline.

 

We include a cache.manifest file in our project as follows:

 

CACHE MANIFEST
CACHE:
/demo/Clients/Simple
/demo/Scripts/knockout-1.2.1.js
/demo/Scripts/jquery-1.5.1.min.js
/demo/Scripts/jquery-toDictionary.js
/demo/Scripts/jquery.tmpl.js
/demo/Content/computer.css
/demo/Content/mobileDevice.css
/demo/Content/newFonts.css
FALLBACK:
/demo/Scripts/online.js /demo/Scripts/offline.js
NETWORK:
*
#version 1

 

This says that whatever is below the CACHE: should be stored locally, everything else should be fetched from the network (NETWORK: *) and if the browser cannot reach the online.js file (meaning that there is no internet connection), the browser should use the offline.js file that is automatically cached for offline use.

 

The online.js file has only: var online=true; inside while the offline file has only: var online=false; inside. Our webpage includes only online.js as follows (we DO NOT include the offline.js):

 

<script src="@Url.Content("~/Scripts/online.js")" type="text/javascript"></script>

 

and before performing the initial get request for the clients we add:

 

$(document).ready(function () {
        if (!online) {
                alert("Sorry but you are offline!");
                return;
        }
        $.getJSON("@Url.Action("GetClients","Clients")", null, clientsReceived);
});

 

The trick is that when online online.js will set the online variable to true while when offline the online.js file will be substituted with the offline.js file and the online variable will be false. Note that when you use the cache manifest you need to have the complete path for your files. In my case I have deployed the site in a subfolder named demo. Also note that if you have a single mistake or a missing file or a wrong path in the cache.manifest, then the whole file is not parsed. You generally need fiddlerto debug this correctly where you see what is wrong.

 

Well this is it! The complete makeover of an HTML4 page to HTML5. Hope you have enjoyed it!

 

You may download the project

Tags:

ASP.NET MVC | HTML5

A scripting language for your (web)app with DLR or Roslyn

by Ioannis 31. October 2011 13:14

Well, this subject always excited me! The fact that you can provide at your executable, a programming language (script) that the user can use to interact with your application is something amazing. In this post, we see how this is possible by using the Dynamic Language Runtime (DLR) and IronPython, and then see whether the services provided by the Roslyn project can help as achieve the same result with C#.

The whole idea behind providing a scripting language at runtime may find the following three applications:

 

  • You implement a DSL (Domain Specific Language) meaning that you want to provide to your end use an expressive tool to specify his behavior in a specific application domain. In this case you expose some parts of the information held in your application in the form of methods that the user can use in his scripts to achieve the desired result.
  • You want to be able to change a specific behavior without having to recompile the whole application or in the case where you use the same executable in multi-tenant scenarios.
  • You want your application’s users to have some fun by interacting with your application in ways you have never imagined.

 

We will use a typical ASP.NET MVC application for our tests. The web app consists of one Controller with the following View:

image

That is the user can write a script in the TextArea (in IronPython in this figure) and then click the “Execute” button. The server receives the code and executes it returning the result just below the “Execution” header. While the For-Loop is native to IronPython, the PrintOnWebPage statement has been added by our WebApp providing a way for the script writer to write HTML for the view.

 

Implementation with the DLR (Dynamic Language Runtime)

Download and compile the DLR from Codeplex. Then in your ASP.NET MVC project add the following references:

 

  • IronPython.dll
  • Microsoft.Dynamic.dll
  • Microsoft.Scripting.dll

 

You will find those in the Bin folder of the DLR project you have compiled in the previous step. The whole idea behind script execution is shown in the figure below:

image

An object is initialized called the Script Engine. This engine will receive the user’s script and handle its execution. The engine without anything else only recognizes the native “Python” commands and therefore it has very limited interaction with our WebApp. To change this we need to provide “libraries” in the Scope object used by the engine which contain methods that the user’s script can use. In the initial figure’s example the PrintOnWebPage method is such a method. To provide it to our user the following steps have been taken:

 

A BasicLib class has been implemented:

 

public class BasicLib
{
    public ViewDataDictionary ViewData { get; set; }

    public void PrintOnWebPage(string Content)
    {
        if (ViewData["Result"] == null)
            ViewData["Result"] = Content;
        else
            ViewData["Result"] = ViewData["Result"].ToString() + Content;

    }
}

Note that the class has a property ViewData. PrinOnWebPage uses this ViewData property to add the HTML content provided at its parameters. An object of this class, will be instantiated, its ViewData property will be given the value of the actual View’s ViewData and then it will be added to the engine’s scope with a special name (eg. BasicLib). From that point on, the PrintOnWebPage method is available to the script via the following notation: BasicLib.PrintOnWebPage.

So the whole picture is as follows. The script engine with its scope are implemented in a class like the following:

 

public class ScriptManager
{
    private ScriptEngine _engine;
    private ScriptScope _scope;

    public ScriptManager(){
        _engine = Python.CreateEngine();
        _scope = _engine.CreateScope();
    }
    public void AddLibrary(object Lib){
        _scope.SetVariable(Lib.GetType().Name,Lib);
    }
    public string RunCode(string script){
        try {_engine.Execute(script, _scope);}
        catch (Exception e) {return e.Message;}
        return "";
    }
}

 

The class provides a “RunCode” method where the script in the string parameter will be executed and an “AddLibraryMethod” that adds a library (like the BasicLib) to the scope. The SetVariable method that adds the library to the scope receives as second parameter the name that will be used to recognize the library in our scripts. In this case we just use the library’s type name.

 

Having this ScriptManager our Controller receives the script from the view and performs the following:

 

[HttpPost]
[ValidateInput(false)] 
public ActionResult Index(String Code)
{
    ...
    BasicLib basicLib = new BasicLib();
    basicLib.ViewData = ViewData;
    ScriptManager Mngr = new ScriptManager();
    Mngr.AddLibrary(basicLib);
    Mngr.RunCode(Code);

    return View();
}

 

And this is all it takes to provide a scripting language to your user’s. Not bad with so few lines of code don’t you agree?

 

Implementation with the Roslyn CTP

If you have found the previous approach exciting wait until you see this one. Roslyn is the project that puts the C# compiler at your service! It is an API that gives you access to its inner workings. This enables you to do lots of interesting stuff, one of which being the fact that you can give your users the option to use C# as their scripting language.

First you need to download and install the VS 2010 SDK and the Roslyn CTP. Then you need to add the following references to your ASP.NET MVC Application:

 

  • Roslyn.Compilers
  • Roslyn.Compilers.CSharp

 

The idea behind executing scripts written in C# is more or less the same as it is for the DLR. We again have a script engine:

 

Roslyn.Scripting.CSharp.ScriptEngine Engine = new Roslyn.Scripting.CSharp.ScriptEngine(new[] {
                           "System", this.GetType().Assembly.Location});

 

And we provide a session which is a single object whose public methods and properties will be accessible by our script. In our case:

 

BasicLib basicLib = new BasicLib();
basicLib.ViewData = ViewData;
Roslyn.Scripting.Session session = Roslyn.Scripting.Session.Create(basicLib);

 

The complete handling of C# scripting code in the Controller’s post action is given below:

 

Roslyn.Scripting.CSharp.ScriptEngine Engine = new Roslyn.Scripting.CSharp.ScriptEngine(new[] {
                           "System", this.GetType().Assembly.Location});
BasicLib basicLib = new BasicLib();
basicLib.ViewData = ViewData;
Roslyn.Scripting.Session session = Roslyn.Scripting.Session.Create(basicLib);
Engine.Execute(Code, session);

 

And this concludes our brief introduction into providing a scripting language to your users. The post’s project can be downloaded

Shout it

ASP.NET MVC HTML5 Before and After: The “semantic” markup of HTML5

by Ioannis 25. June 2011 08:16

Featuring in this post: The semantic markup/value of HTML5 In this post, we see how the new semantics of HTML5 alter the HTML generated for the homepage of a simple site and why this is important. Imagine you want to implement a website that every day displays a special offer of some kind. In our example, the site is featuring a special offer for a place to go for the weekend. The homepage is as follows:

 

image

 

That is, you have your header where the company’s logo and tagline are displayed (yellow area), a left area where you have some sponsored banners and exactly below an area with previous offers (orange area), your main page where you show the current offer (green area) and the footer with a small navigation menu and information on the site’s creators (white area at the bottom).

Imagine that you have a view model of the following form:

public class HomePageViewModel
{
public Offer CurrentOffer { get; set; }
public IEnumerable<Offer> PreviousOffers { get; set; }

public string BannerImage1File { get; set; }
public string BannerImage1Link { get; set; }

public string BannerImage2File { get; set; }
public string BannerImage2Link { get; set; }
}

The view model consists of the featured offer (the current one) along with a list of the previous offers. Moreover, it has the link and the image file path of the two banners on the left of the homepage. Each offer is represented by an object of the class Offer which has some basic properties related to is such as the Title, Description, Date, Initial price etc. An object of type HomePageViewModel is returned in your “Index” Action of the “Home” controller and is being rendered in the View (Index.cshtml) using Razor. Prior to HTML5 your _Layout.cshtml file defining the main areas of your website as described above would be as follows (body part):

<body>
<div id="container">
<div id="header">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<div style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</div>
</div>

<div id="mainContent">
<div id="leftArea">
@RenderSection("LeftArea",false)
</div>
<div id="mainArea">
@RenderBody()
</div>
</div>

<div id="footer">
<div id="menu">
Home | Previous Offers | About | Join us
</div>
&copy; 2011 - Ioannis Panagopoulos - HTML5 Demo Series from
<a href="http://www.progware.org">http://www.progware.org</a>
For more information call: 210666666666
</div>
</div>
</body>

(a visual representation of the site’s structure can be found at the end of the post)

That is, you have each one of the areas defined with a div element carrying a unique id so that you can apply positioning and styling rules through the Site.css file. This is the pretty straightforward approach of developing the visual structure of the homepage of the website. Similarly, the view template for the homepage (Index.cshtml - a strongly-typed View using the HomePageViewModel object returned from the Index action) is as follows (sections only displayed here):

 

@section LeftArea
{
<a href="@Model.BannerImage1Link"><img alt="Ad1" src="@String.Format("Images/{0}", Model.BannerImage1File)" width=170 height=100 /></a><br />
<a href="@Model.BannerImage2Link"><img alt="Ad2" src="@String.Format("Images/{0}", Model.BannerImage2File)" width=170 height=100 /></a><br />
<h1>Previous Deals</h1>
@foreach (var PrevDeal in Model.PreviousOffers)
{
<h2>@PrevDeal.Title</h2>
<img alt="Deal" src="@String.Format("Images/Places/{0}", PrevDeal.RelativePhotoPath)" width=100 height=60 /><br />
@:from: <p>@PrevDeal.InitialPrice.ToString("C")</p>
@:to: <p>@PrevDeal.Price.ToString("C")</p>
@Html.ActionLink("View", "Index", "Offer", new { Id = @PrevDeal.Id }, null)
}
}

<h1>Current Deal!</h1>
<h2>@Model.CurrentOffer.Title</h2>
<img alt="Deal" src="@String.Format("Images/Places/{0}",Model.CurrentOffer.RelativePhotoPath)" width=300 height=180 /><br />
<p>@Model.CurrentOffer.Description.Substring(0, 100)+"..."</p>
from: <p>@Model.CurrentOffer.InitialPrice.ToString("C")</p>
to: <p>@Model.CurrentOffer.Price.ToString("C")</p>
posted on: <p>@Model.CurrentOffer.Date.ToShortDateString() time: @Model.CurrentOffer.Date.ToShortTimeString() </p>
@Html.ActionLink("View","Index", "Offer", new { Id = @Model.CurrentOffer.Id },null)

 

As you may have already realized, all the divs and ids of the divs, are strictly for display-rendering purposes. The HTML of the page does not give any indication whatsoever on the “meaning” of each area of the page. Any crawler or automated process visiting our site will not be able to deduce anything for the content. HTML5 introduces tag elements to use in the place of the divs that have more “semantic” meaning. Therefore the main objective of this post is the replacement of the divs of the page with HTML5 tag elements that give a meaning to the content apart from the strict visual arrangement governing the use of divs in HTML4.

 

So, the first key element of HTML5 is learning this new semantic markup and using it appropriately. And this is exactly what we will do. Actually, the first thing is changing the header of the site enclosed in a div with id=header to the following:

<header class="mainSiteHeader">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<hgroup style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</hgroup>
</header>

As you see we define in markup that this is the header of the site (tag header) and within we define the logo and tagline as a group accompanying the header (tag hgroup). In other words we have given semantic information for the header content of our website.

Considering now the main content, we see that there are three semantic sections. One that contains the main offer, one that contains previous offers and one that comprises of the banners. In HTML5, semantic areas are defined with “section” tags. Individual pieces of information within the same section (such as each offer in the previous offers section) are defined with “article” tags. Each article can also have a header and sections. Finally since the area of the two banners is not directly related to the content of the website in HTML5 it should be enclosed within an “aside” tag. The footer of the homepage in HTML5 has its own “footer” tag and the navigation menu is enclosed in “nav” tags. Finally, note that some specific information such as the date of the offer are semantically defined  with specific HTML5 tags such as the “time datetime” tag.

 

The new _Layout.cshtml, using HTML5 markup is as follows:

<body>
<div id="container">
<header class="mainSiteHeader">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<hgroup style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</hgroup>
</header>

<section class="mainContent">
<section class="leftArea">
@RenderSection("LeftArea",false)
</section>
<section class="mainArea">
@RenderBody()
</section>
</section>

<footer>
<nav>
Home | Previous Offers | About | Join us
</nav>
&copy; 2011 - Ioannis Panagopoulos - HTML5 Demo Series from <a href="http://www.progware.org">http://www.progware.org</a>
For more information call: 210666666666
</footer>
</div>
</body>

And the Index.cshtml which works with the same controller but with HTML5 markup is as follows:

@section LeftArea
{
<aside>
<a href="@Model.BannerImage1Link"><img src="@String.Format("Images/{0}", Model.BannerImage1File)" width=170 height=100 /></a><br />
<a href="@Model.BannerImage2Link"><img src="@String.Format("Images/{0}", Model.BannerImage2File)" width=170 height=100 /></a><br />
</aside>
<section>
<header class="sectionHeader"><h1>Previous Deals</h1></header>
@foreach (var PrevDeal in Model.PreviousOffers)
{
<article>
<header class="sectionHeader">
<h2>@PrevDeal.Title</h2>
</header>
<img src="@String.Format("Images/Places/{0}", PrevDeal.RelativePhotoPath)" width=100 height=60 />
<br />
from: <p>@PrevDeal.InitialPrice.ToString("C")</p>
to: <p>@PrevDeal.Price.ToString("C")</p>
@Html.ActionLink("View", "Index", "Offer", new { Id = @PrevDeal.Id }, null)
</article>
}
</section>
}
<header><h1>Current Deal!</h1></header>
<article>
<header class="sectionHeader">
<h2>@Model.CurrentOffer.Title</h2>
</header>
<img alt="Deal Image" src="@String.Format("Images/Places/{0}",Model.CurrentOffer.RelativePhotoPath)" width=300 height=180 /><br />
<p>@Model.CurrentOffer.Description.Substring(0, 100)+"..."</p>
from: <p>@Model.CurrentOffer.InitialPrice.ToString("C")</p>
to: <p>@Model.CurrentOffer.Price.ToString("C")</p>
posted on: <time datetime=@Model.CurrentOffer.Date.ToString("yyyy-MM-dd") pubdate>@Model.CurrentOffer.Date.ToShortDateString() </time> time:<time>@Model.CurrentOffer.Date.ToShortTimeString()</time> </p>
@Html.ActionLink("View","Index", "Offer", new { Id = @Model.CurrentOffer.Id },null)
</article>

Graphically in HTML4 the structure of the site was defined as follows:

 

image

 

And after with HTML5:

image

The two sites with the help of styling with .css are visually the same but the second in HTML5 carries a lot more semantic information.

 

The first step for making  an ASP.NET MVC site using HTML5 is searching for the new HTML5 semantic tags and introducing them to your Views where appropriate. You will find a lot of sites in the web describing which are those new tags and where it is appropriate to use them in your page.

Shout it

Tags:

ASP.NET MVC | HTML5

Powered by BlogEngine.NET 2.0.0.36

ITPRO DevConnections 2011

Creative Commons License

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award


Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add