Why when thread starts, the form crashes?Is Thread-safe? - c#

I've a game with 90 grey numbers and, periodically, some of them become blue.(Simplified!)
Now i use:
Game-->Timer-->Thread-->coloring numbers
because i want use my game also when the numbers in coloration.
the code:
some variables..
public FormHome()
{
InitializeComponent();
TEstrazione = new System.Timers.Timer(10000);
TEstrazione.AutoReset = false;
TEstrazione.Elapsed += timerEstrazioneElapsed;
TEstrazione.Start();
}
private void timerEstrazioneElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ThreadEstrazione = new Thread(cThread);
ThreadEstrazione.IsBackground = true;
ThreadEstrazione.Start();
}
private void cThread()
{
//other methods that change others controls..
coloring();
TEstrazione.Start();
}
void coloring()
{
for (int i = 0; i < 20; i++)//20 numbers to make colored
{
int num = *get random number*;
Label lbl;
lbl = (Label)panelNumembers.Controls["n" + num.ToString()];
if (lbl.InvokeRequired)
{
lbl.Invoke(new Action(coloring), new object[] { });
return;
}
lbl.BackColor = Color.DodgerBlue;
lbl.ForeColor = Color.Navy;
lbl.Refresh();
Thread.Sleep(800);
}
}
excuse me if some names are in italian but i think they aren't important.
The problem is that when the thread is coloring the label, the program locks: i can't push buttons and even close it! I think is a thread-safe problem but i don't know other ways.
As i've written, there are other methods inside a cThread, all change the controls. One of theese is:
void refreshLabel()
{
//here I always used one of the controls for the InvokeRequired
because in some cases I have so many and if I do the InvokeRequired
for each control becomes too long the code
if (label1.InvokeRequired)
{
ne.Invoke(new Action(refreshLabel), new object[] { });
return;
}
label1.Text = "xxxxx";
label1.Refresh();
label2.Text = "xxxxxxxxxxxxxxxx";
label2.Refresh();
}
In conclusion I have a lot of methods started by cThread that change a lot of controls, there is a way to make it thread safe without stretching too much code?
I apologize for the English but I'm Italian and is not easy explain the problems in an another language.

I'm using my girlfriend laptop so I'll edit my answer and give you a much better answer ASAP. the problem occurred because most of the execution is in the UI thread. you should execute most of your execution in background. when you want to update the UI you need to change context to the UI thread(only updates!).
Change coloring to:
void coloring()
{
for (int i = 0; i < 20; i++)//20 numbers to make colored
{
int num = *get random number*;
Label lbl;
lbl = (Label)panelNumembers.Controls["n" + num.ToString()];
var updateUI = new Action(() => {
lbl.BackColor = Color.DodgerBlue;
lbl.ForeColor = Color.Navy;
lbl.Refresh();
});
if (lbl.InvokeRequired)
{
Invoke(updateUI);
}
else
{
updateUI();
}
Thread.Sleep(800);
}
if there is no other blocking operations inside your code then this might work

Related

Real Time chart performance issue c# winform

I am currently using liveChart to plot a real time graph of 3 values: a position, a load and a deformation. The program is based on the Doli.DoPE library (a proprietary dll)
In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
public void Initialisation()
{
//...
MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData)
//...
}
with
private int OnData(ref DoPE.OnData Data, object Parameter)
{
DoPE.Data Sample = Data.Data;
if (Data.DoPError == DoPE.ERR.NOERROR)
{
Int32 Time = Environment.TickCount;
if ((Time - LastTime) >= 250 /*ms*/)
{
// Send the data from the ondata handler inside of a global list
ListData.time.Add(Sample.Time);
ListData.position.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S]);
ListData.load.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F]);
ListData.extend.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E]);
Thread ThForUpdateChart = new Thread(() =>
{
if (NewINstanceOfChart != null)
{ NewINstanceOfChart.UpdateValues(ListData.time.Last(), ListData.position.Last(),ListData.load.Last(), ListData.extend.Last()); }
});
ThForUpdateChart.Start();
LastTime = Time;
}
}
return 0;
}
The function UpdateValues is part of a second form RealTimeChart.cs called in the MainForm through a button click event:
private void btnGraph_Click(object sender, EventArgs e)
{
var thread = new Thread(() =>
{
NewINstanceOfChart = new RealTimeChart(ListData);
NewINstanceOfChart.Show();
});
thread.Start();
}
the form RealTimeCharts.cs is initalised this way:
public RealTimeChart(Globals ListData)
{
InitializeComponent();
//measures = ListData;
ListPosition = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListPosition.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.position[i]
});
}
ListLoad = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListLoad.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.load[i]
});
}
ListExtend = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListExtend.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.extend[i]
});
}
resultChart.Series.Add(new LineSeries
{
LineSmoothness = 0,
Values = ListPosition,
PointGeometrySize = 2,
StrokeThickness = 4
});
SetXAxisLimits();
}
And the UpdateValues function is defined as followed:
public void UpdateValues(double time, double position, double load, double extend)
{
measures.time.Add(time-measures.TareTime);
measures.position.Add(position);
measures.load.Add(load);
measures.extend.Add(extend);
UpdateEnabledSequencialPartToTrue();
}
public void UpdateEnabledSequencialPartToTrue()
{
if (this.InvokeRequired)
BeginInvoke(new System.Action(() => this.InternalUpdateEnabledSequencialPartToTrue()));
else
InternalUpdateEnabledSequencialPartToTrue();
}
private void InternalUpdateEnabledSequencialPartToTrue()
{
try
{
ListPosition.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.position.Last()
});
ListLoad.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.load.Last()
});
ListExtend.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.extend.Last()
});
//LineSeries plot = new LineSeries();
SetXAxisLimits();
// lets only use the last 14400 values (1h long recording, 14400 values at frequency of 1 record very 250ms, see OnData function MainForm
if (measures.time.Count > 14400)
{
ListPosition.RemoveAt(0);
ListLoad.RemoveAt(0);
ListExtend.RemoveAt(0);
}
}
catch (NullReferenceException) { }
}
After a minute, the programme starts to be really laggy. I tried putting the second winform (RealTimeCharts) on another thread so the MainForm does not lag (it is piloting a machine, it has to be responsive), but no success.
I would like to know if the whole thing is laggy because the code is way too bad, or if it is liveChart that reached its (free) limits. Would you advice another way to plot real time data ?
In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
That is natturally way higher then what Winforms Drawing can take. See, drawing a GUI is expensive. If you only do it once per user-triggered event, you will never notice that. But do it from a loop - including sampling a sensor every MS - and you can quickly overlord the UI. My first Multithreading tests actually appeared to have failed on big numbers, becaus I ended up sending so many updates I plain overloaded the GUI thread. Since then I know not to go past progress bars.
You can add data to a background collection as quickly as you can sample them, but you can not draw that quickly. And honestly drawing more often then 30-60 times/second (every ~17 ms) is not really going to help anyone anyway. Usually you can not use a timer, as the Tick might happen more often then it can be processed - again, a GUI Thread with a overflowing Event Queue.
I do not have any rate limiting code for WindowsForms. But I would guess an Event that re-queues itself at the end of the EventQueue after finishing the work would work.

How to give a PictureBox Control a Bullet or Projectile-like trayectory when a specific key is pressed

Im a beginner with C# I'm doing some School project using a Windows Form App, It's a simple game but I ran into some issues, I want to give a picture box control a bullet-like trajectory when the [Space] key is pressed but haven't been able to do it yet. I was instructed to do this only using Threads and NO timers.
Also I want this picture box to 'spawn' and start moving from another picture box named 'SpaceShip' but i don't know how i could do that
Of course, advice and suggestions are very welcome even if it means changing entire parts of my awful code
I tried using picturebox.Left = picturebox.Left +5; but I guess i'm doing something wrong
if (e.KeyCode == Keys.Space)
{
if(pbEnergy.Value <= 0)
{
pbEnergy.Value = 0;
System.Media.SoundPlayer SFX = new System.Media.SoundPlayer(#"C:\SFX\nolaser.wav");
SFX.Play();
}
else
{
System.Media.SoundPlayer SFX = new System.Media.SoundPlayer(#"C:\SFX\laser.wav");
SFX.Play();
pbEnergy.Value -= 10;
ThreadStart Delegate = new ThreadStart(phys);
Thread MovTh = new Thread(Delegate);
MovTh.Start();
}
}
public void phys()
{
for (int i = 0; i <= 60; i++)
{
Thread.Sleep(10);
Progress(i);
if (i == 60) //stop when value is reached
{
Thread.CurrentThread.Suspend();
}
}
}
delegate void Delegate(int n);
private void Progress(int num)
{
if (this.InvokeRequired)
{
Delegate d = new Delegate(Progress);
object[] Parameters = new object[] { num };
this.Invoke(d, Parameters);
}
else
{
picprj.Visible = true;
picprj.Left = picprj.Left + 5;
}
I expected the picture box to travel and then disappear but it finishes its trajectory and stays visible, I would appreciate some advice on this or even a better way to do it.

Picture unable to be shown in Picture Box from listbox when doing loop C#

Newbie in programming here so please give answers with clear elaboration. Thank you.
When i do this loop for a slideshow with given number of loops, the pictures do not show in the picturebox while the loop is running.
And, i have a selectedindexchanged event somewhere so only when the loop ends, that event fires and only the last picture is shown on the picturebox.
CODE:
if (mtxtloop.Text != "")
{
int intNumberOfLoops = Convert.ToInt16(mtxtloop.Text);
for (int intAlbum = 0; intAlbum < intNumberOfLoops; intAlbum++)
{
for (int intPictures = 0; intPictures < listBoxPicturesInAlbum.Items.Count; intPictures++)
{
//Just to check position.
listboxPicturesInAlbum.SelectedIndex = intPictures;
Thread.Sleep(2000);
//Insert selecteditem into picture
//ERROR HERE: PictureBox doesn't show selecteditem
pBoxOfSelectedPicture.Image = Image.FromFile(listBoxPicturesInAlbum.SelectedItem.ToString());
}
}
}
In your original code you are simply missing a PBox_loop.Refresh();. But you are also tying up the UI thread by sending it to sleep for seconds. Never a good idea.. (Thread.Sleep() can sometimes help resolve race conditions but not here and never for more than 10-100ms)
Here is the way I would do it: Use a Timer and three variables at e.g. class level to keep track of the progress..
int intNumberOfLoops = 1;
int intLoopCounter = 0;
int pictureIndex = 0;
private void startButton_Click(object sender, EventArgs e)
{
pictureIndex = 0;
intLoopCounter = 0;
// insert error checking here!
intNumberOfLoops = Convert.ToInt16(mtxtloop.Text);
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
// final image:
if (intLoopCounter >= intNumberOfLoops)
{ // this assumes there is a selected item!
PBox_loop.ImageLocation = listBoxPicturesInAlbum.SelectedItem.ToString();
timer1.Stop();
return;
}
// the regular loop:
PBox_loop.ImageLocation = listBoxPicturesInAlbum.Items[pictureIndex];
pictureIndex++;
if (pictureIndex >= listBoxPicturesInAlbum.Items.Count)
{
pictureIndex = 0;
intLoopCounter++;
}
}
Using a Timer prevent the UI thread of being blocked without the hassle of starting a Thread of your own..

Text animation in Windows Forms

I was wondering if there was a way of adding a sort of animation to text displayed on a form.
What I had in mind when I thought of this was kind of similar to what you can do with text in PowerPoint (i.e. a typewriter-like animation where the text is typed one at a time, have the whole textbox appear with a certain effect etc), I'm just looking to find out what you can do using Windows Forms.
Currently I'm using a textbox to display information on my form application, though in hindsight I realise labels would have worked just as well.
EDIT: Turns out I was using labels after all, I just gave it a name with 'textbox' inside for lack of a better description.
public partial class Form1 : Form
{
int _charIndex = 0;
string _text = "Hello World!!";
public Form1()
{
InitializeComponent();
}
private void button_TypewriteText_Click(object sender, EventArgs e)
{
_charIndex = 0;
label1.Text = string.Empty;
Thread t = new Thread(new ThreadStart(this.TypewriteText));
t.Start();
}
private void TypewriteText()
{
while (_charIndex < _text.Length)
{
Thread.Sleep(500);
label1.Invoke(new Action(() =>
{
label1.Text += _text[_charIndex];
}));
_charIndex++;
}
}
}
Now, I personally wouldn't do this because gratuitous animations tend to annoy users. I'd only use animation sparingly - when it really makes sense.
That said, you can certainly do something like:
string stuff = "This is some text that looks like it is being typed.";
int pos = 0;
Timer t;
public Form1()
{
InitializeComponent();
t = new Timer();
t.Interval = 500;
t.Tick += new EventHandler(t_Tick);
}
void t_Tick(object sender, EventArgs e)
{
if (pos < stuff.Length)
{
textBox1.AppendText(stuff.Substring(pos, 1));
++pos;
}
else
{
t.Stop();
}
}
private void button1_Click(object sender, EventArgs e)
{
pos = 0;
textBox1.Clear();
t.Start();
}
or something like that. It'll tick off ever half second and add another character to the multi-line text box. Just an example of what someone could do.

C# Threading - an array of threads, where each thread contains a form with an image

I have an array of five threads. Each thread contains the same form, each form is put on to the screen in a different location (still working on that method :P).
I am trying to have each form load its contents (an image) before the other forms have finishing being placed. At the moment this works for the first form, but the others are blank or disappear :P
Originally each form would be placed but the method would need to finish before all the forms contents were displayed.
Any help would be appreciated, thanks :)
public partial class TrollFrm : Form
{
int number = 0;
public TrollFrm()
{
InitializeComponent();
startThreads();
}
private void TrollFrm_Load(object sender, EventArgs e)
{
}
private void TrollFrm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
public void startThreads()
{
Thread[] ThreadArray = new Thread[5];
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
}
public void createForm()
{
Form frm = new TrollChildFrm();
Random randomX = new Random();
Random randomY = new Random();
number++;
int xValue;
int yValue;
if (number % 2 == 0) //number is even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) + 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) - 200;
}
else //number is not even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) - 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) + 200;
}
frm.Show();
frm.Location = new Point(xValue, yValue);
Thread.Sleep(1000);
}
Your forms are not displaying correctly because they are not running on a thread with a message loop. The general rule is that all UI element accesses must occur on the main UI thread.
Since you have a call to Thread.Sleep(1000) I am going to assume that you want to wait 1 second between the initial display of each form. In that case I would use a System.Windows.Forms.Timer who's Tick event will call createForm directly. Enable the timer, let 5 Tick events come through, and then disable the timer. I see no need to create any threads at all.
The reason your forms aren't displaying is because you are running inside one method on the main UI thread. Instead, you could create a method that spawns a new form and launch that at certain intervals on another thread (making sure the form handling is done on the main UI thread). So you could do something like:
(Pseudo Code)
private const int TIME_THRESHOLD = 100;
int mElapsedTime = 0;
Timer mTimer = new Timer();
.ctor
{
mTimer.Elapsed += mTimer_Elapsed;
}
private void mTimer_Elapsed(...)
{
mElapsedTime++;
if (mElapsedTime >= TIME_THRESHOLD)
{
mElapsedTime = 0;
SpawnForm();
}
}
private void SpawnForm()
{
// Make sure your running on the UI thread
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(SpawnForm));
return;
}
// ... spawn the form ...
}
This is just an example of what I was proposing - it would not look exactly like this in the code, but this should give you an idea of the execution steps.
I would suggest to use Thread.Sleep(1000) in this manner
Caller section
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
Thread.Sleep(1000);
Also in the method that executing the work for the thread.
while(!something)
{
Thread.Sleep(1000)
}

Categories

Resources