4

In my C# program there is a "Login" function. When user presses on the button - program uses given login/pas info to login on a certain website using POST url and to save cookies. The webpage after login is then further processed in my program. Then, the result is shown to the user.

The problem is - the moment user clicks on a button - whole program hangs up and is waiting until operation from above is accomplished. That's about 3-5 seconds of inactivity.

I'd like to block user input during that time and to prompt with the message, for example "working...". Othervise there is no way to tell is the program is actually working on something or not. Here's what I mean:

enter image description here

What techniques should I use to achieve the desired goal?

Alex
  • 4,607
  • 9
  • 61
  • 99

3 Answers3

2

UI is blocked, because you are running login code in UI thread. To avoid that, you may use 'BackgroundWorker', or if you're using 4 or 4.5 .NET you may use 'Tasks' to move your login stuff to another thread to avoid UI blocking.

If Windows Forms and .NET 4+, following may work:

  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));
    }

What it does, it moves Login processing to another thread, so UI thread is not blocked. Before staring login, it unhides progress bar, which has style set to marque and after Login is finished, it hides progress bar again.
As long as UI is not blocked, user is allowed to input/press anything he wants during login, so solution would be either disable all controls before login or show progress bar in separate modal form, in such way user won't see application as hanged and won't be able to do any input until progress bar form will be closed.

Update: added example with separate progress form:

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();
    }
}
Giedrius
  • 8,430
  • 6
  • 50
  • 91
  • I hadn't tried your approach yet, but what will happen if user clicks on login button 2 or more times in a row? – Alex May 29 '13 at 11:41
  • I've added comment regarding that in the end of the answer – Giedrius May 29 '13 at 11:48
  • Added example with separate progress form, which solves the issue with ability to interact with UI while some processing in happening. – Giedrius May 29 '13 at 11:59
0

When pressing the button you can disable the button button1.Enable = false; while displaying message like "Please wait..". Enable button when process finish and display result.

pieterlouw
  • 81
  • 2
  • 3
  • i AM disabling it ;) in fact, even if i disable this 1 button - there are still 5 more of them in the form.. Nevertheless as I said that's not the problem because i managed to deal with it my way.. I was just thinking that there must be a better way to deal with the problem globally. For example, each time main thread is hanged -> progress bar is shown and user input is blocked (globally, not just for 1 button, but for the whole form) – Alex May 29 '13 at 11:25
  • How about disabling the whole form then? Or make it invisible and show another form with progress of operation – pieterlouw May 29 '13 at 11:29
0

I guess the question is about a Forms application?

If so, you can setup your form to the "loading screen" then do a Application.Doevents(); then start your process. After it's done you can remove your "loading screen".

ikwillem
  • 1,044
  • 1
  • 12
  • 24
  • that's something what I've been thinking.. But is the there a way to trigger this "screen" automatically, each time main thread is hanged? So I don't have to "call for this screen" for each single operation. – Alex May 29 '13 at 11:26
  • http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/f13c9d08-00a4-402c-9beb-6a77b99ee221/ Here the solution was to do: this.Refresh(); Application.Doevents(); – ikwillem May 29 '13 at 11:29
  • Doevents is not recommended solution to the problem. http://www.codinghorror.com/blog/2004/12/is-doevents-evil.html – Giedrius May 29 '13 at 11:32
  • Yes I understand, but threading will cause a lot of overhead for such a simple problem. That while 1 line of code will have the same expected result, although it's not a really neat solution. – ikwillem May 29 '13 at 11:39