Ok, I have a chess board application. The application has 64 panels within a groupbox. These panels are identified with the expression CHessBoardPanels{x,y). I am using this to change the background color of the panels. I want a small delay in between where the chessPanel.BackGround changes to red and when the panels turn back to white.(approximately 1 to 2 second delay)
I have tried the Sleep function but it basically locks the application up till the task have been complete
Here is the code which I have tried:
for (int Row = 7; Row > 3; --Row)
{
chessBoardPanels[ Column ,Row].BackColor = Color.Red;
++Column;
//Add text to Moves TextBox
MovesText.AppendFormat("WhtB {0} {1}{2}", ColumnText, RowText, Environment.NewLine);
MovesTxt.Text = MovesText.ToString();
++ColumnText;
--RowText;
}
//Start White Horizonal Drill
Column = 0;
Thread.Sleep(5000); //This does not delay proerperly
for (int Row = 7; Row > 4; --Row)
{
chessBoardPanels[Column, Row].BackColor = Color.White;
++Column;
//Add text to Moves TextBox
MovesText.AppendFormat("WhtB {0} {1}{2}", ColumnText, RowText, Environment.NewLine);
MovesTxt.Text = MovesText.ToString();
++ColumnText;
--RowText;
}
I am using .Net framework 4.0 because Visual Studio 2010 doesn't support 4.5
That's what Sleep does, it pauses the thread.
What you need is to use a Timer control, and create a queue with color changes, and let the timer control call an event handler periodically, like so:
(in your Form class):
Timer timer = new Timer();
(in your Form's constructor):
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 500; // every 1/2 second
timer.Enabled = true;
timer.Start();
Then your event handler would do something like this:
void timer_Tick(object sender, EventArgs e)
{
// Read a queue, that contains timings
var nextItem = PeekAtQueue();
if ((nextItem != null) && (nextItem.WhenToChangeColor <= DateTime.Now))
{
var item = TakeFromQueue(); // as opposed to just peeking
ChangeColor(item);
}
}
What does this do? (Of course this code is not 100% complete, you'll have to add the queue-accessing methods yourself, it depends on what you want/can use)
Seeing that Sleep() freezes your application temporarily, you need a
solution that allows you to still interact with your form, while
still having a delay and doing something after that delay.
So you
need to use a timer to execute something at a later date. The timer
in this example gets executed every 1/2 second but you can use any
interval you choose.
Point is, because of this timer and this
configuration, a certain method -- the event handler -- is executed
every 1/2 second, or the interval of your choice.
So what you can
then do is, when you want a certain color change to happen later, to
queue an object that describes the color change, for instance {E5, "red", today at 04:20:30 PM}.
The event handler executed by the
timer peeks every time to see if there is something in the queue, and
to have a look at what it is. It checks whether the "moment to change
the color", as described in the queued object, is now or has already
passed. If so, it changes the color. If not, it leaves the item at
the top of the queue, and the next time the event handler gets
executed again (1/2 second later), it will perform the same check
again.
Once a color change is done, the object is taken off the
queue.
Consider using a Timer class instead. That should allow you to delay your application without locking the thread.
I have tried the Sleep function but it basically locks the application
up till the task have been complete
You need to move the logic of your application our of the main (or UI) thread. Your application will "freeze" if you call a Thread.Sleep in the main (UI) thread as the Windows event-handling loop is blocked.
Things you can use:
New Thread, Task, Background worker to execute logic of the game
Timers
Update the GUI from another thread
Sleep will lock the current thread, meaning if it's the main UI thread then no user input will be processed - and ideally you should set an hourglass cursor at that point.
To do what you want I think you need to start a background timer and then set the colours on the callback, then cancel the timer.
While the timer is ticking down the main UI will still be responsive.
You might also find that over 1 second delay will feel like too long a delay for users, but the timer period is in milliseconds so you can define a shorter delay.
Try following:
Task.Run(() => Thread.Sleep(5000)).Wait();
Related
I need help stopping this loop from constantly looping, i need it to give the user time to choose something in the new form it opens up but currently it just opens a whole load of them and doesn't stop.
while (Convert.ToInt32(PlayerHP[PlayerNo]) > 0 && Convert.ToInt32(PlayerHP[PlayerNo+1]) > 0){
ChooseAttack();
PlayerHP[PlayerNo+1] = (Convert.ToInt32(PlayerHP[PlayerNo+1]) - Damage()).ToString();
lblHPP1.Text = PlayerHP[0];
lblHPP2.Text = PlayerHP[1];
PlayerNo = ((PlayerNo+1)%2);
Application.DoEvents();
}
I've been experimenting with the "Application.DoEvents()" line but whereever i place it it doesnt do anything.
I don't see a form anywhere, but expect that somewhere in that ChooseAttack(); call you have some code that looks like this:
frmChoose.Show();
And all you need to do is change it to this:
frmChoose.ShowDialog();
I would push the loop out onto a timer thread so you can control the "loop" externally. The speed at which the loop runs would be the timeout time of the timer.
For more clarity, you would no longer have a 'while' statement, more of an if at the top of the timer's call back method to check your conditions. The actual looping part of it would be taken care of by the timer executing over and over.
Depending on which timer you choose, when you click a button on the form, you would just say Timer.Enabled = false; or something similar.
Attempt number 2. Trying to be as clear as possible because I have been on this task for much longer than I should have and made little progress.
I need to make a progress bar for an application. Right now, everything is happening in one UI thread, all the processing and whatnot, so when I do the long running process after a click of a button, the program hangs for roughly 40 seconds, then resumes with the output (I cannot change that part, the application was given to me). And I also have to make a cancel button, so if it is hit during the intermediate process, after that process is done, it checks for the Cancel flag, and if it is ON, break out of all the methods and return to initial position.
What I have to do is make a progress bar for this process. Two of them, one for intermediate process and one for total. Intermediate is a small action, like DB call, output intermediate process (for and foreach loops).
I have tried putting the bars in the same UI window and update them in the same thread as I go on (find major points, start the progress bars there and increment them based on my judgement. And loops). That was just loading the UI even more, but it was working (choppy progress bar movements).
Then I tried using the background worker. Same thing, put the bars in the UI and run them through the background worker. The problem with that even though the BG worker runs, the refresh must happen in the UI window, which is busy processing the greedy request, so it is not updated when I want it to.
I tried using multi threading at core (no BG worker) and create a new form in a new thread that would reflect the progress, but I was cautioned not to do it (running 2 UI threads at the same time) and I had troubles passing values around. Not to mention if using the threading method it is impossible to get an accurate representation of the progress happening without completely overloading the CPU (have another thread run in a while(true) and do an update when you call the updateMethod from some point in the process)
Do I have any other options left? Because the least negative aspect is the first where I update the progress bar as I go along with the program.
Do I have any other options left?
Yes and no. The best option is still to put time to learn how to use the backgroundworker properly, it's by far the easiest solution for this problem.
Go ahead and study these links:
Official MSDN Backgroundworker
Step by Step Tutorial Number 1 (dotnetperls)
Step by Step Tutorial Number 2 (dreamincode)
Most important thing is to set this property:
bw.WorkerReportsProgress = true;
And in the DoWork-Function, make sure you report the progress to the UI, as given in the linked examples:
worker.ReportProgress((i * 10));
Also, don't forget to handle the progresschanged event in the UI:
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
Go ahead, and you will see: it is fairly simple.
When using a background worker you should be doing all of the non-UI processing in the background worker; you should not be using it to update the UI while still doing processing in the UI thread.
By moving the work to the DoWork handler of the BGW you free up the UI thread to allow it to process UI events, including the updating of the progress bar.
private void btnCreate_Click(object sender, EventArgs e)
{
Progressbar1.Minimum = 1;
Progressbar1.Maximum = dt.Rows.Count;
Progressbar1.Value = 1;
Progressbar1.Step = 1;
for (int i = 1; i <= dt.Rows.Count; i++)
{
bool result=InsertUserdetails(Username);
if(result== true)
{
Progressbar1.PerformStep();
}
}
}
I have only a little experience with threading in WinForms. I am trying to set a progress bar to 100, sleep for 2 seconds, and then hide the progress bar.
I have:
public void ProgressBarTimerStop()
{
if (!AnyWorkersBusy())
{
if (InvokeRequired)
{
Invoke((MethodInvoker)delegate
{
ProgressBarTimer.Stop();
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
});
}
else
{
ProgressBarTimer.Stop();
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
}
}
}
For the current problem I am experiencing InvokeRequired is false.
When I call the line:
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
my watch window shows the value as set, but the GUI has not updated to reflect this. If I immediately sleep for a full two seconds the GUI does not update before sleeping.
If I call Application.DoEvents() without calling sleep beforehand -- it appears to do nothing.
If I sleep, then call DoEvents -- the GUI updates, and then I go back to sleep. This is correct, but I feel like my code is heinous.
Am I misunderstanding a key concept here?
EDIT: ProgressBarTimerStop is called only through "RunWorkerCompleted" event handlers.
I tried this, but it had no effect:
StatusToolStripProgressBar.GetCurrentParent().Refresh();
The 'key concept' that you are missing is that updating the progress bar's value does not immediately tell the progress bar to redraw itself.
When you update the progress bar, the GUI thread repaints the progress bar on the next window repaint. This only happens when the GUI thread becomes free to do so. Normally this happens when the window receives a paint message from the operating system. This should be picked up by Application.DoEvents, but it could be that the message isn't queued quickly enough to be picked up by DoEvents. You may be able to force it to with a Refresh call.
Call StatusToolStripProgressBar.ProgressBar.Refresh(); on every step. ProgressBar property of ToolStripProgressBar is hidden and it may not be shown in intellisense list.
Why is the picture box control's visibility property not working here. I have initially set them to false, so that when the screen loads they are not visible. But then I wish to toggle this. I have done the following but does not seem to work. This is a windows forms application.
private void Action()
{
while (true)
{
Random r1 = new Random();
int num = r1.Next(1,3);
switch (num)
{
case 1:
pictureBoxLeft.Visible = true;
pictureBoxRight.Visible = true;
break;
case 2:
pictureBoxLeft.Visible = true;
pictureBoxRight.Visible = false;
break;
case 3:
pictureBoxLeft.Visible = false;
pictureBoxRight.Visible = true;
break;
}
Thread.Sleep(200);
pictureBoxLeft.Visible = false;
pictureBoxRight.Visible = false;
Thread.Sleep(500);
}
}
Also to add, this is working properly with a text box!!! Any Ideas???
Many thanks in advance
Setting the Visible property to true doesn't show the control, it only creates a message that will show it. As long as you are keeping the main thread busy with a Sleep, it will not process any messages, and the controls won't be shown.
You should show the picture boxes and then set up a timer with code that will hide the picture boxes when the timer's tick event is triggered. Then you exit your method so that the main thread can handle the messages that will show the picture boxes.
This is your UI thread. UI thread is so busy that it is not getting time to refresh the display. UI thread is busy in endless while loop, so how can it update UI?
In addition to Thread.Sleep(); call Application.DoEvents() to trigger event handling/ui updating. However ensure you're not calling Action() again somehow. A simple Thread.Sleep() won't allow the UI to update as you're still in the handler and the framework won't be able fire any new events on its own as it won't know if you're finished when waiting (due to the thread still being busy).
Overall your approach might be a bad idea (depending on other code etc.). Consider using timers or a background worker instead.
Whenever the UI changes does not apear after you did some changes, always do Refres(). e.g. pictureBoxLeft.Refresh();. This will always make UI changes appear. Please let me know if it works for you.
I have the follow button click event:
private void btnRun_Click(object sender, EventArgs e)
{
label1.Visible = true;
if (SelectDatabase())
{
if (string.IsNullOrEmpty(txtFolderAddress.Text))
MessageBox.Show("Please select a folder to begin the search.");
else
{
if (cbRecurse.Checked == false || Directory.GetDirectories(initialDirectory).Length == 0)
{
CheckSingleFolder();
}
else
{
CheckSingleFolder();
directoryRecurse(initialDirectory);
}
}
}
}
Effectively, it does a few checks and then starts some directory recursion looking for specific files. However, the very first line of code to make the label visible does not occur until after the directories have been recursed? Anybody know why this would be?
Thanks.
You're doing everything within the UI thread, which is a really bad idea - the UI doesn't get to update, react to events etc until you've finished.
You should use a background thread and update the UI with progress etc using Control.BeginInvoke, or perhaps use a BackgroundWorker.
Basically, there are two golden rules in WinForms (and similar with WPF/Silverlight):
Don't do anything which can take a significant amount of time in the UI thread
Don't touch any UI elements from any thread other than the UI thread
your whole method runs as a blocking unit currently - add an Application.DoEvents() as a workaround, but really you should be doing this kind of processing in a background thread, i.e. using a background worker.
The code is executing on the same thread which is drawing your user-interface. Therefore, while the code is executing, your UI is not being re-drawn. Once the button-click code has finished, the UI is redrawn and label1 gets drawn invisibly.
You can move your code into a separate thread using, for example, Task or BackgroundWorker. However, you cannot directly set UI properties from a different thread, so you will need to either be careful to set your UI properties from the UI thread or see this question about how to update the GUI from another thread.
The view is not updated until the code block finished. So I would propose a BackgroundWorker for the recursion part.
The explanation: the label is set to visible and it it is Invalidated (needs repainting) but the Windows message pump doesn't start repainting until it is running idle. So your code blocks it.
A simple solution is to call label1.Update() right after setting it visible.
A better solution is to move the time-consuming code to a thread (Backgroundworker).