3

I am new in C#. I have two button and two label. What I want is that, When I click button1 it start counting and shows in label1. When I press button2 it resume counting under button1 and start counting under button2 and shows in label2. Here is my code

bool testt = true;
int i = 0;
int j = 0;

private void button1_Click(object sender, EventArgs e)
{
    while (testt)
    {
        label1.Text = i.ToString();
        i++;
        System.Threading.Thread.Sleep(50);
        if (i > 5000)
        {
            i = 0;
        }
    }
}

private void button2_Click(object sender, EventArgs e)
{
    testt = false;
    while (!testt)
    {
        label2.Text = j.ToString();
        j++;
        System.Threading.Thread.Sleep(50);
        if (j > 5000)
        {
            i = 0;
        }
    }
}

Here the problem is when I click one button, it does not allow me to click another button. I used a global variable to detect which button was pressed. But its not working. How can I make this work?

spajce
  • 7,044
  • 5
  • 29
  • 44
asdfkjasdfjk
  • 3,784
  • 18
  • 64
  • 104
  • just follow this [stuff](http://lostechies.com/gabrielschenker/2009/01/23/synchronizing-calls-to-the-ui-in-a-multi-threaded-application/) – spajce Mar 16 '13 at 22:16

2 Answers2

2

You are doing all this on the UI thread, so you are blocking it. You need to have another thread do the actual counting and delegate the label changing to the main thread by using Control.Invoke so that the main thread has time to run the message pump. You can use the built-in BackgroundWorker or simply create a thread with either ThreadPool or TaskFactory.

I've written a short example of how this can be accomplished with LINQ and ThreadPool, it's not the best approach design-wise but it would give you a starting point on developing an adequate solution

    semaphore = true;
    int i = 0;
    int j = 0;

    private void button1_Click(object sender, EventArgs e)
    {
        semaphore = true;
        ExecuteAsync(()=>
            {
                while (semaphore)
                {
                    //Dispatch a call to the UI thread to change the label
                    Invoke((MethodInvoker)(() => ChangeLabel(label1, i.ToString())));
                    i++;
                    Thread.Sleep(50);
                    if (i > 5000)
                    {
                        i = 0;
                    }
                }
            });
    }

    //Executes a function on a ThreadPool thread
    private void ExecuteAsync(Action action)
    {
        ThreadPool.QueueUserWorkItem(
            obj => action());
    }

    private void ChangeLabel(Label label, string labelText)
    {
        label.Text = labelText;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        semaphore = false;
        ExecuteAsync(() =>
                {
                    while (!semaphore)
                    {
                        //Dispatch a call to the UI thread to change the label
                        Invoke((MethodInvoker)(() => ChangeLabel(label2, j.ToString())));
                        j++;
                        Thread.Sleep(50);
                        if (j > 5000)
                        {
                            i = 0;
                        }
                    }
                });
    }
matei.navidad
  • 676
  • 6
  • 18
  • I tried to use BackgroundWorker. In that case count show in label but I still can not click on another button – asdfkjasdfjk Mar 16 '13 at 22:07
  • when I click button1 it works and when i click button2 it resume button1 counting and start counting under button2 but again when I click button1 it don't work anymore, label two keep counting. Anyway, Thanks for the solution – asdfkjasdfjk Mar 16 '13 at 22:40
  • Thanks so much. Can you give me a link for the tutorial of ExecuteAsync method? – asdfkjasdfjk Mar 18 '13 at 10:57
  • The ExecuteAsync method is defined by us, it makes use of the ThreadPool http://msdn.microsoft.com/en-us/library/3dasc8as(v=vs.80).aspx. This link should provide you with the basic information regarding ThreadPool usage. Good luck. – matei.navidad Mar 18 '13 at 11:05
  • Thanks again for yuor help. – asdfkjasdfjk Mar 18 '13 at 20:30
0

The problem you have here is a thread issue.

When you have winforms, the main thread has the job of displaying and making the interface interactive AND performing any tasks you give it.

In your code, you start the countdown. But in there, the same thread which makes the interface work is now busy decreasing your value, so it won't update the interface or respond to buttons until its ready.

What you want is a helper thread to do the countdown for you (or honestly, start a TIMER instead). Otherwise the Interface will be non-interactive until you're done from that loop.

Timer is the easiest way to do it.

http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx

Haedrian
  • 4,240
  • 2
  • 32
  • 53
  • actually this the summary of another problem. I just gave the concept here. I need to know how this can be done without timer. – asdfkjasdfjk Mar 16 '13 at 22:03
  • In that case you'll need to spawn a new thread something like the BackgroundWorker class. The problem is then you'll need to delegate the click tasks - since ui can only be done on the main thread. The quick, easy and VERY dirty fix is to run Application.DoEvents() in the loop. – Haedrian Mar 16 '13 at 22:07
  • This might help: http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c – Haedrian Mar 16 '13 at 22:09