asdfkjasdfjk
asdfkjasdfjk

Reputation: 3904

Activate or deactivate button depending on another button

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?

Upvotes: 3

Views: 772

Answers (2)

matei.navidad
matei.navidad

Reputation: 745

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

Upvotes: 2

Haedrian
Haedrian

Reputation: 4328

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

Upvotes: 0

Related Questions