Click here to Skip to main content
12,451,703 members (62,623 online)
Click here to Skip to main content
Add your own
alternative version

Stats

33.1K views
772 downloads
33 bookmarked
Posted

ASP.NET 5 Web API RESTful CRUDs and Windows 10 native application

, 26 Aug 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
In this article I will explain the steps to create a full featured standalone ASP.NET 5's Web API that can be used by Windows 10's native Universal Windows Runtime application.

Introduction and Background

I have been developing Windows 10 applications ever since I became a Windows 10 Insider but ASP.NET was left behind for a very long time, likely to be 2 years. ASP.NET has also received a lot of updates so I thought it was time for me to start working on ASP.NET also. ASP.NET 5 has a lot of great features:

  1. Fast start-up.
  2. Just-in-time compilation.
    Actually, compilation now works as you type in the code, or as you change the debugging settings (Debugging settings also require you to re-compile the project; build some might say). 
  3. Shorter memory benchmark.
  4. Above all, XML is getting out, JSON is getting into the frameworks. most settings are now using JSON! 

In this article I will create a stand-alone Web API, without HTML pages because I am not showing how to create a small API for your website, instead a full-featured API (standalone API) for your project that needs to be hosted. There is a common difference in between these. 

ASP.NET Web Application + Web API

Standalone Web API (No Web App)

Good if you want to have both flavor for your client. This would allow you to get use of an HTML application and an API for clients; mobiles and software apps.

Much powerful framework and being stand-alone would allow your developers to focus on only one part, HTTP RESTful clients

They both host on the same server, save hosting environment, save URL. 

This option doesn't include application, you will host only the API. You can host it anywhere, even inside your own network or send it to clients to run on their networks!

Cross-platform data sharing plus user-interface.

Web APIs are not intended for user-interfaces. They are simple command-based services. 

Cost double time, developers have to work for maintaining the application's user-interface for the web application part and the web API part.

User-interface part is removed, so all you have to focus on is the logic of your application. 

Table 1: Differences between ASP.NET Web Application + Web API and ASP.NET Web API alone.

Now, let me ask you a few things. Do you:

  1. Really need to stick to old web programming in which you would have to maintain the look and feel of your application cross-browser? IE is still the horror!
  2. Need your clients to install offline applications on their machines so that they can run and execute functions with fancy buttons and that you do not have to worry about security that much?
  3. Want to cut short your financial budget and team size?

If you answered yes, then trust me. ASP.NET MVC application is not what you are looking for. Straight away create a small Web API and use it for your purposes. It is still based on ASP.NET MVC so that the technical knowledge you have for ASP.NET MVC can be implemented for ASP.NET Web API. A few good things that come with Web API are:

  1. No worry about user-interface. You build the user-interface for the client. Most are simple console applications. But Windows Presentation Foundation, Windows Forms can also be used to consume those APIs. Which means, that graphical support is even better with these frameworks in hand.
  2. Cross-platform support. A Web API can be consumed by any device, platform, OS, location and network. Thus, it prevents you from writing a separate environment and service for every platform and so on. It hosts on a same location, rest is assured by the network. 
  3. Smaller memory benchmark as compared to other web services and websites. Although you can write web applications to ask for data. But, Web API can allow you to write native applications (good performance!) and at the same time native experience.

Windows 10 on the other hand, is a new OS using the same old Windows Runtime technology. Windows Runtime allows you to write applications that are compiled to a native library providing you much better performance than any other .NET application can. Windows Runtime is the core of your Universal Applications Platform. 

In this article I will create two projects, one for each. 

  1. An ASP.NET 5's Web API (stand-alone, do not confuse yourself with the Web API + Web Application template). 
    This would serve as the API we are going to make some requests to, to get the data for our application's logic.
  2. A Windows 10 native Universal application
    This would be the client for our server. Note that we would create the user-interface in only this Windows 10 application. The look and feel would follow the native conventions and themes. The rest would be captured by the API on run-time. 

Understanding ASP.NET 5 Web API and Windows 10

Before we get started, it is better to actually understand what ASP.NET 5 has for us in its API and what would Windows 10 serve us as. We will dissect a few things of them each, and learn them in steps.

ASP.NET 5 Web API

ASP.NET 5 is a new version of ASP.NET by Microsoft. Web API has been introduced for ASP.NET since a very long time and we are well aware of what a RESTful API is. ASP.NET's Web API allows us to use HTTP verbs to communicate with the API. Since ASP.NET's Web API is based on much of a MVC pattern (ASP.NET MVC) you can define action methods for each of the type. Attributes are used to target an action. 

// HttpGet attribute notifies that this should be run if request has GET method
[HttpGet]
public List<string> Get() {
   return _list; // Suppose _list is of type List<string> with some data
}

This way we can generate responses for our clients. Also note that unlike default MVC we are not returning an ActionResult which allows us to return a View (with or without Model). Instead we return native objects, that are returned either in JSON or XML format. 

Personally, I recommend using JSON format instead of XML because of its compact size. You should consider keeping the traffic usage lesser, bytes large in size may throttle the network or take a lot of time. Which is not something to happen in your applications. 

Rest of the codes are just basics of C# and MVC pattern. We create a model, add some logic to it to retreive the data and we return (or manipulate) the data using our API. 

Windows 10 — Universal App

Time to introduce ourselves Windows 10. Windows 10 is just a fancy term that I used in the title. In real what I mean is using Windows Runtime to create a client. I won't be using .NET's applications such as Windows Presentation Foundation or Windows Forms. Instead a native Universal application, Windows Runtime based one. 

Windows Runtime has a lot of helpful libraries for us to communicate over network. Windows.Web.Http namespace of Windows Runtime is one of them. It exposes us to HttpClient object in a Runtime application. It is indeed similar to HttpClient of .NET framework, but with better performance because Runtime is built on top of native code, unlike .NET framework

User-interface for it is native, too. I will use Windows.UI.Xaml.Controls library to render different controls on screen. 

  1. Page
  2. TextBlock
  3. TextBox
  4. Button
  5. ListView

Adding just a few lines of C# code would make it our client for the API. Next, I will show you how to create a Web API and how to configure it to run! 

CRUD — introduction

CRUD is just an abbreviation to Create, read, update and delete. An application with CRUD functionality is simply an application that allows you to

  1. Create a new record.
  2. Read all records, or one record at a time.
  3. Update those records.
  4. Delete any record.

In our API, I will create same functions. They would allow us to perform CRUD functions on our model. There is no card under my sleeve when I am writing a CRUD API. It is just a fancy term, nothing else. 

Bonus library — Newtonsoft.Json

Since we are going to work around with JSON data, I would recommend that you use Newtonsoft.Json library while working with JSON data. In my application, I have also used this library. Trust me, it saves you from most of troubles.

You can get it from NuGet by running the following NuGet Package Manager command:

PM> Install-Package Newtonsoft.Json

NuGet would install the package for you. In the source code that I will provide along with this article, package is already included for you.

Getting started

I hope by now you have understood what is ASP.NET 5 and what is Windows 10's Universal App. If so, you may continue to program a client/server architecture based on both of these new technologies on the block!

Setting up environment

Before we get started, you need to set up your environment for development. I want you to make sure of a few things:

  1. Do you have Windows 10?
    Programming a Windows 10 application requires you to be present on a Windows 10 operating system. So, get a copy of Windows 10, by upgrading or by becoming an Insider! 
  2. Do you have Visual Studio 2015?
    Visual Studio 2015 is required to program a Windows 10 application. You can get a Visual Studio Community Edition license for free to start programming an application. ASP.NET 5 also comes shipped with Visual Studio 2015, so Visual Studio 2015 is a must

Once you have answered yes to above questions continue. In next section I will explain creating both, API and a client for your own purposes. 

Creating the Web API

I would recommend that you first develop the API rather than writing the code for your client. Writing the API would benefit you in a number of ways. 

  1. Having the API already set up would help you to do the scratch work for clients. In a way that you will know what methods are provided in API, what parameters I need to pass and so on. 
  2. You won't have to make changes to the API again just to ensure that client and API are working together. Editing the API for one client would also make it potentially problematic for other clients; devices.
  3. You must always configure your clients to work with API, not the other way around. You are going to have multiple clients, connecting through multiple networks, devices and platforms. Your API is static, it won't change. That is why, make sure your API is open enough that any one can connect. Thus, simple HTTP verbs and a very clear action method is enough.

Tip: Try creating interfaces and objects while working in API. This way, upon a request you would be able to send that request to another back ground thread or an interface which would handle the request and then return the data back to you. Hard-coding everything right inside the request handler would make it harder to be edited once your model gets to enterprise level complexity. 

In the coming sections I will show you how make sure that the model is not kept complex, rather a very simple and straight-forward one which is capable of further programming to make it even better! Smile | :)

Create a new Web API project

In Visual Studio 2015 (which even Edition you hold, I had Community) create a new web application project. Make sure that you select from ASP.NET 5 templates and not the ASP.NET 4 templates. Among them, select the Web API and then continue with your project. 

At this stage, your project would only have one single controller, ValuesController in my case. Just edit that out, change the name of class to UserController and (if you want to tidy up a few things) change the file name to UserController.cs. Leave everything else as it is until we have created a model to work around with when making requests and accepting requests from clients. 

Creating and editing the Model

Since Web API is mostly dependant on ASP.NET MVC, you will see the Model-view-controller pattern being applied here. Similarly, that controller of our Web API (recall the UserController) and the Model (which we are going to create now) form a part of that MVC pattern. As already mentioned there are no Views in this case because we are going to work around with only HTTP requests and no HTML rendering to be used for View

I have created a basic example of objects for you that I would be using. A simple model structure for our object, User. I have another class (with all static members) that would serve us for our model data source. The following is the class diagram for them:

Pretty much clear it is, User has the properties that can be used for our data. We can edit them in the list to create a unique list of users for our data source. Further all we need is a model with some static functions to allow us to perform the actions. Model allows us to manipulate the data source of ours (in this article case it is just a list of users). 

User class:

// Actual data model
public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public bool Gender { get; set; }
    public string[] Interests { get; set; }
}

Model class:

// A class to work-around with out data source
public class Model
{
    // Example data source
    private static List<User> users = new List<User>()
    {
        new User 
        { 
            ID = 1, 
            Name = "Afzaal Ahmad Zeeshan", 
            Gender = true, 
            Interests = new string[] { "Programming", "Songs" } 
        },
        new User 
        { 
            ID = 2, 
            Name = "Shani", 
            Gender = true, 
            Interests = new string[] { "Eminem", "Eating" } 
        },
        new User 
        { 
            ID = 3, 
            Name = "Daniyal Ahmad Rizwan", 
            Gender = true, 
            Interests = new string[] { "Games", "Movies" } 
        },
        new User 
        { 
            ID = 4, 
            Name = "Marshall Bruce Mathers III",
            Gender = true, 
            Interests = new string[] { "Rapping", "Parenting" } 
        }
    };
        
    // C part of CRUD
    public static void CreateUser(User user)
    {
        users.Add(user);
    }

    // R part of CRUD
    public static List<User> GetAll()
    {
        return users;
    }

    public static User GetUser (int id)
    {
        return users.Find(x => x.ID == id); // Find one user and return him
    }

    // U part of CRUD
    public static void UpdateUser(int id, User user)
    {
        users.Remove(users.Find(x => x.ID == id)); // Remove the previous User
        users.Add(user);
    }

    // D part of CRUD 
    public static void DeleteUser(int id)
    {
        users.Remove(users.Find(x => x.ID == id)); // Find and remove the user
    }
}

Revise the above code, isn't it pretty much clear as to what each function does? There is no ambiguity and the function and role is also clear. We would definitely use these functions in our controllers to execute different aspects of actions based on HTTP requests and their verbs. 

In addition to the function, property (data source) users is also static. The reason behind that is if it was not set to static, every new request would re-initialize the data source to initial (4 object) list only. Which would undo any changes that we perform in the application. Indeed this was just over-the-head data source. In your application you would have a database supporting your application. Database would have other functions and features for your project. In those cases, you won't need to even create these classes and objects. You would be able to get a driver or controller for your database. For example, System.Data.SqlClient for SQL Server databases.

Final touch to the API

Now time to update the API controllers to handle the request and pass the control over to model to manipulate the state of application to return a response if client is waiting for a response. The controller is as simpler as 4 methods, one for each request type. 

The source code for the controller is:

[Route("api/[controller]")]
public class UserController : Controller
{
    // GET: api/user
    [HttpGet]
    public IEnumerable<User> Get()
    {
        return Model.GetAll();
    }

    // GET api/user/5
    [HttpGet("{id}")]
    public User Get(int id)
    {
        return Model.GetUser(id);
    }

    // POST api/user
    [HttpPost]
    public void Post([FromForm]string value)
    {
        var user = JsonConvert.DeserializeObject<User>(value); // Convert JSON to Users
        Model.CreateUser(user); // Create a new User
    }

    // PUT api/user/5
    [HttpPut("{id}")]
    public void Put([FromForm]int id, [FromForm]string value)
    {
        var user = JsonConvert.DeserializeObject<User>(value);
        Model.UpdateUser(id, user);
    }

    // DELETE api/user/5
    [HttpDelete("{id}")]
    public void Delete(int id)
    {
        Model.DeleteUser(id);
    }
}

Have a look again, same basic functions. One for each of type of request that user may send. These functions are handled by that Http attribute. It tells the API which method to execute for which HTTP verb. 

Request URL

HTTP verb

Response

GET http://www.example.com/api/user

GET

[Users]

GET http://www.example.com/api/user/1

GET

{User}

POST http://www.example.com/api/user

POST

void | bool | object

PUT http://www.example.com/api/user

PUT

void | bool | object

DELETE http://www.example.com/api/user

DELETE

void | bool | object

Table 2: HTTP requests, methods and responses.

The above table has a lot of things that you need to understand before continuing reading more! A few things I will make clear here for you:

  1. In the first case (with no ID appended), the result is an array. Notice that the convention for return in first two rows is JSON (or JavaScript). In first case an array is returned. In second one, a single instance of object is returned. Due to spacing, I did not write the objects in their formal representation, sorry!
  2. In case of POST, PUT and DELETE I have returned void. You can (as per your need) return a bool or object to work around with the response on client-side. Suit yourself. Smile | :)

This way, these requests are sent to the server, mapped to an action and then executed. The response is then streamed to client over network which then renders it. Ok, enough of API, now let us consider writing the client application in Windows 10 framework. 

Configuring the server settings

Although if you are willing to continue with default settings then you should feel free to skip this section. But you find yourself handy while editing out the website configurations. It would allow you to use a handy URL and a few other settings. Consider going to properties page and under the Debug (or Web) edit the settings. 

In my case, I had set start-up URL to /api/user and the port was changed to 1234. Easy to remember.

Creating the client in Windows 10 application

Now in this section I will show you how to create a Windows 10 application that acts like a client to our API. I will have template pages all created and ready for the API to fill the data in to the pages. I would have a few pages and a bunch of logical expressions to actually run the logic. I won't show how everything is connected at the back-end just a few high-level abstraction of the application will be shown. You can (if eager to test) download the source code provided and test the project by building it and running it on your own machine. 

Note: Make sure API is running while your start the Windows application. Otherwise, it won't work or at worse start annoying you.

Now open Visual Studio 2015 and create a new project for Windows 10. You will find it under Visual C# → Windows → Universal. Create an empty application. You do not to bug yourself with a complex framework for such a basic application as it stands. 

Creating the application views

You may consider making the views for your application. In source code I have added 3 pages for our application. 

  1. View
    This page would refer to the Read of CRUD.
  2. CreateOrUpdate
    This page would be used to either Creare or Update of CRUD. Since both of these forms would provide same fields for input. I thought why create two different pages, just create one and on a condition change their values! Helpful though. Smile | :)
  3. Delete
    Finally, this would prompt you to delete the record.

I won't show the source code to the XAML markup because that would make the article too long to read and this article is not about XAML or Windows 10, instead more of about server-client applications using ASP.NET 5 Web API and Windows Runtime. So, I would focus on Windows Runtime APIs more. If you are interested in application codes, please download the source code and tinker with it. Feel free to share it or port it in your own applications also. 

First step to be a client

Now the main part of Windows Runtime programming. Windows Runtime is not .NET framework, and the objects that you are aware of in .NET framework are not available in Windows Runtime. Windows Runtime has its own API sets and libraries. However a portable library of .NET framework is also available for Windows Runtime. 

In this application, I will be using HttpClient object of Windows.Web.Http namespace rather than using HttpClient of System.Net.Http namespace. They are entirely different ones, so please don't get yourself confused. Windows.Web.Http.HttpClient is a Windows Runtime object, where as System.Net.Http.HttpClient is a .NET object. Since this object also implements IDisposable, we can use it in a using block for better results.

Most of the times, the code would just be like

using (var client = new HttpClient()) {
   /*
    * Send requests here
    * client.GetAsync(); For GET HTTP
    * client.PostAsync(); // For POST HTTP
    * Parameters are still required to be passed.
    **/
}

Rest of the stuff is simply application state logic which shouldn't be explained as it would bring up some confusions. That is why I will intent to paste the code only which is relative to CRUD operations among our client-server model. 

Initiating CRUD requests

In HTTP API, different verbs are used to initiate requests and to tell server what to do. In this section I will show you how to actually send requests to the API with a specific HTTP method along with some data, so servers know what to do with the data. You must note that HTTP GET and HTTP DELETE requests do not need any additional data. You can pass parameter in the URL such as DELETE http://www.example.com/api/user/5 and server would know to delete the user at ID 5. PUT and POST methods require you to pass some data that has to be saved or stored. 

HTTP PUT or HTTP POST

Mostly question arise, since they both update content on server, which must be used to actually implement to store the data. HTTP PUT or HTTP POST. Some use POST to upload data and PUT to update it, some use PUT to store the data and POST to update it.

The actual answer to this ambiguity is their idempotency, which one is actually intended to update the data and which one stores the data. 

From a very great thread I got the answer, 

HTTP Post
x++
HTTP PUT
x = 5

Easy isn't it? HTTP POST is not idempotent, it is intended to work when you want to, let's say, add more records to the database. Increments the records always. HTTP PUT on other hand create a new record (if it doesn't exist) keeps updating it (if it already exists).

Setting up the application

I created some application properties as to be able to work around with the API and state of application all in one application. 

public static Uri BaseUri = new Uri("http://localhost:1234/api/user"); // base API URL; UserController
public static Frame RootFrame { get; set; } // RootFrame to be updated for different Views
public static User ActiveUser { get; set; } // Active User to be worked with. 

Creating or updating the user

First step I would talk about is creating the user, creating the user takes a form which can be then reused for updating the user. I would use the same page, which just a little alteration. So, it would serve me for both purposes. 

Creating the user

While creating, the form would be empty. We would fill in the details and then we will send those details using HttpClient to the API. 

Upon submission, the data is passed over to API by serializing the content to JSON. 

Updating the user

Updating the user is just so similar process, only difference is that in this case the form won't be empty. Instead application would show us the previous data that we can edit and then click "Update".

The source code for them is similar, that is why I did not share it before. What happens is when button is clicked (in both cases) data is serialized and a request is sent to API with appropriate HTTP method. 

private void Button_Click(object sender, RoutedEventArgs e)
{
    if((sender as Button).Content.ToString() == "Cancel")
    {
        // Go to default page 
        App.RootFrame.Navigate(typeof(MainPage));
        return; // and cancel the event.
    }

    // Otherwise
    var user = new User
    {
        ID = Convert.ToInt32(userId.Text),
        Name = name.Text,
        Gender = (gender.IsChecked == true) ? true : false,
        Interests = interests.Text.Split(',')
    };
            
    using(var client = new HttpClient())
    {
        var content = JsonConvert.SerializeObject(user);

        if (create == null || create == true)
        {
            // Send a POST
            Task task = Task.Run(async () =>
            {
                 var data = new HttpFormUrlEncodedContent(
                     new Dictionary<string, string>
                     {
                         ["value"] = content
                     }
                 );
                 await client.PostAsync(App.BaseUri, data); // sends POST request
             });
             task.Wait();
         }
         else
         {
             // Send a PUT
             Task task = Task.Run(async () =>
             {
                 var data = new HttpFormUrlEncodedContent(
                     new Dictionary<string, string>
                     {
                         ["value"] = content,
                         ["id"] = App.ActiveUser.ID.ToString()
                     }
                 );
              await client.PutAsync(App.BaseUri, data); // sends PUT request
              });
              task.Wait();
         }
    }

    App.RootFrame.Navigate(typeof(MainPage)); // Finally navigate back
}

Reading the users

Reading the users is (IMO) the simplest task to perform on the API. You send a request (with no additional data) and API returns a list of users. 

To fetch the records from the API, use the following code,

var response = "";
Task task = Task.Run(async () =>
{
    response = await client.GetStringAsync(App.BaseUri); // sends GET request
});
task.Wait(); // Wait
listView.ItemsSource = JsonConvert.DeserializeObject<List<User>>(response); // Bind the list

The result for this would be a list, I used ListView control to define my own custom layout for the list rather than default ToString to be called. 

You can see the new user that we created in last section available there. Right now it doesn't show much details, clicking on it (SelectionChanged) would help us. 

The second one (although doesn't have a separate code) comes from the ActiveUser property of application. I changed the view when the selection changes, which enables me to change the view and show the details that I have to. 

Deleting the users

Final and another easy task is to delete the users. It takes just a DELETE request and no additional data. 

Upon clicking the Yes button. Application executes the following code, 

// Send a request to delete the user
using (var client = new HttpClient())
{
    Task task = Task.Run(async () =>
    {
        await client.DeleteAsync(App.BaseUri + "/" + App.ActiveUser.ID.ToString());
    });
    task.Wait();
}
App.RootFrame.Navigate(typeof(MainPage));

Quite simple though. 

So, as from this application it is clear how easy it actually is to create cross-platform clients that consume a central API. It allows us to create a native application on different platforms and send HTTP requests to the API. Another good thing is that applications all can have a native feel and look. We don't have to work that much hard to fix the cross-platform and cross-device layout and other similar problems that arise in web applications. 

Download the source codes from the article and try out the applications yourself. You can use it for your own purposes also, feel free to share it. Smile | :)  

History

First version of post. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Afzaal Ahmad Zeeshan
Student
Pakistan Pakistan
I am "The Afzaal Ahmad Zeeshan"!

I am a self-learner, computer programmer, Eminem's fan, student, teacher, blogger, gamer and not available. Smile | :)

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Praiseasp.net and api Pin
Beginner Luck24-Jan-16 21:01
memberBeginner Luck24-Jan-16 21:01 
GeneralMy vote of 5 Pin
dangquang10202-Oct-15 23:37
memberdangquang10202-Oct-15 23:37 
GeneralNice Pin
Shuby Arora12-Sep-15 3:02
memberShuby Arora12-Sep-15 3:02 
GeneralRe: Nice Pin
Afzaal Ahmad Zeeshan12-Sep-15 3:40
professionalAfzaal Ahmad Zeeshan12-Sep-15 3:40 
QuestionNice Article Pin
Santhakumar Munuswamy @ Chennai11-Sep-15 21:46
professionalSanthakumar Munuswamy @ Chennai11-Sep-15 21:46 
AnswerRe: Nice Article Pin
Afzaal Ahmad Zeeshan11-Sep-15 21:49
professionalAfzaal Ahmad Zeeshan11-Sep-15 21:49 
GeneralRe: Nice Article Pin
Santhakumar Munuswamy @ Chennai11-Sep-15 21:51
professionalSanthakumar Munuswamy @ Chennai11-Sep-15 21:51 
QuestionYour action Parameters Pin
Taofeek Lasisi27-Aug-15 5:09
professionalTaofeek Lasisi27-Aug-15 5:09 
AnswerRe: Your action Parameters Pin
Afzaal Ahmad Zeeshan27-Aug-15 9:02
professionalAfzaal Ahmad Zeeshan27-Aug-15 9:02 
GeneralRe: Your action Parameters Pin
Taofeek Lasisi31-Aug-15 1:49
professionalTaofeek Lasisi31-Aug-15 1:49 
QuestionImproper use of async Pin
markmnl26-Aug-15 22:09
membermarkmnl26-Aug-15 22:09 
AnswerRe: Improper use of async Pin
Afzaal Ahmad Zeeshan27-Aug-15 1:07
professionalAfzaal Ahmad Zeeshan27-Aug-15 1:07 
GeneralRe: Improper use of async Pin
markmnl27-Aug-15 15:27
membermarkmnl27-Aug-15 15:27 
GeneralRe: Improper use of async Pin
Afzaal Ahmad Zeeshan27-Aug-15 15:30
professionalAfzaal Ahmad Zeeshan27-Aug-15 15:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160826.1 | Last Updated 26 Aug 2015
Article Copyright 2015 by Afzaal Ahmad Zeeshan
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid