Tuesday 8 April 2008

WCF Hello World Part -2

In my previous post, I have created a WCF service. In this part, I will create a client to consume the WCF Service created. Since this is an hello world example, Let's create a web method in WCF service. Todo that, create a method first on interface and then implement it in the service as shown in code.


[ServiceContract]
public interface IHelloWorldService
{

[OperationContract]
string GetData(int value);

[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);

// TODO: Add your service operations here
[OperationContract]
string GetGreetings(string name);
}

public class HelloWorldService : IHelloWorldService
{
...

public string GetGreetings(string name)
{
return string.Format("Hello {0}, Welcome to WCF World!", name);
}

....
}



Now, lets create a client to consume this service. I prefer to create a web client. In order to do this, one needs a service proxy, which will communicate to the service using Soap as transport mechanism. There are 2 ways to create a proxy.
1. by using Visual Studio
2. by using svcutil.exe utility by WCF framework.


for this example, i will simply use Visual Studio as we used to do earlier versions of .net. Add a new website by name WCFHelloWorld-Web and add a service reference and point to the WCF service created as show shown fig below.







Add namespace of WCF Service to codebehind of aspx file, in this case ServiceReference1 is the namespace. using this namespace, create a proxy object as class variable in the aspx.cs file and in aspx file create some server controls in order to type some input and display as label. I have created UI as follows:



and in button event handler, pass the textbox input to webservice's GetGreetings method and display on UI with label as shown below:


using ServiceReference1;

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

protected void Page_Load(object sender, EventArgs e)
{

}
protected void btnSumbit_Click(object sender, EventArgs e)
{
lblGreetings.Text = proxy.GetGreetings(txtName.Text);
}
}



Hit F5 to see the aspx page in browser. Input some name and hit Get Greetings button, you should be able to see display output as shown in fig below:





that's it.. simple to create a web cilent and consume a WCF service. I will take you in depth in next part of this series, until then bye.

Thursday 3 April 2008

WCF Hello World Part -1

This is my first posting on WCF, i thought I would start with Hello World on WCF and i dont know to what extent WCF will take me!. For a reader who finds this posting, this is would be a head start on WCF and am gonna discuss challenges and issues on WCF in part by part.

so, here i am.

first of all I would like to brief What tools i will be using at the moment.

1. Visual Studio 2008 for this posting.

I may wish to revisit this part again for my next posting.

Ok then. Lets fire up Visual Studio 2008 and create WCF Service by name WCFHelloWorld as shown in fig below:



So When i have created, Visual Studio creates some files for me. those are

1. Service1.svc
2. IService1.cs
3. Web.Config
4. App_Code folder with web project settings and references.

What might interests is 2 files 1 and 2 from above. In eariler version of .Net, you would have created Web Services and seen asmx files instead.

now, I am gonna rename those files to fit into my demonstration as WCFHelloWorld.

So, I rename Service1.svc as WCFHelloWorld.svc and IService1.cs as IWCFHelloWorld.cs.

I leave it with you, how you rename the files and organise and make it compilable at least for now. finally i get my compilable as shown below:





Note: Keep in mind that WCF is a runtime for Web Services. In earlier versions of .Net, Asp.Net was runtime for Web Services. now WCF is runtime for Web Services and .Net remoting!. Those who have worked on .Net Remoting, here is a good news, now they can enjoy exploring separate runtime!.


When you build is succeeded, press F5 and opens in browser as shown below:



At this moment, I have created web service and launched it. In next part of this demo, will create a client and consume this web service.

I have tried to make it simple, hence have not been able to delve in detail but taking this as an example, in some part later of this demo will go in depth, until then bye...

----------------end of part -1 ------------

Postings coming soon! on .Net 3.5 Framework concepts

Its been a while I haven't posted anything on my blog after started working at Cambridge Assessment. Though i had so many things in my mind, could not post even a post!. Today, i am making an effort to put as a start up and have decided to put some articles on 3.5 Framework's WCF, ASP.Net MVC, Linq and Sql Server 2008. I hope i can maintain a consistency in posting at least one everyday!.

Wish me good luck!

Saturday 8 December 2007

SQL SERVER - Fix : Error : 40 - could not open a connection to SQL server

Recently i was setting up development environment at home. When i had to connect SQL server remotely from another VPC, i could not connect it even after enabling allow remote connections on my Sql Server. the error messaage was...

An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)


for some reason it did not work as suggested by many..

but after banging my head to wall... cpu and then to lcd monitor, figured the solution.

but dont worry, figured what was the problem at last………

the probelm is the Instance name from the client is not resolved, to do that follow the below shown steps.

1. Navigate to C:\Windows\System32\drivers\etc
2. Open hosts file in notepad
3. Add the entry at the end of line, as i did for example

192.168.131.65 KSSQLSRVR
192.168.131.65 KSSQLSRVR\SPDEV01

those were my instances i am tried to connect remotely

sweeeeet... it worked!

should work for anyone who have this problem...

Monday 5 November 2007

Setting up different Authentication models in WSS 3.0 - Part 1



Setting up different Authentication models in WSS3.0
Currently WSS 3.0 supports following authentication models for intranet, extranet and Internet environments for their partner applications.

1. Windows
2. Forms

Forms authentication is preferred over windows authentication for extranet environments. The WSS 3.0 supports following Authentication providers.

1. Active Directory Membership Provider
2. SQL Membership provider
3. Single Sign On - SSO

In this article I have made an attempt to depict Authentication using Active Directory membership provider. And another challenging functionality implemented is, if for some reason, user can not logon, the page would display the reason along with logon details like account locked, expiry date and disabled features as shown in screen shot below:



Hence I have divided this article as 3 parts.
Part 1: Develop and Implement Custom Login for Active Directory
Part 2: Develop and Implement Custom Change Password for Active Directory

Part 1: Develop and Implement Custom Login for Active Directory


In order to implement this part, firstly I would need to create a class library which would override Login page of sharepoint provided page.

3. From Visual Studio, create a class library and name the project as SharePoint.Authentication.Custom.ActiveDirectory.

4. Since I need to deploy onto SharePoint, I have thought that, its better idea to put this dll into GAC. So, from the properties of the Project, under Signing tab, create a new snk and check delay sign.



5. Rename the class1.cs to CustomLoginPage.cs
6. Add a reference from .Net tab, select Windows SharePoint Services as shown below:



7. Add a reference from .Net tab and browse to Microsoft.SharePoint.ApplicationPages.dll located in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG\BIN\



8. Add a reference from COM tab, Active DS Library.



9. Add a reference from .Net tab, System.Web, System.DirectoryServices

10. In class CustomLoginPage.cs, add the following entries in the namespace section
Using Microsoft.SharePoint;
Using ActiveDs;
11. Now, we are ready to start making some changes to the class file.
12. Inherit the class from Microsoft.SharePoint.ApplicationPages.LoginPage class
13. Since we are interested to know logon details on error, I am gonna override on error event of Login page.as shown below



protected override void OnLoad(EventArgs e)
{
this.login.LoginError += new EventHandler(login_LoginError);
base.OnLoad(e);
}

void login_LoginError(object sender, EventArgs e)
{ }

14. In the above event handler, I would need to call login details if user can not logon. Hence for the simplicity, I have created a simple class and then populated data. Below show is the class.

public class ADResult
{
public ADResult() { }

private bool _locked;

public bool Locked
{
get { return _locked; }
set { _locked = value; }
}

private bool _disabled;

public bool Disabled
{
get { return _disabled; }
set { _disabled = value; }
}

private string _lastLogon;

public string LastLogon
{
get { return _lastLogon; }
set { _lastLogon = value; }
}

private int _expiresIn;

public int ExpiresIn
{
get { return _expiresIn; }
set { _expiresIn = value; }
}

private string _errorMessage;

public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}

private bool _valid;

public bool Valid
{
get { return _valid; }
set { _valid = value; }
}

}

15. Next step is create a method GetLoginDetails() which actually would query Active Directory and return the ADResult object as shown below:

Also, You might wonder on one thing, why i have used SPSecurity.RunXXX(delegate()), this is because the code will not run until it is trusted in Sharepoint, hence it is required to elevate code security level.


private ADResult GetLoginDetails(string username)
{
ADResult adResult = new ADResult();
try
{
string strLoginName = username;
int iPosition = strLoginName.IndexOf("\\") + 1;
strLoginName = strLoginName.Substring(iPosition);

DirectoryEntry entry = new DirectoryEntry(ADPATH); //ADPATH is path of Active dir
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + strLoginName + ")";

Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate()
{
SearchResult result = search.FindOne();
entry = result.GetDirectoryEntry();
});

try
{
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate()
{
object o = entry.InvokeGet("IsAccountLocked");

if (o != null)
{
bool locked = (bool)o;
adResult.Locked = locked;
};

bool isDisabled;
isDisabled = ((int)entry.Properties["userAccountControl"].Value & (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) != 0;
adResult.Disabled = isDisabled;

object lastlogon = entry.InvokeGet("LastLogin");
if (lastlogon != null)
{
DateTime LastLogon = (DateTime)lastlogon;
adResult.LastLogon = "Last Logon was at : " + LastLogon.ToLongDateString() + " " + LastLogon.ToShortTimeString();
}


LargeInteger liAcctPwdChange = entry.Properties["pwdLastSet"].Value as LargeInteger;

// Convert the highorder/loworder parts of the property pulled to a long.
long dateAcctPwdChange = (((long)(liAcctPwdChange.HighPart) << 32) + (long)liAcctPwdChange.LowPart);

// Convert FileTime to DateTime and get what today's date is.
DateTime dtNow = DateTime.Now;
DateTime dtAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange);
string strAcctPwdChange = DateTime.FromFileTime(dateAcctPwdChange).ToString();
string strAcctPwdExpires = DateTime.FromFileTime(dateAcctPwdChange).ToString();

// Calculate the difference between the date the pasword was changed, and what day it is now and display the # of days.
TimeSpan time;
time = dtNow - dtAcctPwdChange;

adResult.ExpiresIn = time.Days;
adResult.Valid = true;
}
);
}
catch (Exception ex)
{
adResult.ErrorMessage = ex.Message;
}
}
catch (Exception e)
{
adResult.ErrorMessage = e.Message;
}

return adResult;
}

16. That’s it, and finally I would need to build UI from the object returned. Hence I have created BuildLoginDetails(ADResult reault) method to build UI as shown below:


private string BuildLoginDetails(ADResult adResult)
{

if (!adResult.Valid)
{
return string.Empty;
}

StringBuilder sb = new StringBuilder();

if (adResult.Disabled)
{
sb.Append("Account Disabled");
}
else
{
if (adResult.Locked)
{
sb.Append("Account Locked");
}

if (adResult.ExpiresIn == 0 && !adResult.Locked)
{
sb.Append("Password Expired: ");
sb.Append("Change Password");
}
else if (!adResult.Locked)
{
sb.Append(String.Format("
Password expires in {0} days", adResult.ExpiresIn));
}

sb.Append("
" + adResult.LastLogon + "");
}

return sb.ToString();
}


17. From the string returned from the above method, can be displayed on UI with a simple label in the event handler as shown below:



void login_LoginError(object sender, EventArgs e)
{
System.Web.UI.WebControls.Label lblDetails = new System.Web.UI.WebControls.Label();
lblDetails.Text = this.BuildLoginDetails(this.GetLoginDetails(this.login.UserName));
this.login.Controls.Add(lblDetails);
}


18. Now, build the class library, and drop into GAC.
19. Finally, Login page should be created. Navigate to C:\Program files\Common files\Microsoft shared\web service extensions\12\layouts folder . Copy Login page and paste it as CustomLogin.aspx.. Couple of changes needed on the CustomLogin.aspx.
No one is , The aspx page should inherit from CustomLoginPage.cs which we had created earlier.

<%@ Page Language="C#" Inherits="SharePoint.Authentication.Custom.ActiveDirectory.CustomLoginPage" MasterPageFile="~masterUrl/default.master" %>


Second is, add an assembly entry to use the dll we had created. As



<%@ Assembly Name=" SharePoint.Authentication.Custom.ActiveDirectory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12bc1ebe5d50d609"%>



in your case the PublicKeyToken will be different, make sure you copy right token from GAC or by using good old friend .Net Reflector

Third is, Importing namespaces. Add below shown entries.


<%@ Import Namespace="ActiveDs" %>


20. We have now developed CustomLogin page. Next part is customising SharePoint Application to use the Custom Login page.
21. The following steps provide modification needed to Web.config file for using ASP.NET forms authentication to use an Active Directory service membership provider.

1.Open web.config file from the path: c:\Inetpub\wwwroot\web.config and add below shown entries under section of web.config file


<connectionStrings>
<add name="ADConnectionString"
connectionString=
" LDAP://ptr.headquarters.com/DC=ptr,DC=headquarters,DC=com" />
</connectionStrings>

2. And add below shown membership provider under section


<membership defaultProvider="ADirectoryMembershipProvider">
<providers>
<add connectionStringName="ADConnectionString" name="ADirectoryMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
enableSearchMethods="true" attributeMapUsername="sAMAccountName"
/>
</providers>
</membership>

4. From the Central Administration page of WSS 3.0, navigate to Application management.
5. Create New Intranet application from the link Create/Extend New Web Application as shown in fig below:






6. Now, Extend this Intranet application to external users by clicking on Application Management\Create new or extend web application link from the Central Administration Page.
7. The details should be provided in order to extend the intranet application is shown fig below:




8. Click OK to continue to extend web application.
9. Navigate to Application Management\Authentication Providers and select Extranet Zone and provide details as show in fig below:



10. Click Ok to continue.
11. open IIS Manager, select the Web site choosen for authentication, go to properties by Right click. And then to ASP.Net tab. Click on Edit Configuration settings and move to Authentication tab. On this screen, modify Login.aspx to CustomLogin.aspx as show in screen shot.




by doing so, we are instructing the Membership provider to use CustomLogin.aspx. and the rest IIS.

22. From the browser, enter the URL of extranet web application, which should display the login screen as shown below:



for some reasons, user can not logon, the following UI would be displayed.



I think this article make sense to implement and delve challenges.

ufff.. thts it..!
In next part, will explain how to implement change password, until then bye..







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.