How to correct the code that each newly launched thread use the new counter. At the moment when you start a new thread hangs the old, instead of going on.
Thanks for help.
private void button1_Click(object sender, EventArgs e)
{
thread[counter] = new Thread(goThread);
thread[counter].Start();
counter++;
}
private void goThread()
{
kolejka[counter] = new PictureBox();
kolejka[counter].Location = new Point(325, n - 150);
kolejka[counter].Image = threading.Properties.Resources.car;
kolejka[counter].Size = new Size(20, 37);
this.Invoke((MethodInvoker)delegate
{
this.Controls.Add(kolejka[counter]);
});
for (int i = 0; i < 500; i++)
{
this.Invoke((MethodInvoker)delegate
{
kolejka[counter].Location = new Point(kolejka[counter].Location.X, kolejka[counter].Location.Y - 3);
this.Refresh();
});
Thread.Sleep(50);
}
}
The problem is that you're increasing the counter variable, but it is used within your threads. Don't do that. In your case it is very important to make information local to the thread, because you want each thread to work on "its own" counter. This can be achieved like this:
private class ThreadInfo
{
public PictureBox Picture;
public int Counter;
}
private void button1_Click(object sender, EventArgs e)
{
kolejka[counter] = new PictureBox();
kolejka[counter].Location = new Point(325, n - 150);
kolejka[counter].Image = threading.Properties.Resources.car;
kolejka[counter].Size = new Size(20, 37);
this.Controls.Add(kolejka[counter]);
ThreadInfo info = new ThreadInfo() {
Picture = kolejka[counter],
Counter = counter
};
thread[counter] = new Thread(goThread);
thread[counter].Start(info);
counter++;
}
private void goThread(object state)
{
ThreadInfo info = state as ThreadInfo;
for (int i = 0; i < 500; i++)
{
this.Invoke((MethodInvoker)delegate
{
info.Picture.Location = new Point(info.Picture.Location.X, info.Picture.Location.Y - 3);
this.Refresh();
});
Thread.Sleep(50);
}
}
This does all the initialization stuff in your button event and passes in an instance of an info class. That info class takes all the information the thread needs, but so that it is local to the thread!
Your old thread is not hanging. The problem is in your counter variable. It is shared by you threads. The old thread just continues on the kolejka[counter] of the new thread. I guess that is not what your want.
In the beginning of your goThread method you can do something like:
var item = kolejka[counter];
And then you can use item instead of kolejka[counter]. However this is not thread safe yet either, but a lot better then you have now.
There are several problems with your code.
You use a variable counter without locking in two threads.
Don't use arrays for this, because you don't have the counter value in control.
Don't create controls on other threads than the gui thread.
For this purpose, you don't need threads. The easiest way is using one timer.
PSEUDO:
List<Car> _myCars = new List<Car>();
private Form1()
{
_timer = new Timer();
_timer.Interval = 50;
_timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
foreach(var car in _myCars.ToArray())
{
car.Location = new Point(car.Location.X, car.Location.Y - 3);
if(car.Location.Y < 0)
_myCars.Remove(car);
}
}
private void button1_Click(object sender, EventArgs e)
{
_myCars.Add(new Car());
}
Follow up on Peter, you could create a copy at the beginning of the thread:
private void button1_Click(object sender, EventArgs e)
{
ManualResetEvent threadStartedSignal = new ManualResetEvent(false);
thread[counter] = new Thread(goThread);
thread[counter].Start(threadStartedSignal);
// wait for the thread to create a local reference.
threadStartedSignal.WaitOne();
counter++;
}
private void goThread(object state)
{
kolejka[counter] = new PictureBox();
var myPictureBox = kolejka[counter];
// signal the other thread, that the counter may be changed.
((ManualResetEvent)state).Set();
myPictureBox .Location = new Point(325, n - 150);
myPictureBox .Image = threading.Properties.Resources.car;
myPictureBox .Size = new Size(20, 37);
this.Invoke((MethodInvoker)delegate
{
this.Controls.Add(myPictureBox );
});
for (int i = 0; i < 500; i++)
{
this.Invoke((MethodInvoker)delegate
{
myPictureBox.Location = new Point(myPictureBox.Location.X, myPictureBox.Location.Y - 3);
this.Refresh();
});
Thread.Sleep(50);
}
}
Related
My programm (in C# using Windows Forms) is reading and parsing large amounts of Data and I'm using a Backgroundworker which calls those global methods (reading and parsing). I'd like to keep the user updated on how long it's going to take, so the Backgroundworker is supposed to display what action its doing and has a progressbar that should fill for every individual action too.
Unfortunately, I can't get it to work, as the progressbar just doesn't update at all and just stays empty.
Here is what I have so far:
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void buttonParse_Click(object sender, EventArgs e)
{
DescriptionLabel.Visible = true;
progressBar1.Visible = true;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Methods.ParsePerfusionData(backgroundWorker1); //Also tried using 'worker' here, but didnt work either
}
And in the method it looks like that:
public static void ParsePerfusionData(BackgroundWorker worker)
{
for (int i = 2; i < Globals.DataList.Count; i++)
{
worker.ReportProgress(i / amount * 100);
rest of the code etc.
}
}
Can I not use a backgroundworker in a global method like that? Thanks in advance!
When i < amount then i / amount * 100 = 0 * 100 = 0.
Simply use i * 100 / amount instead.
Also make sure backgroundWorker1.WorkerReportsProgress = true
You can only report progress between distinct operations. That means either:
using a very modern class that supports this level of reporting. Such a classs might not exist for your case.
reverse engineering parts of the code down to the loop you want to make reporting on. Usually the loop that itterates over files or the like.
GUI updates must be contained to RunWorkerCompelted and ProgressReport events. And depending on how often updates happen, ProgressReport may have to be kept to only updating a progress bar.
Here some old code I wrote with BackgroundWorker wich should get you started:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
For some reason, This freezes the program:
private void Form1_Shown(object sender, EventArgs e)
{
int x = 1;
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while(x < 2)
{
pictureBox1.BackgroundImage = frame1;
pictureBox1.BackgroundImage = frame2;
}
}
why?
pictureBox2 contains the first frame and pictureBox3 contains the second.
pictureBox1 contains the 'animation'. (as seen in code)
EDIT: I don't want it to animate ONCE, I want it to animate FOREVER.
Here's one way...
Mark your Shown() event with async, then use await Task.Delay() between frames:
private async void Form1_Shown(object sender, EventArgs e)
{
bool first = true;
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while (true)
{
pictureBox1.BackgroundImage = first ? frame1 : frame2;
first = !first;
await Task.Delay(500); // 1/2 second delay <-- set it to your desired delay between frames
}
}
---------- Edit ----------
An alternate approach using a Timer and an IEnumerator. This would be a good way to go if you had more than just two frames:
private IEnumerator<Image> frames;
private System.Windows.Forms.Timer tmr;
private void Form1_Shown(object sender, EventArgs e)
{
List<Image> lstFrames = new List<Image>();
lstFrames.Add(pictureBox2.BackgroundImage);
lstFrames.Add(pictureBox3.BackgroundImage);
lstFrames.Add(pictureBox4.BackgroundImage);
// etc...
frames = lstFrames.GetEnumerator();
DisplayNextFrame();
tmr = new System.Windows.Forms.Timer();
tmr.Interval = 500;
tmr.Tick += Tmr_Tick;
tmr.Start();
}
private void Tmr_Tick(object sender, EventArgs e)
{
DisplayNextFrame();
}
private void DisplayNextFrame()
{
if (!frames.MoveNext())
{
frames.Reset();
frames.MoveNext();
}
pictureBox1.BackgroundImage = frames.Current;
}
In your case, the value of x is always 1 and that is why the while loop keep iterating and the program goes in an infinite loop.
Solution:
You should use BackgroundWorker to run the code as an Asynchronous
Insert a new BackgroundWorker into your form.
Insert your function into DoWork event.
private void bWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var frame1;
var frame2;
if (pictureBox2.InvokeRequired) { pictureBox2.Invoke((MethodInvoker)delegate { frame1 = pictureBox2.BackgroundImage; }); } else { frame1 = pictureBox2.BackgroundImage; }
if (pictureBox3.InvokeRequired) { pictureBox3.Invoke((MethodInvoker)delegate { frame2 = pictureBox3.BackgroundImage; }); } else { frame2 = pictureBox3.BackgroundImage; }
while (true)
{
if (pictureBox1.InvokeRequired) { pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.BackgroundImage = frame1; }); } else { pictureBox1.BackgroundImage = frame1; }
if (pictureBox1.InvokeRequired) { pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.BackgroundImage = frame2; }); } else { pictureBox1.BackgroundImage = frame2; }
}
}
Now, Start running BackgroundWorker.
bWorker1.RunWorkerAsync();
I think you should add some time delays between the frame1 and frame2 inside while loop to see the animation work.
Your loop runs forever and never terminates.
There's no termination clause for the while (x < 2) loop hence your program freezes; x is always smaller than 2. If you don't allow the application to breath it freezes. Try this:
private void Form1_Shown(object sender, EventArgs e)
{
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while(true)
{
pictureBox1.BackgroundImage = frame1;
Application.DoEvents(); // give the thread room to run background tasks
pictureBox1.BackgroundImage = frame2;
Application.DoEvents(); // give the thread room to run background tasks
}
}
DoEvents() is just one option, maybe not the best for your case. You're probably better off writing a proper timer which switches between the two images ever 40 milliseconds or whatever your animation speed is supposed to be.
Create a parallel thread for your animiation:
private void Form1_Shown(object sender, EventArgs e)
{
//create parallel thread
Thread animationThread = new Thread(Animation);
animationThread.Start();
}
//create a new method for animation to run on a parallel thread
private void Animation()
{
int x = 1;
var frame1 = pictureBox1.Image;
var frame2 = pictureBox2.Image;
while (x < 2)
{
this.Invoke(new Action(() => pictureBox3.Image = frame1));
this.Invoke(new Action(() => pictureBox3.Refresh()));
Thread.Sleep(100); //you must keep a dealy other wise it will consume too much processing
this.Invoke(new Action(() => pictureBox3.Image = frame2));
this.Invoke(new Action(() => pictureBox3.Refresh()));
}
}
I don't have a thorough knowledge about controlling threads in c#. I want to create a progress bar while running a method.
I have two forms:
Form1 is the form which show up as you run the app. It has a button called btnScrape. When it is clicked the method should be called, the form with the progress bar should show up. Form1 should be disabled to the user until the progress bar is completed.
ProgressBarForm - this has the progress bar and a label.
The code is as follows.
//In Form1.cs I have a button.
private void btnScrape_Click(object sender, EventArgs e)
{
//gather data for folloeing parameters from the form.
Controller cntrlr = new Controller(urlFilePath, destinationPath, destinationfilename,cmbDepth.SelectedIndex);
cntrlr.Vmain(); // this is the method in controller class. while this is running i want show the progress bar.
}
// in Contrller class
class Controller{
List<string> urlList = null;
URLFileReader urlFileReader = null;
HTTPWorker httpWorker = null;
SourceReader srcreader = null;
ReportWriter reportWriter = null;
string urlFilePath, destinationPath, destinationFileName;
int depth;
public Controller(string urlFilePath,string destinationPath,string destinationFileName,int depth)
{
this.urlFilePath = urlFilePath;
this.destinationPath = destinationPath;
this.destinationFileName = destinationFileName;
this.urlList = new List<string>();
this.urlFileReader = new URLFileReader();
this.httpWorker = new HTTPWorker(this.destinationPath);
this.reportWriter = new ReportWriter(this.destinationPath,this.destinationFileName);
this.srcreader = new SourceReader(this.reportWriter);
this.depth = depth;
}
//this is the method
public void Vmain(){
this.urlFileReader.ReadURL(urlFilePath);
this.urlList = urlFileReader.geturlList();
string pageSrc;
foreach (string requestUrl in urlList)
{
//do sruff for requestUrl
//the progressbar should move as the urlList iterate.
//additionally i want the label on the top of progress bar to display the current "requestUrl"
//as the urlList is over i want quit from the progressbar window and come back to Form1. Till the progrss bar is completed Form1 should be disabled for the user.
}
}
}
Please explain what is happening there and give a working code if you can. Thank you in advance. There were not any perfect answer that worked for me, even though I spent two days for this. I tried with BackgroundWorkerand threads. But no solution found. :(
In the main form you could use this code:
private Progressfrm _loadForm;
private void ShowProgress()
{
ToggleForm();
_loadForm = new Progressfrm();
_loadForm.ShowDialog();
var tcheck = new Thread(CheckLoadedProgress);
tcheck.Start();
//do stuff here
}
private void CheckLoadedProgress()
{
while (_loadForm.IsAccessible) { }
ToggleForm();
}
private void ToggleForm()
{
Invoke(new Action(() => Enabled = !Enabled));
}
private void btnScrape_Click(object sender, EventArgs e)
{
var tform = new Thread(ShowProgress);
tform.Start();
}
Then the Progress-Form will appear until it is filled:
private ProgressBar _progressBar;
private void Progressfrm_Shown(object sender, EventArgs e)
{
_progressBar = new ProgressBar { Size = new Size(100, 20), Location = new Point(10, 10) };
Controls.Add(_progressBar);
_progressBar.Show();
Refresh();
LoadProgress();
}
private void LoadProgress()
{
while (_progressBar.Value < 100)
{
_progressBar.Value++;
Thread.Sleep(100);
}
Close();
}
On this Form you have to add the Event Shown and add the code like in my example. Hope this helps.
Use BackgroundWorker class to output progressBar and statusLabel changes:
BackgroundWorker bgw;
private void btnScrape_Click(object sender, EventArgs e)
{
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
bgw.ReportProgress(i);
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
This is only an example how to update controls in an asynchron way.
I'm a tad new to C# so please bear with me!
I am writing a program to send code via RS232 to a home made telescope mount.
The issues I have at the moment is hopefully very simple (But quite difficult for me!)
As an example say I have a button, I want to execute a loop when the left mouse button is held down, (which will be a continuous stream of 232 data), then when the left mouse button is released I need the loop to stop and to execute another line of code.
I sincerely hope that information I have given is enough and somebody is kind enough to help me along (I have searched the internet for answers believe me!)
Many thanks.
Hook into the MouseDown and MouseUp events on the button. The MouseDown event should spawn a thread, or signal to the thread to begin executing the loop. The MouseUp event should signal to the thread to stop executing the loop.
Something like this:
public class InterruptibleLoop
{
private volatile bool stopLoop;
private Thread loopThread;
public void Start() {
// If the thread is already running, do nothing.
if (loopThread != null) {
return;
}
// Reset the "stop loop" signal.
stopLoop = false;
// Create and start the new thread.
loopThread = new Thread(LoopBody);
loopThread.Start();
}
public void Stop() {
// If the thread is not running, do nothing.
if (loopThread == null) {
return;
}
// Signal to the thread that it should stop looping.
stopLoop = true;
// Wait for the thread to terminate.
loopThread.Join();
loopThread = null;
}
private void LoopBody() {
while (!stopLoop) {
// Do your work here
}
}
}
Method 1:
First create a timer with the interval set to the frequency you want it to send the data. And send the data in the tick event. Create an event for the button's mouse down event and the buttons' mouse up event. In the mouse down event, start the timer. In the mouse up event, stop the timer.
Method 2:
Instead of starting a timer on the mouse down event, start a new thread where you do a continuous loop of data sending. Stop the thread on the mouse up event.
namespace Scope_Project_Ver_2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// *** Output data timer ***
otimer.Interval = 50;
// otimer.Interval = isendFreq;
otimer.Tick += new EventHandler(otimer_Tick);
// *** Input data timer ***
// itimer.Interval = 601; <- to be unchecked
// itimer.Tick += new EventHandler(itimer_Tick); <- to be unchecked
}
public int b1,b2,b3,b4,b5;
public string sb1, sb2, sb3, sb4, sb5;
public int ivorSpeed;
public string svorSpeed;
public int ihorSpeed;
public string shorSpeed;
public int isendFreq;
private void sendDataB_Click(object sender, MouseEventArgs e)
{
if (sendDataCB.Checked)
{
sendDataCB.Checked = false;
if (otimer.Enabled)
otimer.Stop();
}
else
{
sendDataCB.Checked = true;
if (!otimer.Enabled)
otimer.Start();
}
}
void otimer_Tick(object sender, EventArgs e)
{
SerialPort port = new SerialPort(
"COM1", 9600, Parity.None, 8, StopBits.One);
port.Open();
port.Write("Q"); // Q
port.Write(sb1); // 1
port.Write(sb2); // 2
// Binary stuff // Ver Speed Binary 3
byte[] bverbinary = new byte[1];
byte verbinary = 0;
verbinary = Byte.Parse(svorSpeed);
bverbinary[0] = verbinary;
port.Write(bverbinary, 0, bverbinary.Length);
// End of Binary stuff for Ver Speed
// Binary stuff // Hor Speed Binary 4
byte[] bhorbinary = new byte[1];
byte horbinary = 0;
horbinary = Byte.Parse(shorSpeed);
bhorbinary[0] = horbinary;
port.Write(bhorbinary, 0, bhorbinary.Length);
port.Write(sb5); // Movement 5
port.Close();
}
private void vorSpeed_ValueChanged(object sender, EventArgs e)
{
// MessageBox.Show((this.vorSpeed.Value).ToString());
this.Text = "You changed the Vertical Speed to " + vorSpeed.Value;
ivorSpeed = (int)vorSpeed.Value;
svorSpeed = ivorSpeed.ToString();
}
private void horSpeed_ValueChanged(object sender, EventArgs e)
{
// MessageBox.Show((this.horSpeed.Value).ToString());
this.Text = "You changed the Horizontal Speed to " + horSpeed.Value;
ihorSpeed = (int)horSpeed.Value;
shorSpeed = ihorSpeed.ToString();
}
private void scopeUp_MouseDown(object sender, MouseEventArgs e) // Scope Up On
{
b1 = 2;
b2 = 0;
b5 = 1;
sb1 = b1.ToString();
sb2 = b2.ToString();
sb3 = b3.ToString();
sb4 = b4.ToString();
sb5 = b5.ToString();
}
private void scopeUp_MouseUp(object sender, MouseEventArgs e) // Scope Up Off
{
}
private void scopeRight_MouseDown(object sender, MouseEventArgs e)
{
b1 = 1;
b2 = 2;
b5 = 1;
sb1 = b1.ToString();
sb2 = b2.ToString();
sb3 = b3.ToString();
sb4 = b4.ToString();
sb5 = b5.ToString();
}
private void scopeRight_MouseUp(object sender, MouseEventArgs e)
{
}
private void scopeDown_MouseDown(object sender, MouseEventArgs e)
{
b1 = 2;
b2 = 1;
b5 = 1;
sb1 = b1.ToString();
sb2 = b2.ToString();
sb3 = b3.ToString();
sb4 = b4.ToString();
sb5 = b5.ToString();
}
private void scopeDown_MouseUp(object sender, MouseEventArgs e)
{
}
private void scopeLeft_MouseDown(object sender, MouseEventArgs e)
{
b1 = 0;
b2 = 2;
b5 = 1;
sb1 = b1.ToString();
sb2 = b2.ToString();
sb3 = b3.ToString();
sb4 = b4.ToString();
sb5 = b5.ToString();
}
private void scopeLeft_MouseUp(object sender, MouseEventArgs e)
{
}
private void scopeStop_Click(object sender, EventArgs e)
{
b1 = 0;
b2 = 0;
b5 = 0;
sb1 = b1.ToString();
sb2 = b2.ToString();
sb3 = b3.ToString();
sb4 = b4.ToString();
sb5 = b5.ToString();
}
private void sendFreq_ValueChanged(object sender, EventArgs e)
{
this.Text = "You changed the send Freq to " + sendFreq.Value + " m/s";
isendFreq = (int)sendFreq.Value;
}
}
}
With reference to the Software Project I am currently working on.
I have the below methods that basically move a canvas with a Timer:
DispatcherTimer dt = new DispatcherTimer(); //global
public void Ahead(int pix)
{
var movx = 0;
var movy = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler((object sender, EventArgs e) =>
{
if (movx >= pix || movy >= pix)
{
dt.Stop();
return;
}
Bot.Body.RenderTransform = new TranslateTransform(movx++, movy++);
});
dt.Start();
}
public void TurnLeft(double deg)
{
var currAngle = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (currAngle <= (deg - (deg * 2)))
{
dt.Stop();
}
Bot.Body.RenderTransform = new RotateTransform(currAngle--, BodyCenter.X, BodyCenter.Y);
});
dt.Start();
}
Now, from another library, these methods are called like such:
public void run()
{
Ahead(200);
TurnLeft(90);
}
Now of course, I want these animations to happen after another, but what is happening is that the dt.Tick event handler of the DispatchTimer is being overwritten when the second method (in this case, TurnLeft(90)) is invoked and thus, only the second method gets executed as it should.
I need to create some sort of queue that will allow me to push and pop methods to that queue so that dt (the DispatchTimer timer) executes them one by one...in the order they are in the 'queue'
Any way I can go about doing this ? Am I on the right track here, or completely off course?
When you call Invoke() or BeginInvoke() on the Dispatcher, the operation will be queued up and run when the thread associated with the Dispatcher is free. So instead of using the Tick event, use the overload of Dispatcher.Invoke that takes a Timespan.
I have fixed this problem by myself. What I did was create a global Queue of type Delegate and instead of executing the methods directly, I add them to this queue.
Then I would have a separate thread in the constructor that will dequeue methods one by one and executing them:
Queue<TimerDelegate> eventQueue = new Queue<TimerDelegate>();
public Vehicle(IVehicle veh, Canvas arena, Dispatcher battleArenaDispatcher)
{
DispatcherTimer actionTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
actionTimer.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (IsActionRunning || eventQueue.Count == 0)
{
return;
}
eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
});
actionTimer.Start();
}
public void TurnRight(double deg)
{
eventQueue.Enqueue((TimerDelegate)delegate(DispatcherTimer dt)
{
IsActionRunning = true;
var currAngle = 0;
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
lock (threadLocker)
{
if (currAngle >= deg)
{
IsActionRunning = false;
dt.Stop();
}
Rotator_Body.Angle++;
currAngle++;
}
});
dt.Start();
});
}