Friday, 3 August 2007

Ajax style with a flavour of LINQ


In this article, I would be demonstrating an example using Ajax with latest and hottest feature of .net: LINQ (Language INtegrated Query framework). I assume you are familiar with basics of Microsoft’s ASP.Net Ajax and LINQ.

First of all I would like to brief about requirements of the demo and then steps to implement the same.

A client wishes to see list of customers choosing a country.

For this requirement, I am gonna list countries in a dropdown and display customers in a DataGrid. Simple isn’t it?

There are many ways to implement this, but I have chosen my own way using Ajax style with LINQ.

In detail, I will be using UpdateProgress and client scripts for user interaction and LINQ for processing data in the codebehind.


Here are the few steps to go ahead.
Step1: Create a Ajax enabled website either using Visual Studio 2005 Codename Orcas or Visual Web Developer 2005 Codename Orcas.

This will create a web form Default.aspx. Lets make screen ready and then go into deep!.

With a blank web form, you would have already had Scriptmanager control (don’t worry about that now) on top of it. Drop a dropdownlist and GridView from tool box onto the screen as shown below:



So when user selects a country from Dropdown, list of Customers are displayed. Dropdown triggers the request and GridView displays the action.

For this purpose, I have created 2 classes for a Country and Customers inline with Default.aspx.cs which will be a data source for me.


public class CustomerList
{
private List _customers;

public CustomerList()
{
_customers = new List();
_countries = new List();

this._countries.Add(new Country("India", "IN"));
this._countries.Add(new Country("United States of America", "US"));
this._countries.Add(new Country("United Kingdom", "UK"));
this._countries.Add(new Country("France", "FR"));
this._countries.Add(new Country("Italy", "IT"));
}

public List Customers()
{


this._customers.Add(new Customer("Puru", this._countries[2]));
this._customers.Add(new Customer("Venu", this._countries[1]));
this._customers.Add(new Customer("Arindam", this._countries[0]));
this._customers.Add(new Customer("John", this._countries[3]));
this._customers.Add(new Customer("Philips", this._countries[4]));


return _customers;
}

private List _countries;
public List Countries()
{
return _countries;
}
}

public class Customer
{
private string name;

public string Name
{
get { return name; }
}

private Country country;

public Country Country
{
get { return country; }
}

public Customer(string name, Country country)
{
this.name = name;
this.country = country;
}
}

public class Country
{
private string name;

public string Name
{
get { return name; }
}

private string code;

public string Code
{
get { return code; }
}

public Country(string name, string code)
{
this.name = name;
this.code = code;
}
}



Next, Create an handler to handle postback for Dropdownlist in the codebehind. Which will be like this..



protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.DropDownList1.SelectedIndex != -1)
{
}

}
.. .





This is interesting handler to discuss.

In the page load add the below shown code in order to display country list in dropdown.




protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
//..
}
}



This is a point of foucs now. Here in this Page load I am gonna query data from CustomerList using LINQ to assin the result to dropdownlist.




. . .

if (!this.IsPostBack)
{
var countryList = from countries in customerList.Countries()
select countries;
this.DropDownList1.DataSource = countryList;
this.DropDownList1.DataTextField = "Name";
this.DropDownList1.DataValueField = "Code";
this.DropDownList1.DataBind();
}

. . .
Intially I do not want to display the GridView, so make its visible false in PageLoad.
. . .

this.GridView1.Visible = false;

. . .



Then move on to Dropwdown’s handler: SelectIndexChanged..


Here I would need to take selected value from country dropdown and query customsers from the list.

I assume that there will be delay in getting customers from data. Hence there will be a sleep of 5 seconds for this thread.




.. .
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.DropDownList1.SelectedIndex != -1)
{
System.Threading.Thread.Sleep(5000);

var customers = from cust in this.customerList.Customers()
select cust;

var customer = from c in customers
where c.Country.Code == this.DropDownList1.SelectedValue.ToString()
select c;

this.GridView1.Visible = true;
this.GridView1.DataSource = customer;
this.GridView1.DataBind();

}
}

. . .



That’s it from the code behind point of view..
And a lot more needs to be done on aspx page. At this moment if you run the project, when you select a country, there will be a postback for each selection.

We need to implement Ajax for this page.

Step 2: Implementation of Ajax
This page needs partial render/refresh for every post back. So Ajax supplies a control called UpdatePanel and will add on to the page.

UpdatePanel allows us to update a page partially without post backing whole page or re rendering the whole page, which is not desirable if page is has tonnes of data. Hence I have dropped GridView into the UpdatePanel which needs partial refresh.

I am gonna take GridView and drop into the UpdatePanel. No if you run the application, you will not see any post backs at all.

Setp 3: Implement User’s interaction for Cancel/Abort
For this purpose I would need to create client side javascript. So in the head element of html script add below shown lines of code.



. . .
<html xmlns=http://www.w3.org/1999/xhtml>
<head runat="server">
<script type="text/javascript">

</script>
/


For this purpose I will add UpdateProgress control from Ajax Extensions tab. This control is responsible for displaying Progress activities when you select a country and allows us to interact with those post backing activities.

Ajax exposes Ajax Libraries thro Sys namespace. There are tonnes of classes available in this namespace; among them I will be using PageRequestManager from Sys.WebForms namespace.

I had to find workaround to fix “Sys undefined” javascript error on my by browser, Hence I am initalising intial javascript code on load of the html’s body.

So, I have created following code snippet in script block.



<script type="text/javascript">
var prm;

function pageLoad()
{
if(prm == null)
{
prm = Sys.WebForms.PageRequestManager.getInstance();
}
}
</script>


And called this method in OnLoad of body’s event



. .
<body onload="pageLoad()">
<form id="form1" runat="server">
. .

Here I am creating an instance of PageRequestManager. It has several methods, but will be using which are applicable to my requirements.


.add_initializeRequest(..)
.add_endRequest(..)
.abortPostBack() are the methods which am interested in here.

As name suggests they are delegates and consumes methods to initialise, end request.

So I will be creating 2 more metohds to handle initialise and end request and abort/cancel postback/request.



...
var postBackElement;
function InitializeRequest(sender, args) {
if (prm.get_isInAsyncPostBack()) {
args.set_cancel(true);
}
postBackElement = args.get_postBackElement();
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'block';
}
}
function EndRequest(sender, args) {
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'none';
}
. . .


In these methods, I have created postback element which triggers action and setting updatepanel’s propeties using dhtml.

Finally, the overall script looks like this.



<script type="text/javascript">
var prm;

var postBackElement;
function InitializeRequest(sender, args) {
if (prm.get_isInAsyncPostBack()) {
args.set_cancel(true);
}
postBackElement = args.get_postBackElement();
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'block';
}
}
function EndRequest(sender, args) {
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'none';
}
}


function AbortRequest()
{
if(prm != null)
{
prm.abortPostBack();
}
}


function pageLoad()
{
if(prm == null)
{
prm = Sys.WebForms.PageRequestManager.getInstance();
}
prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);
}

</script>

Along with this script, I also would like to add CSS capablities to this page in order to provide elegant look.



<style type="text/css">
#UpdatePanel1 {
width:200px; height:250px;
border: 1px solid gray;
}
#UpdateProgress1 {
width:200px; background-color: #FFC0FF;
bottom: 0%; left: 0px; position: relative;
}
#GridView1
{
width:200px; height:100px;
}
</style>


The next step is, how to trigger action on the page.

We now have, dropdownlist for Country, Gridview for Customer and UpdatePanel for partial rendering upon postback.

I need to add UpdateProgress, a control from Ajax extension which provides status message of activites and abort feature as well.

Please refer UpdateProgress control library for more details from Ajax website.


UpdateProgress provides a property called ProgressTemplate, where in you can add your own html code. Here I am adding a message and an html button and calling javscript’s AbortMethod() upon Abort button click which will cancel a postback which is under progress!



<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
Getting customers..
<input id="btnCancel" type="button" value="Cancel" onclick="AbortRequest()" />
</ProgressTemplate>
</asp:UpdateProgress>


One more thing I would like to mention is, you would need to associate Upadate panel which you are partially refreshing. Here in our case UpdatePanel1 is the candidate.

I think we are done with this page I guess. So lets run and try how it works…





Hope you enjoyed the ride and please leave your comments..

Take care,

Here is the overall code.



<Defaault.aspx.cs>
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Linq;

using System.Collections.Generic;

public partial class _Default : System.Web.UI.Page
{
CustomerList customerList = new CustomerList();

protected void Page_Load(object sender, EventArgs e)
{
this.GridView1.Visible = false;
if (!this.IsPostBack)
{
var countryList = from countries in customerList.Countries()
select countries;
this.DropDownList1.DataSource = countryList;
this.DropDownList1.DataTextField = "Name";
this.DropDownList1.DataValueField = "Code";
this.DropDownList1.DataBind();
}

}

protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.DropDownList1.SelectedIndex != -1)
{
System.Threading.Thread.Sleep(5000);

var customers = from cust in this.customerList.Customers()
select cust;

var customer = from c in customers
where c.Country.Code == this.DropDownList1.SelectedValue.ToString()
select c;

this.GridView1.Visible = true;
this.GridView1.DataSource = customer;
this.GridView1.DataBind();

}
}
}

public class CustomerList
{
private List _customers;

public CustomerList()
{
_customers = new List();
_countries = new List();

this._countries.Add(new Country("India", "IN"));
this._countries.Add(new Country("United States of America", "US"));
this._countries.Add(new Country("United Kingdom", "UK"));
this._countries.Add(new Country("France", "FR"));
this._countries.Add(new Country("Italy", "IT"));
}

public List Customers()
{


this._customers.Add(new Customer("Puru", this._countries[2]));
this._customers.Add(new Customer("Venu", this._countries[1]));
this._customers.Add(new Customer("Arindam", this._countries[0]));
this._customers.Add(new Customer("John", this._countries[3]));
this._customers.Add(new Customer("Philips", this._countries[4]));


return _customers;
}

private List _countries;
public List Countries()
{
return _countries;
}
}

public class Customer
{
private string name;

public string Name
{
get { return name; }
}

private Country country;

public Country Country
{
get { return country; }
}

public Customer(string name, Country country)
{
this.name = name;
this.country = country;
}
}

public class Country
{
private string name;

public string Name
{
get { return name; }
}

private string code;

public string Code
{
get { return code; }
}

public Country(string name, string code)
{
this.name = name;
this.code = code;
}
}



<Default.aspx>
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
#UpdatePanel1 {
width:200px; height:250px;
border: 1px solid gray;
}
#UpdateProgress1 {
width:200px; background-color: #FFC0FF;
bottom: 0%; left: 0px; position: relative;
}
#GridView1
{
width:200px; height:100px;
}
</style>
<script type="text/javascript">
var prm;

var postBackElement;
function InitializeRequest(sender, args) {
if (prm.get_isInAsyncPostBack()) {
args.set_cancel(true);
}
postBackElement = args.get_postBackElement();
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'block';
}
}
function EndRequest(sender, args) {
if (postBackElement.id == 'DropDownList1') {
$get('UpdateProgress1').style.display = 'none';
}
}


function AbortRequest()
{
if(prm != null)
{
prm.abortPostBack();
}
}


function pageLoad()
{
if(prm == null)
{
prm = Sys.WebForms.PageRequestManager.getInstance();
}
prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);
}

</script>
</head>
<body onload="pageLoad()">
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" />
</div>
<div>
<asp:DropDownList ID="DropDownList1" runat="server" Width="200px"
AutoPostBack="True"
onselectedindexchanged="DropDownList1_SelectedIndexChanged1">
</asp:DropDownList>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID ="DropDownList1" />
</Triggers>
</asp:UpdatePanel>
</div>
<div>
</div>
<div>
<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
Getting customers..
<input id="btnCancel" type="button" value="Cancel" onclick="AbortRequest()" />
</ProgressTemplate>
</asp:UpdateProgress>
</div>

</form>
</body>
</html>

Friday, 6 July 2007

ActionTree

Well, i always wanted to create a control that is generic and should provide common functionalities used in our daily programming activities. Here, in this article i have made an attempt to express my thoughts and experience on creating and managing a control which provides basic functionalites.

We know that we use activities CRUD's like Create, Save/Update, Delete in almost all UI's of applications. Also, we think that there should be a way to make these activites by using controls like buttons. We rather place buttons for each Create, Save/Update and Delete and handle their enablity and visiblity at various scenarios.
ActionTree is named after a control which will have Action basic buttons like Create, Save, Delete and provides a an object in the form of Tree to these controls to control their enablity.

Step 1:

So, to get started i am gonna create a form (can be a windows or webform, doesnt matter!).

Next, to control enabilty or visibility of these controls, i would need a class, which holds a property for each control.

public class ActionTree
{
private bool _create;

public bool Create
{
get { return _create; }
set { _create = value; }
}

private bool _save;

public bool Save
{
get { return _save; }
set { _save = value; }
}

private bool _delete;

public bool Delete
{
get { return _delete; }
set { _delete = value; }
}
}


The propeties defined in above class can be corresponds to Properties of Control's Enabiltiy or Visuality. In my case i am choosing enability.

Step 2:
for the above class, i would create one more property called HasChanged and a method NotifyChange() which will notify that property has changed (One more thing i would like to mention here is i am using Observer pattern here) as follows:


...
...

public bool Delete
{
get { return _delete; }
set
{
_delete = value;
NotifyChange();
}
}

private bool _hasChanged;

public bool HasChanged
{
get { return _hasChanged; }
}

private void NotifyChange()
{
this._hasChanged = true;
}


In NotifyChange() method, i would need to notify that property has changed. In order to do that, i am gonna create an event of type EventHandler delegate. But i also would need these properties to send out. So i am gonna create a class to hold that as well.


public class ActionTreeEventArgs : EventArgs
{
private ActionTree _actions;

public ActionTreeEventArgs(ActionTree actions)
{
this._actions = actions;
}

public ActionTree ActionTree
{
get
{
return _actions;
}
}
}


then create a event property of type EventHadnler delegate in class ActionTree which would look like as follows:

ActionTree.cs

...
...
private event EventHandler _actionTreeChanged;
public event EventHandler ActionTreeChanged
{
add
{
_actionTreeChanged += value;
}
remove
{
_actionTreeChanged -= value;
}
}


then finally i am gonna create a method in ActionTree class, to handle this event and notify.

ActionTree.cs

..
..
private void NotifyChange()
{
this._hasChanged = true;
}

private void OnActionTreeChanged()
{
EventHandler handler = this._actionTreeChanged;
if (handler != null)
{
handler.Invoke(this, new ActionTreeEventArgs(this));
}
}
...
...


and call this OnActionTreeChanged() method in NotifyChange() method. So we are nearly complete. What i mean is, at this moment, we are able to notify. and the UI who is subscribing to these notification needs to be done. that is step 3!.
so final ActionTree class looks like this!.


public class ActionTree
{
private bool _create;

public bool Create
{
get { return _create; }
set
{
_create = value;
NotifyChange();
}
}

private bool _save;

public bool Save
{
get { return _save; }
set
{
_save = value;
NotifyChange();
}
}

private bool _delete;

public bool Delete
{
get { return _delete; }
set
{
_delete = value;
NotifyChange();
}
}

private bool _hasChanged;

public bool HasChanged
{
get { return _hasChanged; }
}

private void NotifyChange()
{
this._hasChanged = true;
this.OnActionTreeChanged();
}

private void OnActionTreeChanged()
{
EventHandler handler = this._actionTreeChanged;
if (handler != null)
{
handler.Invoke(this, new ActionTreeEventArgs(this));
}
}

private event EventHandler _actionTreeChanged;
public event EventHandler ActionTreeChanged
{
add
{
_actionTreeChanged += value;
}
remove
{
_actionTreeChanged -= value;
}
}
}


Step 3:
Now we are on UI. Lets create a Windows/Web form. I am gonna create Windows form and add few buttons as said earlier as shown below:



In the form code for each button click, other buttons should be disable accorging to the funcationality requirements. So, i am gonna create handler for each button as follows:


private void btnCreate_Click(object sender, EventArgs e)
{

}

private void btnSave_Click(object sender, EventArgs e)
{

}

private void btnDelete_Click(object sender, EventArgs e)
{

}



and declare ActionTree as class variable and initialise in Load event.


private ActionTree _actions;

private void Form1_Load(object sender, EventArgs e)
{
_actions = new ActionTree();
_actions.ActionTreeChanged += new EventHandler(_actions_ActionTreeChanged);
}

private void _actions_ActionTreeChanged(object sender, ActionTreeEventArgs args)
{
this.btnCreate.Enabled = args.ActionTree.Create;
this.btnSave.Enabled = args.ActionTree.Save;
this.btnDelete.Enabled = args.ActionTree.Delete;
}



Now, we can now actually control properties of buttons using ActionTree object.
for each button's handler, i am gonna introduce the following snippet of code.


private void btnCreate_Click(object sender, EventArgs e)
{
_actions.Create = false;
_actions.Save = true;
_actions.Delete = true;
}

private void btnSave_Click(object sender, EventArgs e)
{
_actions.Create = true;
_actions.Save = false;
_actions.Delete = true;
}

private void btnDelete_Click(object sender, EventArgs e)
{
_actions.Create = true;
_actions.Save = true;
_actions.Delete = false;
}


and when i open form and click on any button, it looks like this!






Nice isn't it!!

Here is the complete code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ActionTreeDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private ActionTree _actions;

private void btnCreate_Click(object sender, EventArgs e)
{
_actions.Create = false;
_actions.Save = true;
_actions.Delete = true;
}

private void btnSave_Click(object sender, EventArgs e)
{
_actions.Create = true;
_actions.Save = false;
_actions.Delete = true;
}

private void btnDelete_Click(object sender, EventArgs e)
{
_actions.Create = true;
_actions.Save = true;
_actions.Delete = false;
}

private void Form1_Load(object sender, EventArgs e)
{
_actions = new ActionTree();
_actions.ActionTreeChanged += new EventHandler(_actions_ActionTreeChanged);
}

private void _actions_ActionTreeChanged(object sender, ActionTreeEventArgs args)
{
this.btnCreate.Enabled = args.ActionTree.Create;
this.btnSave.Enabled = args.ActionTree.Save;
this.btnDelete.Enabled = args.ActionTree.Delete;
}
}

public class ActionTree
{
private bool _create;

public bool Create
{
get { return _create; }
set
{
_create = value;
NotifyChange();
}
}

private bool _save;

public bool Save
{
get { return _save; }
set
{
_save = value;
NotifyChange();
}
}

private bool _delete;

public bool Delete
{
get { return _delete; }
set
{
_delete = value;
NotifyChange();
}
}

private bool _hasChanged;

public bool HasChanged
{
get { return _hasChanged; }
}

private void NotifyChange()
{
this._hasChanged = true;
this.OnActionTreeChanged();
}

private void OnActionTreeChanged()
{
EventHandler handler = this._actionTreeChanged;
if (handler != null)
{
handler.Invoke(this, new ActionTreeEventArgs(this));
}
}

private event EventHandler _actionTreeChanged;
public event EventHandler ActionTreeChanged
{
add
{
_actionTreeChanged += value;
}
remove
{
_actionTreeChanged -= value;
}
}
}

public class ActionTreeEventArgs : EventArgs
{
private ActionTree _actions;

public ActionTreeEventArgs(ActionTree actions)
{
this._actions = actions;
}

public ActionTree ActionTree
{
get
{
return _actions;
}
}
}
}


Finally, u might ask whts the use of using this!? .. confused?... let me make it clear now.

1. ActionTree will have all button's propertes whcih you want to control
2. UI update is completely thread safe!. (irrespective of anything doing in background or asynchronous)

3. you need not have to search buttons on the screen everytime. you will always will see controls defined in ActionTree, thats why its called TREE!...

hope you enjoyed the ride and please leave your critics.