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.

Friday, June 12, 2009

Dynamically register UpdatePanel Trigger

Two approaches can be used to register the UpdatePanel Trigger:

1. At code behind, we can use UpdatePanel.Triggers.Add to add a dynamic trigger.

AsyncPostBackTrigger Trigger1 = new AsyncPostBackTrigger();
Trigger1.ControlID = "Control UniqueID"; //it's UniquelID
Trigger1.EventName = "Click";
UpdatePanel1.Triggers.Add(Trigger1);


That's the way always popular to do when we go with UpdatePanel.


2. Sometimes, we need use client-side approach to register Triggers on the fly that will be more firsthand and more convenient.

Check the following Script code:

Sys.WebForms.PageRequestManager.getInstance()._updateControls(["t"+updatepanelid],
[AsyncPostBackTriggers1_ID, AsyncPostBackTriggers2_ID], [PostBackTriggers1_ID, PostBackTriggers2_ID], 90);// Here ID is UniqueID.


The above code can register the control as the trigger of UpdatePanel. That is real what the Ajax framework is doing. When you check the source code of ajax-enable page, you can find this kind of code.

Let's research this method step on step:
1.This method is to register AsyncPostBackTrigger and PostBackTrigger on client.
2.In the first parameter, it is composted by "t"/"f" and UpdatePanel ID. "t"/"f" means if ChildrenAsTriggers is true or false.
3.In the second parameter, it is an array list of asyncpostback triggers' Unique ID.
3.In the third parameter, it is an array list of postback triggers' Unique ID.
4.In the last parameter, it is the timeout attribute.

--------------------------------------------------------------------------------------

You can choose one of these two approaches to achieve registering Trigger dynamically. However, with this approach, we can get some interesting purpose. Please check the following sample.


I have a DropdownList inside UpdatePanel with Button. When I select the first item, Button will be as PostBack Trigger and do full postback. When I select the second item, Button will be as AsyncPostBack Trigger and do partial postback. So I can do validation to decide to do as PostBack Trigger or AsyncPostBack Trigger dynamically.

<script type="text/javascript">


function check() {
var button1 = '<%=Button1.UniqueID %>';
var dropDownList1 = $get('<%=DropDownList1.UniqueID %>');
var updatePanel = '<%= UpdatePanel1.UniqueID %>';
if (dropDownList1.selectedIndex == 0)
Sys.WebForms.PageRequestManager.getInstance()._updateControls(["t" + updatePanel], [], [button1], 90); //PostBack

else
Sys.WebForms.PageRequestManager.getInstance()._updateControls(["t" + updatePanel], [button1], [], 90); //AsyncPostBack

}
</script>

<body>

<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem>do PostBack</asp:ListItem>
<asp:ListItem>do asyncPostBack</asp:ListItem>
</asp:DropDownList>
<%=DateTime.Now %>
<asp:Button ID="Button1" runat="server" Text="Button" OnClientClick="check();" />
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>

Thursday, June 11, 2009

Keep the TextBox Focus inside UpdatePanel

At this time, let's talk about the old issue -- UpdatePanel Focus problem
Assuming that you have a TextBox inside UpdatePanel, when you set an Ajax Timer for this UpdatePanel or set TextChanged event of TextBox in order to make the TextBox do postback, you will find it losts focus from TextBox after you type something and do postback.
For instance, please check the following sample:

<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged"

AutoPostBack="true"></asp:TextBox>
<asp:Label runat="server" Text="" ID="Label1"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>



protected void TextBox1_TextChanged(object sender, EventArgs e)
{
Label1.Text="you inputed " + TextBox1.Text;
//ScriptManager1.SetFocus(TextBox1);

}


In above sample, TextBox will lost focus after you press enter and doing postback.

As a solution, We can use ScriptManager1.SetFocus(TextBox1) to focus the Textbox1 again after postback. As a result, it gets the focus, but the cursor in TextBox is changed and initialized(It goes to the first charactor location, instead of keeping to the current location).

To resolve this problem so that it can keep the current location in Textbox after postback, we can use the following JavaScript to set the location in TextBox manually.


function setfocus() {
var txb = $get("<%=TextBox1.ClientID%>");
var t = txb.createTextRange();
t.collapse(true);

t.moveStart("character", txb.value.length);
t.select();

}


In addition, we need use JavaScript to store the current location in TextBox before postback and set it back after postback. See the entire sample as below.


<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged"

AutoPostBack="true"></asp:TextBox>
<asp:Label runat="server" Text="" ID="Label1"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>

<script type="text/javascript">
var currentCaret;
function getCaret(textbox) {
var textbox = $get("<%=TextBox1.ClientID%>");
var control = document.activeElement;
textbox.focus();
var rang = document.selection.createRange();
rang.setEndPoint("StartToStart", textbox.createTextRange());
return rang.text.length;
}


function setfocus() {
var txb = $get("<%=TextBox1.ClientID%>");
var t = txb.createTextRange();
t.collapse(true);

t.moveStart("character", currentCaret);
t.select();

}

Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(
function(sender, e) {
currentCaret = getCaret($get("<%=TextBox1.ClientID%>"));

});
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(
function(sender, e) {

setfocus();
});

</script>



protected void TextBox1_TextChanged(object sender, EventArgs e)
{
Label1.Text = "you inputed " + TextBox1.Text;

}


Following this sample, it will store the current cursor loacation in TextBox in beginRequest event, and set it back in endRequest event. In this way, it will achieve keeping the current location in TextBox.

(It's also working if you use an Ajax Timer inside UpdatePanel.)

Wednesday, June 10, 2009

How to add a Custom Button on TopToolBar of HTML Editor

As to the new AjaxControlToolkit control HTML Editor, if some one wants to add a custom button onto TopToolBar of Editor and do some custom code, you can add a MethodButton and define client-side onclick to do script. It's no way to define server-side onclick of it. However, you can build a doPostBack functionality on client if you'd like to do onclick on the server-side.


public class CustomEditor : AjaxControlToolkit.HTMLEditor.Editor
{
public CustomEditor()
{
//
// TODO: Add constructor logic here
//
}


protected override void FillTopToolbar()
{
//base.FillTopToolbar();

AjaxControlToolkit.HTMLEditor.ToolbarButton.MethodButton btn=new AjaxControlToolkit.HTMLEditor.ToolbarButton.MethodButton();
btn.NormalSrc= "aa.jpg";
btn.Attributes.Add("onclick", "alert('a');");
TopToolbar.Buttons.Add(btn);

}
}


Update:

This new feature has been added to Editor control. You can download it and create custom button in editor control:

http://ajaxcontroltoolkit.codeplex.com/SourceControl/changeset/view/54501