Thread.Sleep() in C# - c#

I want to make an image viewer in C# Visual Studio 2010 which displays images one by one after seconds:
i = 0;
if (image1.Length > 0) //image1 is an array string containing the images directory
{
while (i < image1.Length)
{
pictureBox1.Image = System.Drawing.Image.FromFile(image1[i]);
i++;
System.Threading.Thread.Sleep(2000);
}
When the program starts, it stops and just shows me the first and last image.

Thread.Sleep blocks your UI thread use System.Windows.Forms.Timer instead.

Use a Timer.
First declare your Timer and set it to tick every second, calling TimerEventProcessor when it ticks.
static System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Tick += new EventHandler(TimerEventProcessor);
myTimer.Interval = 1000;
myTimer.Start();
Your class will need the image1 array and an int variable imageCounter to keep track of the current image accessible to the TimerEventProcessor function.
var image1[] = ...;
var imageCounter = 0;
Then write what you want to happen on each tick
private static void TimerEventProcessor(Object myObject, EventArgs myEventArgs) {
if (image1 == null || imageCounter >= image1.Length)
return;
pictureBox1.Image = Image.FromFile(image1[imageCounter++]);
}
Something like this should work.

Yes, because Thread.Sleep blocks the UI thread during the 2s.
Use a timer instead.

If you want to avoid using Timer and defining an event handler you can do this:
DateTime t = DateTime.Now;
while (i < image1.Length) {
DateTime now = DateTime.Now;
if ((now - t).TotalSeconds >= 2) {
pictureBox1.Image = Image.FromFile(image1[i]);
i++;
t = now;
}
Application.DoEvents();
}

Related

wpf Multiple dispatch timer for timer application : Run multiple timer simulteniously

Read multiple stackoverflow, codeproject solution, could not integrate to my problem.
Have a datagrid in a usercontrol which is loaded in a window. Each DataRow in the DataGrid represents a timer setting.
Like:
timer name : Test 1 , Timer : 1h 3m
timer name : Test 2 , Timer : 2h 2m
timer name : Test 3 , Timer : 3h 1m
Selecting a row, clicking on the button Start, Starts the timer of that row. And with dispatcher tick event, it updates the grid I have done till this. Now I have to start another(or two or ...) timer which will do the same at the same time. I am stuck on this. Let me share what I have tried!
btnStartClickEvent in mainwindow.xaml.cs
if (btnStart.Content.ToString() == "Start")
{
if (_AUC == ActiveUserControl.Grid)
{
runningRow = (TaskGridData)_TG.dgEmployee.SelectedItem;
if (runningRow != null)
{
currentlyRunningID.Add(runningRow.ID);
btnStart.Content = "Stop";
//worker.RunWorkerAsync(runningRow);
StartTimer(runningRow);
}
}
}
else if (btnStart.Content.ToString() == "Stop")
{
btnStart.Content = "Start";
StopTimer();
}
private DateTime TimerStart { get; set; }
private void StartTimer(TaskGridData tgd)
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1, 0);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimerStart = DateTime.Now;
dispatcherTimer.Start();
//worker.RunWorkerAsync();
//string etime = DateTime.Now.Second.ToString();
}
private void StopTimer()
{
dispatcherTimer.Stop();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
var currentValue = DateTime.Now - TimerStart;
runningRow.Duration = DurationValueToString(currentValue);
temp = (List<TaskGridData>)_TG.dgEmployee.ItemsSource;
foreach (TaskGridData item in temp)
{
if (item.ID == runningRow.ID)
{
item.Duration = DurationValueToString(DurationStringToVlaue(item.Duration) - DurationStringToVlaue(runningRow.Duration));
break;
}
}
//_TG.dgEmployee.ItemsSource = null;
//_TG.dgEmployee.ItemsSource = temp;
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
}
private void UpdateGrid()
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_TG.dgEmployee.ItemsSource = null;
_TG.dgEmployee.ItemsSource = temp;
}));
}
I know this code is for single timer. If I click a 2nd row and try to start timer, then it gets error in tick event, running row is found null.
I am wondering how can I keep this code and make it work for multiple timer. May be multithreading. A guide to do that, will be very helpful.
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
All the above part where you start a new STA thread is unneeded and wrong in this context, since you can't update the visual tree in this way.
You can find a correct example of using a STA thread in one of my previous answers: https://stackoverflow.com/a/42473167/6996876
Try to understand the concept of thread affinity in WPF.
You simply need an UpdateGrid() where you have to delegate UI work to the dispatcher.
Furthermore, passing an argument to the Tick event is already explained here: https://stackoverflow.com/a/16380663/6996876
In your case you may want to change the current unique runningRow so that it's passed to the event instead.

How to return control of flow to a for loop?

I'm trying to code a loop to re execute a block of code 3 times. At the moment the code starts and executes once but doesn't repeat as intended with using the for loop.
I've set a break point on the for loop and it only goes through the loop once.
private async void startBtn_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
int i;
int roundMax = 3;
for (i = 1; i <= roundMax; i++)
{
//delay stop watch start to allow time to get ready.
TimeSpan time = TimeSpan.FromSeconds(1);
await System.Threading.Tasks.Task.Delay(time);
//set text box editing to false to prevent illegal input.
wrkTbx.IsEnabled = false;
restTbx.IsEnabled = false;
roundSlider.IsEnabled = false;
roundsTbx.IsEnabled = false;
StopGoCvs.Background = new SolidColorBrush(Colors.Green);
//startSoundElmt.Play();
// set up the timer
myTimer = new DispatcherTimer();
myTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
myTimer.Tick += myTimer_Tick;
// start both timers
myTimer.Start();
myStopwatch.Start();
}
}
this is a logical issue, and I suspect it exits the for loop at some point, Assumming you're using Visual Studio, I would use Breakpoints at every steps and run the program to see where it fails, you may be missing a return or else somewhere

WPF Dispatchertimer is not stopping, nor restarting

I want a DispatcherTimer to restart everytime the conditions are not met. Only when the if-condition is met for 5 seconds, the method can continue.
How should I stop the Dispatchertimer? The timeToWait variable is set to 3000, that works as intended.
Below is the code in C#. It is not responding as I want. It only starts, but never stops or restarts. I am making a WPF application.
dispatcherTimerStart = new DispatcherTimer();
if (average >= centerOfPlayingField - marginOfDetection && average <= centerOfPlayingField + marginOfDetection)
{
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
dispatcherTimerStart.Tick += new EventHandler(tick_TimerStart);
startTime = DateTime.Now;
dispatcherTimerStart.Start();
} else
{
dispatcherTimerStart.Stop();
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
}
private void tick_TimerStart(object sender, EventArgs args)
{
DispatcherTimer thisTimer = (DispatcherTimer) sender;
thisTimer.Stop();
}
you need to preserve the dispatcherTimer that enter your if block because in your else block you are stopping the new instance of DispatcherTimer not the one that entered the if block.
take a class level field
DispatcherTimer preservedDispatcherTimer=null;
var dispatcherTimerStart = new DispatcherTimer();
if (average >= centerOfPlayingField - marginOfDetection && average <= centerOfPlayingField + marginOfDetection)
{
**preservedDispatcherTimer = dispatcherTimerStart;**
dispatcherTimerStart.Interval = TimeSpan.FromMilliseconds(timeToWait);
dispatcherTimerStart.Tick += new EventHandler(tick_TimerStart);
startTime = DateTime.Now;
dispatcherTimerStart.Start();
}
//use preservedDispatcherTimer in else
else if(preservedDispatcherTimer!=null)
{
preservedDispatcherTimer.Stop();
preservedDispatcherTimer.Interval = TimeSpan.FromMilliseconds(timeToWait);
}

C# Set duration and play sounds stored in an arraylist consecutively

I have the following Play method for playing musical notes stored in an arraylist:
public void Play(Duration d){
int duration = 1;
if (d == Duration.SemiBreve)
{
duration = 16;
}
else if (d == Duration.DotMin)
{
duration = 10;
}
else if (d == Duration.minim)
{
duration = 8;
}
else if (d == Duration.Crotchet)
{
duration = 4;
}
else if (d == Duration.Quaver)
{
duration = 2;
}
else if (d == Duration.SemiQuaver)
{
duration = 1;
}
player = new SoundPlayer();
player.SoundLocation = "pathToLocation";
//set timer
time = new Timer();
time.Tick += new EventHandler(clockTick);
time.Interval = duration * 150;
player.Play();
time.Start();
}
When I call the method with a button:
private void PlayButton_Click(object sender, EventArgs e)
{
//Loops through the collection and plays each note one after the other
foreach (MusicNote music in this.staff.Notes)
{
music.Play(music.Dur)
}
}
Only the last note gets played. With PlaySync() all the notes get played but the duration isn't recognized. I also tried using threads like so:
foreach (MusicNote music in this.staff.Notes)
{
Thread t = new Thread(playMethod=>music.Play(music.Dur));
t.Start();
t.Join();
}
But it doesn't work either. Any suggestions as to how I can get the files to play consecutively like with PlaySync but using their set duration?
You don't wait for the timer anywhere. So in practice all notes get played almost simultaneously, causing just the last one to be effectively audible.
UPDATE: Using the System.Timers.Timer class the way to go is registering a handler for the Elapsed event and playing the notes one-by-one in the event handler. This way you avoid freezing the UI.
Another option is playing in a background thread and the using Thread.Sleep to wait. Here you will have to make sure the thread is stopped according to the state of the UI (i.e. the use closes the current dialog etc.).
In either case to avoid race conditions you will have to solve concurrent access to staff.Notes or make a copy of it for the sake of playing it.

The calling thread cannot access this object because a different thread owns it

So I am making a simple brick breaking game in c#/wpf. I am running into an issue using timers, I feel like it is probably a simple fix but here is whats happening. Whenever t_Elapsed is fired it attempts to call Update() but when it does its like OMG Im not in the right thread so I cant do that sir. How do I invoke the method from the Game from the proper thread? (And yes I know the code is ugly and has magic numbers but I just kinda chugged it out without putting a lot of effort in. And yes I have zero experience programming games)
public partial class Game : Grid
{
public bool running;
public Paddle p;
public Ball b;
Timer t;
public Game()
{
Width = 500;
Height = 400;
t = new Timer(20);
p = new Paddle();
b = new Ball();
for (int i = 15; i < 300; i += 15)
{
for (int j = 15; j < 455; j += 30)
{
Brick br = new Brick();
br.Margin = new Thickness(j, i, j + 30, i + 15);
Children.Add(br);
}
}
Children.Add(p);
Children.Add(b);
p.Focus();
t.AutoReset = true;
t.Start();
t.Elapsed += new ElapsedEventHandler(t_Elapsed);
}
void t_Elapsed(object sender, ElapsedEventArgs e)
{
if (running)
{
Update();
}
}
void Update()
{
b.Update(); //Error here when Update is called from t_Elapsed event
}
void Begin()
{
running = true;
b.Initiate();
}
}
You should use the DispatcherTimer object instead, it will ensure that the timer events are published to the correct thread.
Timer elapsed events fire on a thread from the thread pool (http://www.albahari.com/threading/part3.aspx#_Timers) and not on the UI thread. Your best approach is to invoke the control's dispatcher through a call like this:
yourControl.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal
, new System.Windows.Threading.DispatcherOperationCallback(delegate
{
// update your control here
return null;
}), null);
The calling thread cannot access this object because a different thread owns it
this.Dispatcher.Invoke((Action)(() =>
{
...// your code here.
}));

Categories