如何处理正在进行中的任务

4
在我的C#程序中,有一个“登录”功能。当用户点击按钮时-程序使用给定的登录/密码信息,使用POST URL登录到特定网站并保存Cookie。然后,我的程序进一步处理登录后的网页。然后将结果显示给用户。
问题在于-用户单击按钮的那一刻-整个程序会挂起,并等待上述操作完成。这是3-5秒的不活动时间。
我想在此期间阻止用户输入并提示消息,例如“正在工作...”。否则,无法告诉程序是否正在处理某些内容。这就是我的意思:
需要采取哪些技术来实现所需的目标?

你有一些代码或者什么东西吗? - Soner Gönül
非常相似的答案,符合你的需求:https://dev59.com/VHVC5IYBdhLWcg3wYQAp#6701081 - Giedrius
http://stackoverflow.com/questions/6567488/mvc3-ajax-actionlink-and-delaying-loadingelementid-from-showing - Eugene K.
否则,无法确定程序是否正在处理某些内容。您可以将光标设置为繁忙状态吗? - Jordy
这是WPF还是WinForms? - Dustin Kingen
抱歉,它是WinForms。 - Alex
3个回答

2

UI被阻塞,因为您在UI线程上运行登录代码。为了避免这种情况,您可以使用“BackgroundWorker”,或者如果您正在使用4或4.5 .NET,则可以使用“Tasks”将登录内容移动到另一个线程以避免UI阻塞。

如果您正在使用Windows Forms和.NET 4+,请尝试以下方法:

  private void button1_Click(object sender, EventArgs e)
    {
        progressBar1.Visible = true;
        Task.Factory.StartNew(Login)
            .ContinueWith(t => 
                { 
                    progressBar1.Visible = false; 
                }, TaskScheduler.FromCurrentSynchronizationContext());
    }

    private static void Login()
    {
        // should replace this with actual login stuff
        Thread.Sleep(TimeSpan.FromSeconds(3));
    }

它的作用是将登录处理移动到另一个线程,以便UI线程不会被阻塞。在开始登录之前,它会显示进度条,样式设置为marque,在登录完成后,它会再次隐藏进度条。
只要UI没有被阻塞,用户可以在登录期间输入/按下任何他想要的内容,因此解决方案可能是在登录之前禁用所有控件或在单独的模态窗体中显示进度条,这样用户就不会看到应用程序挂起,并且在进度条窗体关闭之前无法进行任何输入。
更新:添加了单独进度窗体的示例。
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MarqueeForm.DoWithProgress("Doing login", Login);
    }

    private static void Login()
    {
        Thread.Sleep(TimeSpan.FromSeconds(3));
    }
}

public class MarqueeForm : Form
{
    private Label label;

    public MarqueeForm()
    {
        var progressBar = new ProgressBar
                          {
                              Style = ProgressBarStyle.Marquee, 
                              Top = 20, 
                              Size = new Size(300, 15)
                          };

        Controls.Add(progressBar);

        label = new Label();
        Controls.Add(label);
    }

    public static void DoWithProgress(string title, Action action)
    {
        var form = new MarqueeForm
                   {
                       Size = new Size(310, 50),
                       StartPosition = FormStartPosition.CenterParent,
                       FormBorderStyle = FormBorderStyle.FixedDialog,
                       ControlBox = false,
                       label = { Text = title }
                   };

        form.Load += (sender, args) =>
            Task.Factory.StartNew(action)
                .ContinueWith(t => ((Form)sender).Close(),
                    TaskScheduler.FromCurrentSynchronizationContext());

        form.Show();
    }
}

我还没有尝试过你的方法,但是如果用户连续点击登录按钮2次或更多次会发生什么? - Alex
我在答案的结尾添加了相关评论。 - Giedrius
添加了一个示例,其中包含单独的进度表单,解决了在某些处理过程中能够与UI进行交互的问题。 - Giedrius

0

当按下按钮时,您可以禁用按钮 button1.Enable = false; 同时显示消息 "请稍等.."。 当进程完成并显示结果时,启用按钮。


我正在禁用它 ;) 实际上,即使我禁用了这个按钮 - 表单中仍然有其他5个按钮.. 尽管如此,正如我所说的那样,这不是问题,因为我已经用自己的方式解决了它.. 我只是在想必须有一种更好的方法来全局处理这个问题。例如,每当主线程挂起->进度条就会显示,并且用户输入被阻止(全局,不仅仅是针对一个按钮,而是整个表单)。 - Alex
整个表单禁用怎么样?或者将其隐藏,显示另一个带有操作进度的表单。 - pieterlouw

0

我猜这个问题是关于一个窗体应用程序的?

如果是这样,你可以设置你的窗体为“加载屏幕”,然后执行一个Application.Doevents(); 然后开始你的进程。当它完成后,你可以移除你的“加载屏幕”。


这是我一直在思考的问题.. 但是有没有办法在主线程挂起时自动触发这个“屏幕”?这样我就不必为每个单独的操作“调用此屏幕”了。 - Alex
这里的解决方案是:this.Refresh(); Application.DoEvents(); - ikwillem
Doevents不是解决问题的推荐方案。http://www.codinghorror.com/blog/2004/12/is-doevents-evil.html - Giedrius
是的,我明白,但对于这样一个简单的问题,线程会导致很多开销。虽然一行代码就可以得到相同的预期结果,但这并不是一个非常优雅的解决方案。 - ikwillem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接