我正在尝试在工作完成后从后台线程启用客户端上的按钮。我希望实现的功能是,用户单击按钮在后台运行线程,然后按钮将禁用,防止用户创建另一个线程,线程将完成其工作,然后再次启用按钮。不幸的是
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*/
}
嗯,如上所述,单独的进程线程没有 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。
所以,评论中提出了一个很好的后续问题。我们可以在不编写 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 来实现此目的。