Ajax UpdatePanel DropDownList auto-repeat problem.

  • Thread starter Thread starter cmeek1_1999
  • Start date Start date
C

cmeek1_1999

Hello,

On a webpage, create an UpdatePanel with two DropDownLists.
Set AutoPostBack of DropDownList1 to true.
In the SelectedIndexChanged method, refill DropDownList2 and set the focus
to DropDownList1 using the ScriptManager SetFocus method.

Use the Visual Studio debugger.
Set a breakpoint on in the (!IsPostBack) part of the PageLoad method.
Start the web application and continue after the breakpoint has been reached.
Press once the down-arrow on the DropDownList1.
An update of DropDownList2 will occur as expected.

Press and hold the down-arrow (or up-arrow) so the auto-repeat sets in.
From time to time (not always) the breakpoint gets reached, which is highly
unexpected.

Two questions.

1) Why does this happen?
2) What can I do to prevent this from happening without losing focus and
auto-repeat functionality?

Regards,

Carlo Mekenkamp

default.aspx.cs
---8<---
using System;
using System.Web.UI.WebControls;

namespace AjaxDdlTest
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DropDownList1.DataBind(); //breakpoint here
DropDownList1.SelectedValue = "0";
DropDownList2.DataBind();
DropDownList1.Focus();
}
}

protected void DropDownList1_SelectedIndexChanged(object sender,
EventArgs e)
{
DropDownList ddl = (DropDownList)sender;
DropDownList2.DataBind();
ScriptManager1.SetFocus(ddl);
}

protected void DropDownList1_DataBinding(object sender, EventArgs e)
{
DropDownList ddl = (DropDownList)sender;
ddl.Items.Clear();
for (int i = 0; i < 200; i++)
{
ddl.Items.Add(i.ToString());
}
}

protected void DropDownList2_DataBinding(object sender, EventArgs e)
{
DropDownList ddl = (DropDownList)sender;
ddl.Items.Clear();
for (int i = int.Parse(DropDownList1.SelectedValue); i < 200; i++)
{
ddl.Items.Add(i.ToString());
}
}
}
}
---8<---
default.aspx
---8<---
<%@ Page Language="C#" AutoEventWireup="true" Codebehind="Default.aspx.cs"
Inherits="AjaxDdlTest._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>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:DropDownList ID="DropDownList1" runat="server"
AutoPostBack="true" OnDataBinding="DropDownList1_DataBinding"

OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" />
<br />
<asp:DropDownList ID="DropDownList2" runat="server"
AutoPostBack="true" OnDataBinding="DropDownList2_DataBinding" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
---8<---
 
this is one of the problems of using autopostback with a dropdown.
becuase the onclick/onchange fires as the down arrow is used, a postback
is fired on each selection. any postback causes the dropdown to be built
by the browser.

a quick hack is to have client code attached to the onchange disable the
select, then on rerender enable it again. a better approach is to use a
timer to only fire the postback when the user pauses in selecting. the
best is to not use a postback on a dropdown, but some other ajax method.

-- bruce (sqlwork.com)
 
Thank you for responding.

When I disabled the select on the onchange, the focus was lost,
so that was no good solution.
Setting a timer was a better solution. I implemented it like below.
Or should I make an Ajax Extender to handle this?
Letting another control return was not an option I think.

Regards,

Carlo Mekenkamp

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string ddl_timeout_id = DropDownList1.ClientID + "_timeoutId";
string ddl_submit = DropDownList1.ClientID + "_submit";
string delayscript =
@"var " + ddl_timeout_id + @"=0;
function " + ddl_submit + @"()
{
clearTimeout(" + ddl_timeout_id + @");
" + ddl_timeout_id + @"=setTimeout('" +
ClientScript.GetPostBackEventReference(DropDownList1,
string.Empty).Replace(@"'", @"\'")
+ @"', 500);
return true;
}";
ClientScript.RegisterClientScriptBlock(typeof(_Default),
ddl_submit, delayscript, true);
DropDownList1.DataBind(); //breakpoint here
DropDownList1.Attributes["onchange"] = @"return " +
ddl_submit + @"();alert('Bug:No return');";
DropDownList1.SelectedValue = "0";
DropDownList2.DataBind();
DropDownList1.Focus();
}
}
 
I tried writing an Ajax control (my first one) to do it, because I had
troubles with the script inside a FormView and I wanted the first postback
without delay.
Be sure to make AutoPostBackDelayBehavior.js an embedded resource and let
the ClientScriptResource attribute point to the right place.
Let the TargetControlId be the Id of the DropDownList, and it seems to work.

Thanks again.

Carlo Mekenkamp

AutoPostBackDelayExtender.cs
---8<---
using System;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.ComponentModel;
using System.ComponentModel.Design;
using AjaxControlToolkit;

[assembly:
System.Web.UI.WebResource("AutoPostBackDelay.AutoPostBackDelayBehavior.js",
"text/javascript")]

namespace AutoPostBackDelay
{
[Designer(typeof(AutoPostBackDelayDesigner))]
[ClientScriptResource("AutoPostBackDelay.AutoPostBackDelayBehavior",
"AutoPostBackDelay.AutoPostBackDelayBehavior.js")]
[TargetControlType(typeof(Control))]
public class AutoPostBackDelayExtender : ExtenderControlBase
{
// TODO: Add your property accessors here.
//
[ExtenderControlProperty]
[DefaultValue(500)]
public int DelayTime
{
get
{
return GetPropertyValue("DelayTime", 500);
}
set
{
SetPropertyValue("DelayTime", value);
}
}
[ExtenderControlProperty]
[DefaultValue(600)]
public int ResetTime
{
get
{
return GetPropertyValue("ResetTime", 600);
}
set
{
SetPropertyValue("ResetTime", value);
}
}

/// <summary>
/// Specifies the script to run to initiate a postback
/// </summary>
[ExtenderControlProperty]
[ClientPropertyName("postBackScript")]
[DefaultValue("")]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public string PostBackScript
{
get
{
return GetPropertyValue("PostBackScript", string.Empty);
}
set
{
SetPropertyValue("PostBackScript", value);
}
}

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
PostBackScript =
Page.ClientScript.GetPostBackEventReference(TargetControl, "");
}
}
}
---8<---
AutoPostBackDelayDesigner.cs
---8<---
using System.Web.UI.WebControls;
using System.Web.UI;

namespace AutoPostBackDelay
{
class AutoPostBackDelayDesigner :
AjaxControlToolkit.Design.ExtenderControlBaseDesigner<AutoPostBackDelayExtender>
{


}
}
---8<---
AutoPostBackDelayBehavior.js
---8<---
Type.registerNamespace('AutoPostBackDelay');

AutoPostBackDelay.AutoPostBackDelayBehavior = function(element) {
AutoPostBackDelay.AutoPostBackDelayBehavior.initializeBase(this,
[element]);

// TODO : (Step 1) Add your property variables here
this._DelayTimeValue = 500;
this._ResetTimeValue = 600;
this._CurrentDelayTimeValue = 0;
this._DelayId = 0;
this._ResetId = 0;
// Script to call to initiate a postback
this._postBackScript = null;
this._changeHandler = null;
this._oldScript = null;
}
AutoPostBackDelay.AutoPostBackDelayBehavior.prototype = {
initialize : function() {
AutoPostBackDelay.AutoPostBackDelayBehavior.callBaseMethod(this,
'initialize');

var element = this.get_element();

// Attach the handler
this._changeHandler = Function.createDelegate(this, this._onChange);
$addHandler(element, "change", this._changeHandler);

this._oldScript = element.getAttribute("onchange");
if (this._oldScript) {
element.setAttribute("onchange", null);
}
// TODO: Add your initalization code here
},

dispose : function() {
// TODO: Add your cleanup code here
clearTimeout(this._ResetId);
clearTimeout(this._DelayId);
this._CurrentDelayTimeValue = 0;
// Detach event handlers
if (this._changeHandler) {
$removeHandler(this.get_element(), "change", this._changeHandler);
this._changeHandler = null;
}

if (this._oldScript) {
this.get_element().setAttribute("onchange", this._oldScript);
this._oldScript = null;
}

AutoPostBackDelay.AutoPostBackDelayBehavior.callBaseMethod(this,
'dispose');
},

// TODO: (Step 2) Add your property accessors here
_onChange : function() {
var e = this.get_element();
if (e) {
clearTimeout(this._ResetId);
clearTimeout(this._DelayId);
this._DelayId = setTimeout(Function.createDelegate(this,
this._postBack), this._CurrentDelayTimeValue);
this._ResetId = setTimeout(Function.createDelegate(this,
this._reset), this._ResetTimeValue);
this._CurrentDelayTimeValue = this._DelayTimeValue;
return false;
}
},

_reset : function() {
this._CurrentDelayTimeValue = 0;
},

_postBack : function() {
if (this._postBackScript) {
eval(this._postBackScript);
}
},

get_postBackScript : function() {
/// <value type="String">
/// Script to run to initiate a postback
/// </value>
return this._postBackScript;
},
set_postBackScript : function(value) {
if (this._postBackScript != value) {
this._postBackScript = value;
this.raisePropertyChanged('postBackScript');
}
},

get_DelayTime : function() {
return this._DelayTimeValue;
},
set_DelayTime : function(value) {
this._DelayTimeValue = value;
},

get_ResetTime : function() {
return this._ResetTimeValue;
},
set_ResetTime : function(value) {
this._ResetTimeValue = value;
}
}
AutoPostBackDelay.AutoPostBackDelayBehavior.registerClass('AutoPostBackDelay.AutoPostBackDelayBehavior', AjaxControlToolkit.BehaviorBase);
---8<---
 
Back
Top