Reputation: 3904
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
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
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