Thursday 27 December 2012

Friendly URLs in ASP.NET 4.5 Web Forms


In the recent update of ASP.NET, Microsoft released support for Friendly URLs in ASP.NET Web Forms. It is based on the concept of Routing. But, we don’t need to deal with route table to manually add the routes to be mapped. URLs of the site will be automatically made friendly after invoking when the application starts.

To get started using the Friendly URLs, we need to install the NuGet Package Microsoft.AspNet.FriendlyUrls. The package is not stable yet, so we need to search it in pre-release packages.
























This package adds following files to the web application:
  • Microsoft.AspNet.FriendlyUrls assembly – Contains required set of classes and interfaces
  • Site.Mobile.Master – Master page for mobile devices
  • ViewSwitcher.ascx – A user control that can be used to switch views from desktop view to Mobile view and vice versa
Once Visual Studio finishes installing the NuGet package, a read me file will be popped up. This file contains the steps to be followed to enable Friendly URLs on your site. All you have to do is, call the EnableFriendlyUrls extension method of RouteTable in RegisterRoutes method of RouteConfig class. This method is defined in Microsoft.AspNet.FriendlyUrls namespace.
routes.EnableFriendlyUrls();

And make sure that the RegisterRoutes method is called in Application_Start event of Global.asax:
RouteConfig.RegisterRoutes(RouteTable.Routes);

Now run the application and check URL on the address bar of your browser.































And the magic happened! As we see here, the URL doesn’t contain extension of the page.

Note: You don’t have to install NuGet package and apply the above settings if you have installed ASP.NET and Web Tools 2012.2. These changes are built into the ASP.NET web application template in the new template.

If you are using default ASP.NET 4.5 Web application template, you can invoke the Login (which resides in Account folder) page using:

http://mysite/Account/Login

You can link any page that resides in a folder using the same convention.

Hyperlinks to the pages can be replaced with the friendly convention.

<a id="loginLink" runat="server" href="~/Account/Login">Log in</a>

Data can be passed to a page using segments. Href method of FriendlyUrl class can be used for this purpose:
<a href="<%: FriendlyUrl.Href("~/BookDetails","AspNet") %>">ASP.NET</a>

This hyperlink forms the following URL:

http://mysite/BookDetails/AspNet

This data can be displayed on the page in any mark-up element. To display the topic of book sent through the above URL in a span element, we have to get the value from the segment as shown below:

<span><%: Request.GetFriendlyUrlSegments()[0].ToString() %></span>

Also, this value can be passed as a parameter to a method used for Model Binding as shown below:
public IQueryable<Customer> GetBooks([FriendlyUrlSegments]string topic)
{
    var selectedBooks = context.Books.Where(c => c.BookName.Contains(topic));
    return selectedBooks;
}

Remember that, if you are navigating to the page ListBooks.aspx with following URL,

http://mysite/ListBooks/Book/AspNet

then the parameter marked with FriendlyUrlSegments will hold the value Book/AspNet. So, this should be handled with care.

You can also create mobile friendly pages and even pages specific to certain type of device using Friendly URLs. Check Scott Hanselman’s great post on Introducing ASP.NET FriendlyUrls - cleaner URLs, easier Routing, and Mobile Views for ASP.NET Web Forms to learn more
about Friendly URLs and mobile pages.

Happy coding!

Sunday 23 December 2012

Basics: Routing in ASP.NET Web forms

Many of our friends think that Routing is a feature of ASP.NET MVC and cannot be used with ASP.NET web forms. But the fact is that, Routing is a part of ASP.NET core. It is added to the core in ASP.NET 3.5 SP1. In ASP.NET 4.0, routing is made more developer friendly. So, anything that falls under ASP.NET family can use routing.

Using routing in ASP.NET Web Forms, we can generate URLs that don’t correspond to the physical file. Server will reach the physical file to be rendered using the parameters passed with route URL. Defining pretty URLs makes the pages easily searchable by the search engines.


Using Routing with ASP.NET Web forms

To use routing in an ASP.NET Web Forms application, we have to register the routes when the application starts. This means, the routes have to be registered to System.Web.Routing.RouteTable.Routes collection in the Application_Start event of Global.asax. There are several overloads of MapPageRoute method in RouteCollection, but we usually use the following overload:

public Route MapPageRoute(string routeName, string routeUrl, string physicalFile);

Following is an example of registering a simple route which can be used to hide extension of aspx forms:
routes.MapPageRoute("basic-route", "{page}", "~/{page}.aspx");

Once this route is registered, we can safely replace the URL

http://mysite/Page.aspx

with the following URL:

http://mysite/Page

But, this route doesn’t work if we have some pages in a folder. For example, suppose the web application has a folder named Account and this folder has the forms Login.aspx and Register.aspx, we would expect the page Login.aspx to respond when we hit the URL

http://mysite/Account/Login

But, it doesn’t work as we didn’t define a route for it. We have to add the following route to the route table to make this URL work as we expected:

routes.MapPageRoute("folder-route", "{folder}/{Page}", "~/{folder}/{Page}.aspx");

Now a question arises that, do we need to hard-code the routed URLs in hyperlinks? The answer is no. We can refer to the registered routes by their names and provide values to the route parameters to get the URL generated for us. Following HyperLink generates the URL to Login page we discussed above:
<asp:HyperLink ID="hlLogin" Text="Login" NavigateUrl="<%$ RouteUrl:RouteName=folder-route,folder=Account,page=Login %>" runat="server" />

We can also pass input parameters to a page using route parameters. For example, if we have a page named Book.aspx that lists a set of books on a particular topic and the topic is passed using QueryString to the page as shown below,

http://mysite/Book.aspx?topic=JavaScript

We can avoid usage of QueryString and make the URL prettier as shown below

http://mysite/Book/JavaScript

To achieve this we have to add the following route to the RouteCollection:

routes.MapPageRoute("book", "Book/{BookName}", "~/Book.aspx");

The route value passed to this page can be accessed from both aspx mark-up and from code behind file. Following mark-up demonstrates displaying mark-up value on a label:
<asp:Label ID="lblBookName" Text="<%$ RouteValue:BookName %>" runat="server" />

Following code snippet shows accessing the route value from code behind file:
var bookName = Page.RouteData.Values["BookName"].ToString();

This value can be used to query database or any other type of further processing.

Further learning:
http://msdn.microsoft.com/en-us/magazine/dd347546.aspx 
http://weblogs.asp.net/scottgu/archive/2009/10/13/url-routing-with-asp-net-4-web-forms-vs-2010-and-net-4-0-series.aspx
http://haacked.com/archive/2008/03/11/using-routing-with-webforms.aspx


Happy coding!

Monday 17 December 2012

A Real-time chart using SignalR and Flot

I have discussed about SignalR in some of my earlier posts. I lately found Flot, a jQuery plugin that makes it very easy to create charts on HTML pages without doing a lot of work. Since both SignalR and Flot are based on jQuery at the client end, they work well together. In this post, we will see how to create a chart showing price of a stock that changes every second.


On Server Side

As we are using SignalR, we need a hub class. To represent a stock value object, we need a class with a couple of properties in it. Following is the StockValue class that we will use to send data to client:
public class StockValue
{
    public int Time { get; set; }
    public double Price { get; set; }
}

For this solution, I have used Visual Studio 2012 and SignalR 1.0 RC. There are a number of changes made to SignalR in this version. Following are the changes that we will be using in this post:

Calling a client function from Server: Clients.All.functionName(data);
Calling a server function from client: proxy.server.functionName(data);
Defining a JavaScript client function: proxy.client.functionName(data);

We need a StockMarket class that does the following tasks:

  • Initializes stock values
  • Updates the stock values after certain period
  • Sends the updated data to all clients
Code of this class is as follows:
public class StockMarket
    {
        public static readonly Lazy<StockMarket> market = new Lazy<StockMarket>(() => new StockMarket());
        static List<StockValue> stockValues;
        Timer _timer;
 
        public static StockMarket Instance
        {
            get
            {
                return market.Value;
            }
        }
 
        public void InitializeStockvalues()
        {
            stockValues = new List<StockValue>()
                {
                    new StockValue(){Time=10,Price=10.4},
                    new StockValue(){Time=20,Price=10.5},
                    new StockValue(){Time=30,Price=10.2},
                    new StockValue(){Time=40,Price=10.5}
                };
            GetClients().All.drawChart(stockValues);
        }
 
        public void GenerateNextStockValue(object state)
        {
            Random randomGenerator = new Random();
            int changeInCost = randomGenerator.Next(100, 110);
 
            var lastStockValue = stockValues[stockValues.Count - 1];
            var nextStockValue = new StockValue();
            nextStockValue.Time = lastStockValue.Time + 10;
            nextStockValue.Price = changeInCost / 10.0;
 
            stockValues.Add(nextStockValue);
 
            GetClients().All.drawChart(stockValues);
        }
 
        public void PublishPrices()
        {
            if (stockValues == null)
            {
                InitializeStockvalues();
            }
            else
            {
                GenerateNextStockValue(null);
            }
            _timer = new Timer(GenerateNextStockValue, null, 1000, 1000);
        }
 
        public IHubConnectionContext GetClients()
        {
            return GlobalHost.ConnectionManager.GetHubContext<StockValuesHub>().Clients;
        }
    }

Finally, we need a Hub class to kick off the process on the server. The hub class is very simple as we have assigned most of the responsibilities to StockMarket class.
public class StockValuesHub : Hub
    {
        public void GetStockValues()
        {
            StockMarket.Instance.PublishPrices();
        }
    }


On Client Side

On the client HTML page, we need to add references to following JavaScript libraries:
  • jQuery library
  • SignalR jQuery library
  • Flot jQuery library
  • Reference to SignalR’s dynamic proxy library

Flot requires a div element to render chart. So, let’s add the following div element to our page:

<div id="canvasChart" style="height: 300px; width: 300px;"></div>

On page load, we need to invoke the GetStockValues() function to start the process.
var hub = $.connection.stockValuesHub;
var dataToChart = [];
 
$.connection.hub.start()
    .done(function () {
        hub.server.getStockValues();
     });

We need to define the drawChart function on client to consume the data sent by server and render the chart. It involves a bit of logic as Flot doesn’t operate on JSON data. We need to convert our data to a form that Flot understands.

Flot accepts values in the form of an array. In the API documentation of Flot, it is specified that, Flot accepts values in the form of an array of a series. Following is an example of the data to be passed into Flot (copied from API documentation):

[ [1, 3], [2, 14.01], [3.5, 3.14] ]

Since SignalR sends JSON data, we need to convert the data to above format before we render the chart. Each object in the collection has to be converted to an array with two elements. Following function does this job for us:

function convertToArray(data) {
    var dataArray = [];
    dataArray.push(data.Time);
    dataArray.push(data.Price);
    return dataArray;
}

Finally, let’s add the drawChart function to client proxy. Implementation of this function is pretty straight forward.
hub.client.drawChart = function (data) {
    if (dataToChart.length == 0) {
        $.each(data, function () {
            dataToChart.push(convertToArray(this));
        });
    }
    else {
        dataToChart.push(convertToArray(data[data.length - 1]));
    }
    $.plot($("#canvasChart"), [dataToChart]);
};


You can download the complete code here.

Happy Coding!

Tuesday 4 December 2012

Bouncing a ball on HTML5 Canvas

Canvas provides a nice interface to draw anything on it. Canvas doesn’t support animations by default, but as we write JavaScript to do anything on canvas, we can create animations too. Let’s see how we can create a basic animation to bounce a ball inside boundaries of canvas.

Let’s create a ball on the canvas. We can do it by drawing a circle and filling some color inside it. Following code shows how to do it:
function draw(){
    context.beginPath();
    context.fillStyle="green";
    context.arc(10,10,20,0,Math.PI*2,true);
    context.closePath();
    context.fill();
}
Since we have to move the ball on canvas, we may say that we have to draw the ball over and over at different positions. Before drawing the ball at next position, we must clear the ball already drawn. And this logic has to be repeated after certain time.

Let’s modify the draw function defined above to draw the ball at different positions. The logic involves:
  • Clearing the canvas
  • Drawing the ball at a position
  • Calculating next position to draw the ball

Below code implements this logic:
function draw()
{
    context.clearRect(0,0,300,300);
    context.beginPath();
    context.fillStyle="green";
    context.arc(x,y,20,0,Math.PI*2,true);
    context.closePath();
    context.fill();  
    
    if(x+xShift > width || x+xShift < 0)
        xShift=0-xShift;
    if(y+yShift > height || y+yShift < 0)
        yShift=0-yShift;
    
    x+=xShift;
    y+=yShift;
}
On page load, we can use setInterval function to call draw function after certain interval.
setInterval(draw,20);



Happy coding!