Tuesday, September 29, 2009

How to build an Ajax Schedule control (Downloadable source code)

New PainControls are released. You can download it from:

http://cid-79833c0a838434be.skydrive.live.com/self.aspx/PainControls/PainControls%203.0.0.0929.rar

It contains the following Ajax Controls:
TimePicker
DropDown
DropPanel
Simple Schedule
ButtonList

About Schedule:


Please check the following image:




1. The schedule is bound on DataSource control. You can select a date in left clanedar, it will retrieve the data from DataSource, and present the related task.
2. Each task will be presented in a DropPanel. You can Drag & Drop it to another cell(day). It will be updated.
3. When you mouse move on the droppanel, it will show CloseButton so that you can remove this task.
4. When mouse over the cell of schedule, it will expend itself by design if it has more task needs to show.
5. In left calendar, it will highlight the date which has tasks.
6. Since it is an Ajax ScriptControl, you can use custom css style at will.
7. Schedule used ButtonList and DropPanel in Paincontrols I mentioned before.
8. You need build and bind a web service file on this control to achieve updating and deleting function asychronously.

***** It is a simple schedule. I just want to share how to build a schedule and hope it can help. In next time, I'd like to expend the following functionality:
1. Besides "Month" display mode, "Day" display mode is needed.
2. Recently, you can create a DetailView to insert a new task, since Schedule is bound on DataSource. Next time, I'd like to expend the internal insert functionality.

If you have more suggestions, please let me know. Thanks.

How to use PainControl Schedule:

The following sample can help you to use Schedule control:


<Pain:Schedule ID="Schedule" runat="server" AutoPostBack="true" DataSourceID="SqlDataSource1" KeyField="num"
DateTimeFieldName="date_time" TitleFieldName="title" DescriptionFieldName="description" ServicePath="ScheduleWebService.asmx"
UpdateServiceMethod="UpdateWebService" DeleteServiceMethod="DeleteWebService" />
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:DatabaseConnectionString %>"
SelectCommand="SELECT * FROM [schedule]"></asp:SqlDataSource>

Property:
1. DataSourceID ---- Create a DataSource and bind it on Schedule.
2. KeyField ---- The field name of primary key of DataSource.
3. There are three mandatory field you have to create:
DateTimeFieldName ---- related field name about the datetime of the task
TitleFieldName ---- related field name about the title of the task
DescriptionFieldName ---- related field name about the description of the task
4. ServicePath ---- If you have used AjaxControlToolkit, you must be familar with this property. It means the path of the web service file.
UpdateServiceMethod ---- The web method name to execute updating function
DeleteServiceMethod ---- The web method name to execute deleting function

Build a web service bound to achieve updating and deleting function

The following demo snippets are the web methods of updating function and delete function.
In the web method of updating function:
"key" is the primary key that will be updated.
"updateFieldName" is the field name that will be updated.
"updateValue" is the related value which it is updated to about "updateFieldName".

In the web method of deleting function,
just need "key" that is the primary key that will be deleted.

[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public void UpdateWebService(string key, string updateFieldName, string updateValue)
{
string constr = (string)ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString;
string sql = "update schedule set " + updateFieldName + "='" + updateValue+"' where num="+key;
SqlConnection connection = new SqlConnection(constr);
SqlCommand sdc = new SqlCommand(sql, connection);
sdc.CommandType = CommandType.Text;
try
{
connection.Open();
sdc.ExecuteScalar();
}
catch (SqlException SQLexc)
{
throw new Exception(SQLexc.Message);
}
finally
{
connection.Close();
}
System.Threading.Thread.Sleep(2000);

}

[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public void DeleteWebService(string key)
{

string constr = (string)ConfigurationManager.ConnectionStrings["DatabaseConnectionString"].ConnectionString;
string sql = "delete from schedule where num=" + key;
SqlConnection connection = new SqlConnection(constr);
SqlCommand sdc = new SqlCommand(sql, connection);
sdc.CommandType = CommandType.Text;
try
{
connection.Open();
sdc.ExecuteScalar();
}
catch (SqlException SQLexc)
{
throw new Exception(SQLexc.Message);
}
finally
{
connection.Close();
}
System.Threading.Thread.Sleep(2000);
}


In Short:
To use this Schedule button, you need use html tag to create Schedule, create a DataSource control to bind on Schedule and build two web methods to achieve updating and deleting functions.

Build a ButtonList (Ajax Control)

New PainControls are released. You can download it from:

http://cid-79833c0a838434be.skydrive.live.com/self.aspx/PainControls/PainControls%203.0.0.0929.rar

It contains the following Ajax Controls:

TimePicker
DropDown
DropPanel
Simple Schedule
ButtonList

About ButtonList:
Check the following image.


There are three item button you can select. It is like a DropDownList, but it's composted of several Button. You can select one of the items, and it will call SelectedIndexChanged event.

You can use this control by the following code.

<Pain:ButtonList runat="server" ID="ButtonList1" OnSelectedIndexChanged="ButtonList1_SelectedIndexChanged" >
<asp:ListItem>day</asp:ListItem>
<asp:ListItem>month</asp:ListItem>
<asp:ListItem>year</asp:ListItem>
</Pain:ButtonList>

Thursday, August 20, 2009

UpdateProgress is not working with AssociatedUpdatePanelID

Somebody found the UpdateProgress isn't working if it sets the property "AssociatedUpdatePanelID". Actually, when the Trigger control is outside UpdatePanel, and AssociatedUpdatePanelID of UpdateProgress is pointing to this UpdatePanel, the UpdateProgress won't be working.In the following sample, UpdateProgress AssociatedUpdatePanelID is pointing to UpdatePanel1, and Button1 which is the trigger control is outside UpdatePanel1. When you click the Button1, UpdateProgress will not be working.


<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
...Updating
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" ChildrenAsTriggers="false" UpdateMode="Conditional">
<ContentTemplate>
Current Time <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label><br />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />


If Button1 is inside UpdatePanel1, the value of postBackSettings.PanelID will be generated to "UpdatePanel1Button1". AssociatedUpdatePanelID will be checked with postBackSettings.PanelID. If they are equel, UpdateProgress will be displayed.If Button1 is outside UpdatePanel1,the value of postBackSettings.PanelID will be generated to "ScriptManager1Button1". AssociatedUpdatePanelID is not matched with it, so it can't pop out UpdateProgress.In this case, we have to show UpdateProgress manually when the value of get_postBackElement.id is button1.

http://www.asp.net/AJAX/Documentation/Live/ViewSample.aspx?sref=UpdateProgressTutorialIntro11

Thursday, July 23, 2009

Asp.Net Ajax Extender Control Tutorial

I built several Extender Control and Script Control in my provious posts within PainControls assembly, which you can download. You can search the resource about how to build extender control or script control on web. However, I'd still like to write something on it in my words. If you are interesting on that, you can check this article.

Microsoft ASP.Net Ajax Extensions enables you to expand the capabilities of an ASP.Net Web Application in order to create a rich client user experience. We can make use of ScriptControl or ExtenderControl to build a rich client behavior or web control. The difference between ExtenderControl and ScriptControl is that Extender is used on creating client script capabilities on an existing control, which is called "TargetControl" for this behavior, whereas ScriptControl is an absolute web control which contains rich client functionality. For example, when we'd like to build a ModalPopup which will pop out an existing Panel, show/hide functionality is the client script application, then we can build it as ExtenderControl. However, for ScriptControl, for instance, TabContainer which is the entirely new web control contains the client script functionality, so we can build it as ScriptControl.

1. To encapsulate the client behavior for use by ASP.NET page developers, you can use an extender control. An extender control is a Web server control that inherits the ExtenderControl abstract class in the System.Web.UI namespace.
Extender control is used for client script functionality extension of an existing web control. It can be applied to specific Web server control types. You identify the types of Web server controls to which an extender control can be applied by using the TargetControlTypeAttribute attribute.
(The sample code as below is according to the TimePicker which is from http://vincexu.blogspot.com/2009/07/ajax-timepickerclockpicker-control.html)

[TargetControlType(typeof(Control))]
public class TimePicker: ExtenderControl

2. The following two methods of the ExtenderControl abstract class that you must implement in an extender control.

protected override IEnumerable GetScriptDescriptors(Control targetControl)
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor("PainControls.TimePicker", targetControl.ClientID);
descriptor.AddElementProperty("errorSpan", this.NamingContainer.FindControl(ErrorPresentControlID).ClientID);
descriptor.AddProperty("timeType", TimeType);
descriptor.AddEvent("showing", OnClientShowing);
yield return descriptor;

}
protected override IEnumerable GetScriptReferences()

{
yield return new ScriptReference(Page.ClientScript.GetWebResourceUrl(this.GetType(), "PainControls.TimePicker.TimePicker.js"));
}

3. Embed Css reference in PreRender phase.

private void RenderCssReference()
{
string cssUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "PainControls.TimePicker.TimePicker.css");
HtmlLink link = new HtmlLink();

link.Href = cssUrl;
link.Attributes.Add("type", "text/css");
link.Attributes.Add("rel", "stylesheet");
Page.Header.Controls.Add(link);
}

4. Set all resources(contain images, css file and js file) embedded in this extender control as "Embedded Resource"(property "Build Action").

5. This extender control can derive from IExtenderControl interface and a server control, instead of ExtenderControl if you'd like to.

The control can derive from other server controls if you want to make it inherit a server control than ExtenderControl. In this scenario, it should derive from IExtenderControl interface and a server control class. Meanwhile, we have another three steps need to do:
1) Define TargetControl property
2) Override OnPreRender method. Register the web control as the ExtenerControl in OnPreRender phase.
ScriptManager manager = ScriptManager.GetCurrent(this.Page);
if (manager == null)
{
throw new InvalidOperationException("A ScriptManager is required on the page.");
}
manager.RegisterExtenderControl(this);
3) Override Render method. Register the script descriptor which has been defined.
ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);

6. The rest work is on client-side. Register client NameSpace first.

Type.registerNamespace("PainControls");

7. Build client class.

PainControls.TimePicker = function(element)
{
}
PainControls.TimePicker.prototype = {
}


8. Register the class that inherits "Sys.UI.Behavior".

PainControls.TimePicker.registerClass('PainControls.TimePicker', Sys.UI.Behavior);

9. Call base method in constructor method

PainControls.TimePicker.initializeBase(this, [element]);

10. Implementing the Initialize and Dispose Methods.
Build "initialize" and "dispose" method in prototype of the class. The initialize method is called when an instance of the behavior is created. Use this method to set default property values, to create function delegates, and to add delegates as event handlers. The dispose method is called when an instance of the behavior is no longer used on the page and is removed. Use this method to free any resources that are no longer required for the behavior, such as DOM event handlers.

initialize: function() {
PainControls.TimePicker.callBaseMethod(this, 'initialize');
},
dispose: function() {

PainControls.TimePicker.callBaseMethod(this, 'dispose');
}

11. Defining the Property Get and Set Methods.
Each property identified in the ScriptDescriptor object of the extender control's GetScriptDescriptors(Control) method must have corresponding client accessors. The client property accessors are defined as get_<:property> and set_<:property> methods of the client class prototype.

get_timeType: function() {
return this._timeType;
},
set_timeType: function(val) {

if (this._timeType !== val) {
this._timeType = val;
this.raisePropertyChanged('timeType');
}
},

12. Defining the Event Handlers for the DOM Element
1) Defining the handler in constructor function:
this._element_focusHandler = null;
2) Associate the handler with the DOM Element event in initailize method:
this._element_focusHandler = Function.createDelegate(this, this._element_onfocus);
3) Add the handler in initailize method:
$addHandler(this.get_element(), 'focus', this._element_focusHandler)
4) Build callback method about this event:
_element_onfocus:function(){
}


13. Defining the Event Handlers for the behavior
Each event identified in the ScriptDescriptor object of the extender control's GetScriptDescriptors(Control) method must have corresponding client accessors. The client event accessors are defined as add_<:event> and remove_<:event> methods of the client class prototype. The method Raise<:event> is defined to trigger the event.

add_showing: function(handler) {
this.get_events().addHandler("showing", handler);
},
remove_showing: function(handler) {
this.get_events().removeHandler("showing", handler);

},
raiseShowing: function(eventArgs) {
var handler = this.get_events().getHandler('showing');

if (handler) {
handler(this, eventArgs);
}
},

14. Use this extender control TimePicker in page.
1) Register the assembly in page.


<%@ Register TagPrefix="PainControls" Assembly="PainControls" Namespace="PainControls" %>


2) Add a ScriptManager control in page, and create TimePicker control to bind on a TextBox.


<asp:textbox id="TextBox1" runat="server" text=""></asp:textbox>
<PainControls:timepicker id="t1" runat="server" targetcontrolid="TextBox1" timetype="H24">

Friday, July 17, 2009

AjaxControlToolkit ComboBox not showing in TabContainer

We have been refered that combobox is not appearing in ModalPopup: http://vincexu.blogspot.com/2009/05/ajaxcontroltoolkit-combobox-not.html

But somebody found that it also will pop out script error on the line b.width=c+"px" if combobox is in the TabPanel which is not the default active panel in tabcontainer.

For example, we have 2 combobox controls, one is in the TabPanel1, and the other is in TabPanel2. TabPanel1 is the default active tabPanel. After initialized, it will pop out the error, then combobox1 in tabpanel1 is displaying, but combobox2 not.

Check the following sample about it.


<ajaxToolkit:TabContainer ID="TabContainer1" runat="server" Height="228px"
Width="400px">
<ajaxToolkit:TabPanel ID="tabpane1" runat="server" HeaderText="tab1">
<ContentTemplate>
<ajaxToolkit:ComboBox runat="server" ID="ComboBox1" MaxLength="100" AutoCompleteMode="Suggest">
<asp:ListItem Text="ABC" Value="ABC"></asp:ListItem>
<asp:ListItem Text="XYZ" Value="XYZ"></asp:ListItem>
</ajaxToolkit:ComboBox>
</ContentTemplate>
</ajaxToolkit:TabPanel>
<ajaxToolkit:TabPanel ID="TabPanel2" runat="server" HeaderText="Tab2">
<ContentTemplate>
<ajaxToolkit:ComboBox runat="server" ID="ComboBox2" MaxLength="100" AutoCompleteMode="Suggest">
<asp:ListItem Text="ABC" Value="ABC"></asp:ListItem>
<asp:ListItem Text="XYZ" Value="XYZ"></asp:ListItem>
</ajaxToolkit:ComboBox>
</ContentTemplate>
</ajaxToolkit:TabPanel>
</ajaxToolkit:TabContainer>

(You can try this sample. Because Combobox2 is in the TabPanel2, which is an inactive panel, it will pop out the error and it will make Combobox2 off.)

This issue is attributable to the tabpanel which is not active is invisible, combobox can't get the correct offsetHeight and offsetWidth any more.

So, the approach to resolve it is setting the TabPanel2 to be visible so that combobox can create in gear.
Combobox will be created in initialized phase. So we have to set TabPanel2 to be visible before combobox creates and set it back in pageLoad phase.


<script type="text/javascript">
$get('<%=TabPanel2.ClientID %>').style.visibility = "visible";
$get('<%=TabPanel2.ClientID %>').style.display = "block";

function pageLoad() {
$get('<%=TabPanel2.ClientID %>').style.visibility = "hidden";
$get('<%=TabPanel2.ClientID %>').style.display = "none";
}

</script>


Put the above script after document body and form content. It will let Combobox1 and combobox2 showing according to he above HTML sample.

Cheer.

Thursday, July 16, 2009

Ajax TimePicker/ClockPicker control

Hi,

Last time I made a Custom DropDown. Please check the following image about this Ajax control.


Blog link: http://vincexu.blogspot.com/2009/06/about-ajaxcontroltoolkit-combobox.html

It introduces how to build a DropDown/ComboBox by Asp.Net Ajax ScriptControl. You can download this control in above link.


I made a TimePicker/ClockPicker this time, hope it can help you. See the following image about this ajax TimePicker.



You can drag the hour/minute pointer to select a time. It supports 12h and 24h format.

You can download from:


(PainControls contain dropdown and timepicker so far)


If you'd like to use this ajax control, please check the below:


1.Add the reference PainControls.dll, and check the sample code as below.


<asp:TextBox ID="TextBox1" Text="" runat="server"></asp:TextBox> <Pain:TimePicker runat="server" ID="t1" TargetControlID="TextBox1" TimeType="H24" />
Properties:
TargetControl: which TextBox(only TextBox) is bound on TimePicker
TimeType: H24/H12 -- Time format
ErrorPrensentControlID: the control can display the time validation information. You can assign it as some span or label to bind on it.
And there are several client Events you can use:
OnClientShowing : client showing event
OnClientShown : client shown event
OnClientHiding: client hiding event
OnClientHidden: client hidden event
OnClientHourSelectionChanged: it triggers when you select new hour value by mouse. It will trigger when mouse up.
OnClientMinuteSelectionChanged: it triggers when you select new minute value by mouse. It will trigger when mouse up.
2. You need use FrameWork 3.5 sp1 to support this control. (You can recompile the files if you want to use in FrameWork 2.0)
3. Don't forget adding a ScriptManager control in the page.
4. If you want to set a default Time for TimePicker, please set a default value in TextBox. It will reset the time according to the value of TextBox bound.

Monday, June 29, 2009

About AjaxControlToolkit ComboBox

ComboBox is a new contorl in AjaxControlToolkit. In this time, let's talk about what's the ComboBox, and how to build a ComboBox control in ASP.Net. (You can see the structure of the ComboBox which I ever build: http://vincexu.blogspot.com/2008/11/build-combobox-in-aspnet.html)

1. ComboBox is not a DropDownList. It is built by a TextBox(the area you input), a Button(the button you can click to pop out the list below) and a BulletedList(option list) which is the kernel part in this Composite control.

2. ComboBox is deriving from ListControl, so you can make use of some features of ListControl to build the option List functionality, such as SelectedIndex, SelectedValue, DataSource and so on. These properties and functionality of ListControl can help us to build a combo list box conveniently.

3. The option list is built by a BulletedList, which will render to "ul" and "li" elements on client. So each item in optionlist is a "li" element. (in DropDownList, it will render to "select" element and "option" element for each item.)

4. After binding datasource on comboBox(it is a just ListControl now), the items will be stored into ComboBox.items container. To add these items to optionlist, We need retrieve these items from ComboBox and set them on optionList(BulletedList) in render phase.

5. Since OnSelectedItemsChanged event is not working in BulletedList, we have to realize the IPostBackDataHandler interface to support the SelectedItemsChanged event.

6. We need to override AutoPostBack and SelectedIndex attributes of ListControl in ComboBox. SelectedIndex should get the active slected item index, so we have to create an additional HiddenControl to save the selectedIndex user selects. When the user select a new item, we can use script to alter the value in HiddenControl. After postback to the server-side, we can get the value from this HiddenControl. Do SelectedIndex attribute should be bound on the value on this HiddenControl, which is used for recording the active item index.AutoPostBack is an attribute related to the client-side property autoPostBack, which is used for if comboBox will do postback.

7. The above all are what we should do on server side. Then we need to realize client functionality.
1). HighLight functionality of item. We need to build onmouseover for optionList to achieve highlighting functionSee _onListMouseOver function in comboBox.debug.js.

2). To comfirm the item has been selected, we need to build onmousedown do so. See _onListMouseDown function. (If you need build client-side selectedItemChanged, you can override this function.)

3). Once TextBox gets focus, it will check if it need do postback. See _onTextBoxFocus function.

4). Besides the above basic fucntions to build, we need to build auto suggesting functionality of TextBox, and show/hide optionlist on button clicking. That's all based on client side script.

To help you known about ComboBox, I build a simple DropDown which is a ScripControl(It looks like DropdownList, but we can set any Css for it to make it more beautiful). Hope it can help you.

DropDown.cs


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
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.Xml.Linq;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Globalization;

[assembly: WebResource("PainControls.DropDown.DropDown.js", "application/x-javascript")]
[assembly: WebResource("PainControls.DropDown.DropDown.css", "text/css", PerformSubstitution=true)]
[assembly: WebResource("PainControls.DropDown.menuOutPut.gif", "image/gif")]
[assembly: WebResource("PainControls.DropDown.menuLi.gif", "image/gif")]

/////////////We need set the these files as Embedded Resource //////////////
namespace PainControls
{

public class DropDown : ListControl, IScriptControl,IPostBackDataHandler,INamingContainer
{
public DropDown()
{

}

#region IScriptControl Member
private string GetClientID(string controlId)
{
return this.FindControl(controlId).ClientID;
}

public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor("PainControls.DropDown", this.ClientID);
descriptor.AddElementProperty("dropDownOutPutElement", DropDownOutPutElement.ClientID);
descriptor.AddElementProperty("dropDownListElement", DropDownListElement.ClientID);
descriptor.AddElementProperty("dropDownOptionList", DropDownOptionList.ClientID);
descriptor.AddElementProperty("dropDownHiddenField", DropDownHiddenField.ClientID);

descriptor.AddProperty("autoPostBack", AutoPostBack);
descriptor.AddProperty("selectedIndex", SelectedIndex);
descriptor.AddProperty("listItemHighLightCssClass", ListItemHighLightCssClass);
descriptor.AddProperty("listItemCssClass", ListItemCssClass);




yield return descriptor;
}

// Generate the script reference
public IEnumerable<ScriptReference> GetScriptReferences()
{
yield return new ScriptReference(Page.ClientScript.GetWebResourceUrl(this.GetType(), "PainControls.DropDown.DropDown.js"));
}
#endregion



#region Css Property

public virtual string ListItemHighLightCssClass
{
get
{
return "listItemHighLight";
}
}

public virtual string ListItemCssClass
{
get
{
return "listItem";
}
}

#endregion

#region List Properties

public override int SelectedIndex
{
get
{
int selectedIndex = base.SelectedIndex;
if ((selectedIndex < 0) && (this.Items.Count > 0))
{
this.Items[0].Selected = true;
selectedIndex = 0;
}
return selectedIndex;
}
set
{
base.SelectedIndex = value;
DropDownHiddenField.Value = value.ToString();

}
}

public override bool AutoPostBack
{
get
{
return base.AutoPostBack;
}
set
{
base.AutoPostBack = value;
}
}

#endregion

#region Child Controls

private HtmlGenericControl _DropDownOutPutElement;
private HtmlGenericControl _DropDownListElement;
private BulletedList _DropDownOptionList;
private HiddenField _DropDownHiddenField;
protected virtual HtmlGenericControl DropDownOutPutElement
{
get
{
if (_DropDownOutPutElement == null)
_DropDownOutPutElement = new HtmlGenericControl("Div");

return _DropDownOutPutElement;
}
}

protected virtual HtmlGenericControl DropDownListElement
{
get
{
if (_DropDownListElement == null)
_DropDownListElement = new HtmlGenericControl("Div");
return _DropDownListElement;
}
}
protected virtual BulletedList DropDownOptionList
{
get
{
if (_DropDownOptionList == null)

_DropDownOptionList = new BulletedList();

return _DropDownOptionList;
}
}
protected virtual HiddenField DropDownHiddenField
{
get
{
if (_DropDownHiddenField == null)
_DropDownHiddenField = new HiddenField();
return _DropDownHiddenField;
}
}
#endregion

#region Create Child Controls

protected override void CreateChildControls()
{

this.Controls.Clear();
CreateDropDownOutPutElement();
CreateDropDownListElement();
CretaeDropDownOptionList();
CreateDropDownHiddenField();
base.CreateChildControls();

}

private void CreateDropDownOutPutElement()
{

this.Controls.Add(DropDownOutPutElement);
}
private void CreateDropDownListElement()
{

DropDownListElement.Style.Add(HtmlTextWriterStyle.Display, "none");
this.Controls.Add(DropDownListElement);
}
private void CretaeDropDownOptionList()
{

DropDownListElement.Controls.Add(DropDownOptionList);
}
private void CreateDropDownHiddenField()
{
DropDownListElement.Controls.Add(DropDownHiddenField);
}

#endregion

#region Render Methods

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
AddDropDownOutPutElementAttributesToRender(writer);
AddDropDownListElementAttributesToRender(writer);
AddDropDownOptionListAttributesToRender(writer);
}

protected virtual void AddDropDownOutPutElementAttributesToRender(HtmlTextWriter writer)
{

DropDownOutPutElement.InnerHtml = this.Items[SelectedIndex].Text;
DropDownOutPutElement.Attributes.Add("class", "dropDownOutPutElement");
}

protected virtual void AddDropDownListElementAttributesToRender(HtmlTextWriter writer)
{

DropDownListElement.Attributes.Add("class", "dropDownListElement");
}

protected virtual void AddDropDownOptionListAttributesToRender(HtmlTextWriter writer)
{

DropDownOptionList.CssClass = "dropDownOptionList";
}


protected override HtmlTextWriterTag TagKey
{
get { return HtmlTextWriterTag.Div; }
}


// Add Css reference
private void RenderCssReference()
{
string cssUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "PainControls.DropDown.DropDown.css");

HtmlLink link = new HtmlLink();
link.Href = cssUrl;
link.Attributes.Add("type", "text/css");
link.Attributes.Add("rel", "stylesheet");
Page.Header.Controls.Add(link);
}

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
RenderCssReference();


ScriptManager manager = ScriptManager.GetCurrent(this.Page);
if (manager == null)
{
throw new InvalidOperationException("A ScriptManager is required on the page.");
}
manager.RegisterScriptControl<DropDown>(this);
Page.RegisterRequiresPostBack(this);


}
protected override void RenderContents(HtmlTextWriter writer)
{
ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);

DropDownOutPutElement.RenderControl(writer);

DropDownOptionList.Items.Clear();
ListItem[] copy = new ListItem[Items.Count];
Items.CopyTo(copy, 0);
DropDownOptionList.Items.AddRange(copy);

DropDownListElement.RenderControl(writer);
DropDownHiddenField.RenderControl(writer);

}

#endregion

#region IPostBackDataHandler Implementation


bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{
return LoadPostData(postDataKey, postCollection);
}

void IPostBackDataHandler.RaisePostDataChangedEvent()
{
RaisePostDataChangedEvent();
}

protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
if (!Enabled)
return false;

int newSelectedIndex = Convert.ToInt32(postCollection.GetValues(DropDownHiddenField.UniqueID)[0], CultureInfo.InvariantCulture);
EnsureDataBound();

if (newSelectedIndex != SelectedIndex)
{

SelectedIndex = newSelectedIndex;
return true;
}

return false;
}

public virtual void RaisePostDataChangedEvent()
{
this.OnSelectedIndexChanged(EventArgs.Empty);
}

#endregion


}
}


DropDown.js

/// <reference name="MicrosoftAjax.js"/>


Type.registerNamespace("PainControls");

PainControls.DropDown = function(element) {
PainControls.DropDown.initializeBase(this, [element]);
//element property
this._dropDownOutPutElement = null;
this._dropDownListElement = null;
this._dropDownOptionList = null;
this._dropDownHiddenField = null;
//property
this._autoPostBack = null;
this._selectedIndex = null;
this._listItemHighLightCssClass=null;
this._listItemCssClass=null;

//handler
this._outPutClickHandler = null;
this._outPutMouseOverHandler = null;
this._outPutMouseOutHandler = null;
this._listMouseOverHandler = null;
this._listMouseDownHandler = null;

//
this._highlightedIndex=null;
this._dropDownOutPutElementBorderColor=null;
this._isShown=null;
}

PainControls.DropDown.prototype = {
initialize: function() {
PainControls.DropDown.callBaseMethod(this, 'initialize');
this.initializeListItems();
this.createDelegates();
this.createHandlers();


},

initializeListItems:function(){
var children = this.get_dropDownOptionList().childNodes;
for (var i = 0; i < children.length; ++i) {

Sys.UI.DomElement.addCssClass(children[i],this.get_listItemCssClass());
}
},

dispose: function() {
//Add custom dispose actions here
PainControls.DropDown.callBaseMethod(this, 'dispose');
},

createDelegates:function(){
this._outPutClickHandler = Function.createDelegate(this, this._onOutPutClick);
this._outPutMouseOverHandler = Function.createDelegate(this, this._onOutPutMouseOver);
this._outPutMouseOutHandler = Function.createDelegate(this, this._onOutPutMouseOut);
this._listMouseOverHandler = Function.createDelegate(this, this._onListMouseOver);
this._listMouseDownHandler = Function.createDelegate(this, this._onListMouseDown);

},

clearDelegates:function(){

},

createHandlers:function(){

$addHandlers(this.get_dropDownOutPutElement(),
{
'click': this._outPutClickHandler,
'mouseover': this._outPutMouseOverHandler,
'mouseout': this._outPutMouseOutHandler
}, this);

$addHandlers(this._dropDownOptionList,
{
'mouseover': this._listMouseOverHandler,
'mousedown': this._listMouseDownHandler,

}, this);
},

clearHandlers:function(){

},

_showListElement: function() {

var loc = Sys.UI.DomElement.getLocation(this._dropDownOutPutElement);
Sys.UI.DomElement.setLocation(this._dropDownListElement, loc.x, loc.y + this._dropDownOutPutElement.offsetHeight);
this._dropDownListElement.style.display = "block";
this._isShown=true;
},

_hideListElement:function(){
this._dropDownListElement.style.display = "none";
this._isShown=false;
},


//event handle
_onOutPutClick: function(e) {
if(this._isShown)
this._hideListElement();
else
this._showListElement();
e.preventDefault();
return false;
},

_onOutPutMouseOver: function(e) {
//
this._dropDownOutPutElementBorderColor=this.get_dropDownOutPutElement().style.borderColor;
this.get_dropDownOutPutElement().style.borderColor="#F7A040";

},

_onOutPutMouseOut: function(e) {
//
if(this._dropDownOutPutElementBorderColor!=null)
this.get_dropDownOutPutElement().style.borderColor=this._dropDownOutPutElementBorderColor;
},

_onListMouseOver: function(e) {
//
if (e.target !== this.get_dropDownOptionList()) {
var target = e.target;
var children = this.get_dropDownOptionList().childNodes;

// loop through children to find a match with the target
for (var i = 0; i < children.length; ++i) {
// match found, highlight item and break loop
if (target === children[i]) {
this._highlightListItem(i, true);
break;
}
}
}

},

_onListMouseDown: function(e) {

if (e.target == this.get_dropDownOptionList() e.target.tagName == 'scrollbar') {
return true;
}

// set the TextBox to the highlighted ListItem's text and update selectedIndex
if (e.target !== this.get_dropDownOptionList()) {
if(this.get_selectedIndex() != this._highlightedIndex){
var highlightedItem = this.get_dropDownOptionList().childNodes[this._highlightedIndex];
var text = this.get_listItems()[this._highlightedIndex].text;
this.get_dropDownOutPutElement().innerHTML = text;
this.set_selectedIndex(this._highlightedIndex);


// return focus to the TextBox
this.get_dropDownOutPutElement().focus();

if(this.get_autoPostBack())
{
__doPostBack(this.get_element().id, '');

}
}
this._hideListElement();
}
else {
return true;
}
e.preventDefault();
e.stopPropagation();
return false;
},

_highlightListItem: function(index, isHighLighted){
// only highlight valid indices
if (index == undefined index < 0) {
if (this._highlightedIndex != undefined && this._highlightedIndex >= 0) {
this._highlightListItem(this._highlightedIndex, false);
}
return;
}
var children = this.get_dropDownOptionList().childNodes;
var newLiElement = children[index];
var oldLiElement = this._highlightedIndex==null?null: children[this._highlightedIndex];

if(oldLiElement!=null)
this._toggleCssClass(oldLiElement,this.get_listItemCssClass(),this.get_listItemHighLightCssClass());
this._toggleCssClass(newLiElement,this.get_listItemCssClass(),this.get_listItemHighLightCssClass());

this._highlightedIndex=index;


},

_toggleCssClass: function(element,cssClassName1,cssClassName2){

var oldClassName=element.className;
if(oldClassName!=cssClassName1 && oldClassName!=cssClassName2)
return;
var newClassName=(oldClassName==cssClassName1)?cssClassName2:cssClassName1;
Sys.UI.DomElement.removeCssClass(element,oldClassName);
Sys.UI.DomElement.addCssClass(element,newClassName);
},

//property
get_autoPostBack: function() {
return this._autoPostBack;
},

set_autoPostBack: function(val) {
if (this._autoPostBack !== val) {
this._autoPostBack = val;
this.raisePropertyChanged('autoPostBack');
}
},
get_selectedIndex: function() {
this._ensureSelectedIndex();
var selectedIndex = this.get_dropDownHiddenField().value;
return parseInt(selectedIndex);
},

set_selectedIndex: function(val) {
if (this.get_dropDownHiddenField().value !== val.toString()) {
this.get_dropDownHiddenField().value = val.toString();
this._ensureSelectedIndex();
this.raisePropertyChanged('selectedIndex');
}
},

_ensureSelectedIndex: function() {

// server may not always invoke set_selectedIndex(), need to make sure this is always an integer
var selectedIndex = this.get_dropDownHiddenField().value;
if (selectedIndex == '') {
selectedIndex = this.get_listItems().count > 0 ? 0 : -1;
this.get_dropDownHiddenField().value = selectedIndex.toString();
}
},

get_listItemHighLightCssClass:function(){
return this._listItemHighLightCssClass;
},

set_listItemHighLightCssClass:function(val){
if (this._listItemHighLightCssClass !== val) {
this._listItemHighLightCssClass = val;
this.raisePropertyChanged('listItemHighLightCssClass');
}
},

get_listItemCssClass:function(){
return this._listItemCssClass;
},

set_listItemCssClass:function(val){
if (this._listItemCssClass !== val) {
this._listItemCssClass = val;
this.raisePropertyChanged('listItemCssClass');
}
},

get_listItems: function() {
var items = new Array();
var childNodes = this.get_dropDownOptionList().childNodes;
for (var i = 0; i < childNodes.length; i++) {
var obj = new Object();
obj.text = childNodes[i].innerHTML.trim();
Array.add(items,obj);
}
return items;

},

get_dropDownOutPutElement: function() {
return this._dropDownOutPutElement;
},

set_dropDownOutPutElement: function(val) {
if (this._dropDownOutPutElement !== val) {
this._dropDownOutPutElement = val;
this.raisePropertyChanged('dropDownOutPutElement');
}
},

get_dropDownListElement: function() {
return this._dropDownListElement;
},

set_dropDownListElement: function(val) {
if (this._dropDownListElement !== val) {
this._dropDownListElement = val;
this.raisePropertyChanged('dropDownListElement');
}
},

get_dropDownOptionList: function() {
return this._dropDownOptionList;
},

set_dropDownOptionList: function(val) {
if (this._dropDownOptionList !== val) {
this._dropDownOptionList = val;
this.raisePropertyChanged('dropDownOptionList');
}
},

get_dropDownHiddenField: function() {
return this._dropDownHiddenField;
},

set_dropDownHiddenField: function(val) {
if (this._dropDownHiddenField !== val) {
this._dropDownHiddenField = val;
this.raisePropertyChanged('dropDownHiddenField');
}
},



}
PainControls.DropDown.registerClass('PainControls.DropDown', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();



If you would like to download this DropDown ScriptControl, click the following.