Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating ASP.NET UpdatePanel during processing loop

I've seen this particular problem posted multiple times but none of the solutions I've seen actually accomplish what I'm trying to do.

I have an ASP.NET page with an UpdatePanel on it. In the UpdatePanel is a button and a multiline textbox. The button's click event disables the button and enters a processing loop that updates the Text property of the TextBox multiple times. But, of course, the TextBox is not actually updated until the loop completes.

I've seen suggestions to add a Timer control to the page and I've tried that. But the Timer's Tick doesn't fire during until the loop is complete so it's no use!

Does ANYONE have a working solution for this? I need a page that allows the user to click a button to initiate a process that processes multiple records in a database and gives updates to the user indicating each record processed.

Here's my page code:

<!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></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
            <ContentTemplate>
                <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
                <br />
                <asp:TextBox ID="TextBox1" runat="server" Height="230px" TextMode="MultiLine" 
                    Width="800px"></asp:TextBox>
            </ContentTemplate>
        </asp:UpdatePanel>

    </div>
    </form>
</body>
</html>

Here's my code-behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace UpdatePanel
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            Button1.Enabled = false;
            for (int x = 1; x < 6; x++)
            {
                ViewState["progress"] += "Beginning Processing Step " + x.ToString() + " at " + DateTime.Now.ToLongTimeString() + "..." + System.Environment.NewLine;
                System.Threading.Thread.Sleep(1000); //to simulate a process that takes 1 second to complete.
                ViewState["progress"] += "Completed Processing Step " + x.ToString() + " at " + DateTime.Now.ToLongTimeString() + System.Environment.NewLine;
                TextBox1.Text = ViewState["progress"].ToString();
            }

        }

    }

}

If I simply set the VewState value in the loop and then add a Timer that sets the TextBox Text property to the ViewState value, that code never fires until the loop is completed.

like image 655
Dan H. Avatar asked Dec 04 '25 10:12

Dan H.


1 Answers

Well, I finally found the answer in this post. I've adjusted my own code accordingly (with a few tweaks) and it works perfectly now.

Here's my page code:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="UpdatePanel.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></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <asp:Timer runat="server" ID="Timer1" Interval="1000" Enabled="false" ontick="Timer1_Tick" />
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
            </Triggers>
            <ContentTemplate>
                <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
                <br />
                <asp:TextBox ID="TextBox1" runat="server" Height="250px" TextMode="MultiLine" 
                    Width="800px"></asp:TextBox>
            </ContentTemplate>
        </asp:UpdatePanel>

    </div>
    </form>
</body>
</html>

Here's my code-behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;

namespace UpdatePanel
{
    public partial class Default : System.Web.UI.Page
    {
        protected static string content;
        protected static bool inProcess = false;
        protected static bool processComplete = false;
        protected static string processCompleteMsg = "Finished Processing All Records.";

        protected void Page_Load(object sender, EventArgs e){ }

        protected void Button1_Click(object sender, EventArgs e)
        {
            Button1.Enabled = false;
            Timer1.Enabled = true;
            Thread workerThread = new Thread(new ThreadStart(ProcessRecords));
            workerThread.Start();
        }

        protected void ProcessRecords()
        {
            inProcess = true;
            for (int x = 1; x <= 5; x++)
            {
                content += "Beginning Processing Step " + x.ToString() + " at " + DateTime.Now.ToLongTimeString() + "..." + System.Environment.NewLine;
                Thread.Sleep(1000);
                content += "Completed Processing Step " + x.ToString() + " at " + DateTime.Now.ToLongTimeString() + System.Environment.NewLine + System.Environment.NewLine;
            }
            processComplete = true;
            content += processCompleteMsg;
        }

        protected void Timer1_Tick(object sender, EventArgs e)
        {
            if (inProcess)
                TextBox1.Text = content;

            int msgLen = processCompleteMsg.Length;
            if (processComplete && TextBox1.Text.Substring(TextBox1.Text.Length - processCompleteMsg.Length) == processCompleteMsg) //has final message been set?
            {
                inProcess = false;
                Timer1.Enabled = false;
                Button1.Enabled = true;
            }
        }

    }
}
like image 165
Dan H. Avatar answered Dec 07 '25 00:12

Dan H.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!