从后台线程 ASP.NET Web 表单启用 HTML 按钮

问题描述 投票:0回答:1

我正在尝试在工作完成后从后台线程启用客户端上的按钮。我希望实现的功能是,用户单击按钮在后台运行线程,然后按钮将禁用,防止用户创建另一个线程,线程将完成其工作,然后再次启用按钮。不幸的是

this.button.Enabled=true
在线程块内时不启用。请注意,我是 ASP.NET Web 窗体和 C# 的新手。

到目前为止我已经:

protected void BtOnclick(Object sender, EventArgs e){
    
    this.button.Enabled = false;
    Threadstart start = new ThreadStart(this.WorkerThread);
    new Thread(start).Start()
}

protected void WorkerThread(){
    
    /*Here I do some work*/
    this.button.Enabled = true; /* This line is not enabling my button*/
}
c# asp.net multithreading webforms
1个回答
0
投票

嗯,如上所述,单独的进程线程没有 http 上下文。 (而且,它一般也没有 session() 。

我的意思是,最终用户可能会单击链接开始在亚马逊上购物,或者合上笔记本电脑的盖子。

那么,考虑到这种所谓的无状态环境?

好吧,这表明您可以从客户端采用所谓的 Web 套接字,然后在服务器上有一些与客户端通信的代码。

此设置通常用于聊天页面。但是,既然设置和编写代码来维护该 Web 套接字可能需要大量工作?那么大多数人建议采用SignalR库。

但是,虽然 SignalR 可以是一个很好的解决方案(并且允许从服务器向客户端推送通知)?

它还有很多移动部件,可以满足您想要的简单任务。

简单的方法是让客户端 JavaScript 代码运行,比如每秒运行一次。它可以向服务器发出请求以检查处理是否完成,然后您可以重新启用该按钮。

但是,既然 Web 服务调用也是无状态的呢?然后,正在运行的进程将必须更新一些“值”,这些“值”不仅持续存在,而且属于一个用户!

这意味着“完成标志”唯一的两个实际位置是会话()或数据库中的某些行。但是,您必须将 HttpContext.Current 传递给该单独的线程/进程才能正常工作。 (这可能会导致问题)。

所以,最简单的就是使用HttpRuntime.Cache。请记住,这不是针对每个用户的,而是针对所有用户的上下文。

因此,需要添加某种密钥,例如 userID 等。

那么,说出这个示例标记:

        <asp:Button ID="cmdStart" runat="server" 
            Text="Start the process"
            OnClick="cmdStart_Click"
            CssClass="btn" />

        <div id="mywaitdiv" runat="server" style="display:none" clientid="static">
            <h3>Processing - please wait</h3>
            <img src="../Content/wait2.gif" width="48" />
        </div>


        <script>

            function mypoll() {
                $.ajax({
                    type: "POST",
                    url: "ProcessWait.aspx/CheckDone",
                    data: {},
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function (rstuff) {
                        if (rstuff.d === "done") {
                            $("#cmdStart").prop("disabled", false)
                            $("#mywaitdiv").hide()
                        }
                        else {
                            setTimeout(mypoll, 1000)
                        }
                    },
                    error: function (xhr, status, error) {
                        var errorMessage = xhr.status + ': ' + xhr.statusText
                        alert('Error - ' + errorMessage)
                    }
                });
            }
        </script>

因此,我们单击一个按钮,后面的代码就会运行:

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void cmdStart_Click(object sender, EventArgs e)
    {
        cmdStart.Enabled = false;
        mywaitdiv.Style.Add("Display", "inline");

        HttpRuntime.Cache[logonid + "_MyProcess"] = "running";

        string sJava = "mypoll()";
        ScriptManager.RegisterStartupScript(Page, Page.GetType(), "startpoll", sJava, true);

        ThreadStart MyProc = new ThreadStart(MyWorker);
        Thread MyThread = new Thread(MyProc);
        MyThread.Start();


    }

    public static void MyWorker()
    {
        System.Threading.Thread.Sleep(4000);
        HttpRuntime.Cache[logonid + "_MyProcess"] = "done";

    }

    [WebMethod(EnableSession = true)] 
    public static string CheckDone()
    {
        return HttpRuntime.Cache[logonid + "_MyProcess"].ToString();
    }

现在的结果是这样的:

我假设对上述 JavaScript 代码使用 jQuery。

编辑:使用 100% 服务器端控件执行此操作

所以,评论中提出了一个很好的后续问题。我们可以在不编写 JavaScript 和 Web 方法的情况下做到这一点吗?

答案是肯定的,网络表单有一个叫做更新面板的魔术。

所以,请尝试以下步骤。

更新面板将为我们“ajax”此操作,我们不必在这里编写一行 JavaScript。

首先,从工具箱中拖入脚本管理器,将其放在表单标签后面,这样我们现在就有了:

<form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

现在我们很酷的“更新”内容区域和开始按钮。

拖入更新面板。所以,我们有这个:

        <asp:UpdatePanel ID="UpdatePanel1" runat="server"></asp:UpdatePanel>

然后输入“ContentTemplate”

我们现在有这个:

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>


            </ContentTemplate>
        </asp:UpdatePanel>

所以,现在我们简单地放置按钮并更新上面的内容(内容模板区域)。

然后拖入计时器控件。

确保计时器设置为每 1 秒触发一次,并确保“enabled”为 false。您可以使用此控件的属性表,其中包括属性表的“事件”选项卡区域。

所以,我们现在有这个:

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>

                <asp:Button ID="cmdStart" runat="server"
                    Text="Start the process"
                    OnClick="cmdStart_Click"
                    CssClass="btn" />

                <div id="mywaitdiv" runat="server" visible="false">
                    <h3>Processing - please wait</h3>
                    <h4 id="MyStatus" runat="server"></h4>
                    <img src="../Content/wait2.gif" width="48" />
                </div>

                <asp:Timer ID="Timer1" runat="server" 
                    Enabled="False" 
                    Interval="1000" OnTick="Timer1_Tick" >
                </asp:Timer>

            </ContentTemplate>
        </asp:UpdatePanel>

现在,我还添加了处理任务正在进行的“步骤状态”。

所以,我们的代码如下:

    protected void cmdStart_Click(object sender, EventArgs e)
    {
        cmdStart.Enabled = false;
        mywaitdiv.Visible = true;

        Session["mystep"] = "Processing Payroll data";
        MyStatus.InnerText = Session["mystep"].ToString();

        ThreadStart MyProc = new ThreadStart(MyWorker);
        Thread MyThread = new Thread(MyProc);
        MyThread.Start();

        Timer1.Enabled = true; // start the timer

    }

    public  void MyWorker()
    {
        System.Threading.Thread.Sleep(3000);
        // step one done, now work on step 2

        Session["mystep"] = "Sending out emails for payroll";
        System.Threading.Thread.Sleep(4000);
        Session["mystep"] = "done";

    }


    protected void Timer1_Tick(object sender, EventArgs e)
    {

        if (Session["mystep"].ToString() == "done")
        {
            // we are done, hide the process gif and area, stop the timer
            mywaitdiv.Visible = false;
            Timer1.Enabled = false; // stop timer
            MyStatus.InnerText = "";
            cmdStart.Enabled = true;

        }
        else
        {
            MyStatus.InnerText = Session["mystep"].ToString();
        }
    }

所以,现在的结果是这样的:

所以,请注意,虽然我们没有编写任何客户端 JavaScript?

好吧,我们仍然必须在这里采用轮询方式。

如上所述,如果您真的希望推送通知在代码服务器端运行?然后按照建议,您可以采用 SignalR 来实现此目的。

© www.soinside.com 2019 - 2024. All rights reserved.