views:

23

answers:

2

Well I have a strange problem.

I have a page with 2 DropDownLists on it and a custom web-user-control. The custom web user control has an UpdatePanel within it, and an Ajax Timer control within the UpdatePanel to periodically update a listing of stuff.

When I "drop-down" one of the DropDownLists and hover over (not click on) an option while the Timer control within the UpdatePanel asynchronously posts back to the server, the DropDownList "autopostbacks" to the server!

I'm trying to figure out why an asynchronous postback would cause the DropDownList to act as if I selected/clicked on an option so that I can find a way around this issue.

Now it's really simple to reproduce this problem. Create a Web User control called "TimerUpdatedListing"...this is the ASPX code markup for the web user control:

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="TimerUpdatedListing.ascx.vb" Inherits="MyNamespace.TimerUpdatedListing" %>
<div style="width: 150px; height: 150px; overflow: auto; border: solid 1px navy;">
    <asp:UpdatePanel ID="anUpdatePanel" runat="server">
        <ContentTemplate>
            <asp:Repeater ID="aRepeater" runat="server">
                <ItemTemplate>
                    <div style="border-bottom: solid 1px #EEC900; margin: 3px; padding: 2px;">
                        Id:
                        <%#Eval("Id")%>
                        <br />
                        Time:
                        <%#Eval("Time")%>
                    </div>
                </ItemTemplate>
            </asp:Repeater>
            <asp:Timer ID="aTimer" runat="server" Interval="2000">
            </asp:Timer>
        </ContentTemplate>
    </asp:UpdatePanel>
</div>

This is the VB.NET server side code for the web user control:

Public Partial Class TimerUpdatedListing
    Inherits System.Web.UI.UserControl

    Private _aListOFThings As List(Of Things)

    Private Sub aTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles aTimer.Tick
        If Session("_aListOfThings") Is Nothing Then
            _aListOFThings = New List(Of Things)
        Else
            _aListOFThings = CType(Session("_aListOfThings"), List(Of Things))
        End If

        If _aListOFThings.Count > 9 Then
            _aListOFThings = New List(Of Things)
        End If

        _aListOFThings.Add(New Things((_aListOFThings.Count + 1).ToString, Now.ToString("hh:mm:ss")))
        Session("_aListOfThings") = _aListOFThings
        aRepeater.DataSource = _aListOFThings
        aRepeater.DataBind()
    End Sub

    Private Class Things
        Private _time As String
        Private _id As String
        Public Property Time() As String
            Get
                Return _time
            End Get
            Set(ByVal value As String)
                _time = value
            End Set
        End Property
        Public Property ID() As String
            Get
                Return _id
            End Get
            Set(ByVal value As String)
                _id = value
            End Set
        End Property
        Public Sub New(ByVal id As String, ByVal time As String)
            _id = id
            _time = time
        End Sub
    End Class
End Class

Now, in an ASPX page called WebForm1.aspx, add 2 DropDownLists and the web user control:

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm1.aspx.vb" Inherits="MyNamespace.WebForm1" %>

<%@ Register Src="TimerUpdatedListing.ascx" TagName="TimerUpdatedListing" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
    <title>Test</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true">
            <asp:ListItem Text="1" Value="1" />
            <asp:ListItem Text="2" Value="2" />
            <asp:ListItem Text="3" Value="3" />
            <asp:ListItem Text="4" Value="4" />
            <asp:ListItem Text="5" Value="5" />
        </asp:DropDownList>
        <asp:Label ID="selectedValue1" runat="server"></asp:Label>
        <br />
        <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="true">
            <asp:ListItem Text="a" Value="a" />
            <asp:ListItem Text="b" Value="b" />
            <asp:ListItem Text="c" Value="c" />
            <asp:ListItem Text="d" Value="d" />
            <asp:ListItem Text="e" Value="e" />
        </asp:DropDownList>
        <asp:Label ID="selectedValue2" runat="server"></asp:Label>
        <br />
        <br />
        <uc1:TimerUpdatedListing ID="TimerUpdatedListing1" runat="server" />
    </div>
    </form>
</body>
</html>

Here is the VB.NET server side code for the WebForm1.aspx page:

Public Partial Class WebForm1
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If String.IsNullOrEmpty(Request.Params("ddl1")) = False Then
            selectedValue1.Text = Request.Params("ddl1")
        End If
        If String.IsNullOrEmpty(Request.Params("ddl2")) = False Then
            selectedValue2.Text = Request.Params("ddl2")
        End If
    End Sub

    Private Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
        Response.Redirect(Request.Url.LocalPath + "?ddl1=" + DropDownList1.SelectedValue.ToString, True)
    End Sub

    Private Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
        Response.Redirect(Request.Url.LocalPath + "?ddl2=" + DropDownList2.SelectedValue.ToString, True)
    End Sub
End Class

Thanks,

-Frinny

A: 

Here is an example of using triggers to trigger your update panel. Put the timer.tick event as a trigger and see what happens

http://www.asp.net/ajax/tutorials/understanding-asp-net-ajax-updatepanel-triggers

PS: I don't think the timer has to be included within the update panel. I actually build my timers in the codebehind and keep them out of the markup all together.

rockinthesixstring
Thank you for your response.I do not need to use triggers for this solution.Reply to PS: You can have a timer within an UpdatePanel you just need to be aware that the timer control will post back to the server every X-amount of milliseconds regardless of whether or not the last request has finished. If the timer control ticks/postsback before the last request has returned to the browser, then you could run into a situation where the UpdatePanel is "always updating". Be aware of this and you will be fine.
Frinavale
A: 

I attempted two different solutions to this problem.

The first thing I did was to check the Request.Params("__EVENTTARGET") to see if it matched the DropDownList. If it matched then I would call the Response.Redirect() method.

For example:

Public Partial Class WebForm1
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If String.IsNullOrEmpty(Request.Params("ddl1")) = False Then
            selectedValue1.Text = Request.Params("ddl1")
        End If
        If String.IsNullOrEmpty(Request.Params("ddl2")) = False Then
            selectedValue2.Text = Request.Params("ddl2")
        End If
    End Sub

    Private Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
        Dim ctrlNameThatCausedPostback As String = Request.Params("__EVENTTARGET")
        If String.IsNullOrEmpty(ctrlNameThatCausedPostback) = False AndAlso Page.FindControl(ctrlNameThatCausedPostback) Is DropDownList1 Then
            Response.Redirect(Request.Url.LocalPath + "?ddl1=" + DropDownList1.SelectedValue.ToString, True)
        End If
    End Sub

    Private Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
        Dim ctrlNameThatCausedPostback As String = Request.Params("__EVENTTARGET")
        If String.IsNullOrEmpty(ctrlNameThatCausedPostback) = False AndAlso Page.FindControl(ctrlNameThatCausedPostback) Is DropDownList2 Then
            Response.Redirect(Request.Url.LocalPath + "?ddl2=" + DropDownList2.SelectedValue.ToString, True)
        End If
    End Sub
End Class

I noticed that this didn't always work. There were times when I selected an option in the DropDownList and the Redirect wouldn't take place because the timer tick and the selected index changed event happened at the same time.

So the second approach I took was to check to see if the page is in an asynchronous postback to the server. If it was, then I knew that it was the timer tick event taking place and that the Redirect shouldn't occur.

For example:

Public Partial Class WebForm1
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If String.IsNullOrEmpty(Request.Params("ddl1")) = False Then
            selectedValue1.Text = Request.Params("ddl1")
        End If
        If String.IsNullOrEmpty(Request.Params("ddl2")) = False Then
            selectedValue2.Text = Request.Params("ddl2")
        End If
    End Sub

    Private Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
        If ScriptManager.GetCurrent(Me.Page).IsInAsyncPostBack = False Then
            Response.Redirect(Request.Url.LocalPath + "?ddl1=" + DropDownList1.SelectedValue.ToString, True)
        End If
    End Sub

    Private Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
        If ScriptManager.GetCurrent(Me.Page).IsInAsyncPostBack = False Then
            Response.Redirect(Request.Url.LocalPath + "?ddl2=" + DropDownList2.SelectedValue.ToString, True)
        End If
    End Sub
End Class

This helped, and the likelihood that the Redirect would occur properly was better than the previous approach; however, it still isn't 100%.

-Frinny

Frinavale