Wednesday, June 10, 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.)