Remove PictureBox after time - c#

I'm making simple game where I need to remove pictures after certain time without freezing everything else. I'm making explode event:
private void Explode(int x, int y)
{
PictureBox explosion = new PictureBox();
explosion.Image = Properties.Resources.explosion;
explosion.SizeMode = PictureBoxSizeMode.StretchImage;
explosion.Size = new Size(50, 50);
explosion.Tag = "explosion";
explosion.Left = x;
explosion.Top = y;
this.Controls.Add(explosion);
explosion.BringToFront();
}
I have already one timer for running the game, and i want to use if statement to remove picture when it lasts for 3 sec.
private void timer1_Tick(object sender, EventArgs e)
{
foreach (Control x in this.Controls)
{
if (x is PictureBox && x.Tag == "explosion")
{
if (EXPLOSION LASTS MORE THEN 3sec)
{
this.Controls.Remove(x);
}
}
}
}
How can I do this?

Assuming you may have multiple picture box at the same time, then instead of using a single timer for multiple picture boxes which may have different explosion timings, you can use async/await and Task.Delay like this:
private async void button1_Click(object sender, EventArgs e)
{
await AddExplodablePictureBox();
}
private async Task AddExplodablePictureBox()
{
var p = new PictureBox();
p.BackColor = Color.Red;
//Set the Image and other properties
this.Controls.Add(p);
await Task.Delay(3000);
p.Dispose();
}

Before removing the picture box, of course it needs to release picturebox resources.
However there is problem when releasing. For more info, you can read more from the link below.
c# picturebox memory releasing problem

Related

How can I add events to a PictureBox dynamically created by clicking button? (C#)

I'm creating dynamically a bunch of PictureBoxes by clicking a button in my form, but I'm not clear on how can I add an event to each one. Here's my idea
private void insertarBloqueToolStripMenuItem_Click(object sender, EventArgs e)
{
pbA = new PictureBox{
Name = "picturebox" + contB,
Image = new Bitmap(bloque),
Size = pbA.Image.Size,
};
pbA.MouseDown += new MouseEventHandler(pbA_MouseDown);
pbA.MouseUp += new MouseEventHandler(pbA_MouseUp);
pbA.MouseMove += new MouseEventHandler(pbA_MouseMove);
pbA.Paint += new PaintEventHandler(pbA_Paint);
pbA.Cursor = Cursors.Hand;
listaBloques.Add(pbA);
panel1.Controls.Add(pbA);
}
That's what I got, can you help me with that idea? And How can I can create a single method to move one PictureBox on the list? Thank you
Your idea is quite good!
But it would be enought to write (but yours works also fine):
pbA.Paint += pbA_Paint;// generally: ... += methodName;
And you have to create the event itselve. For example:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
//do some cool stuff!
}
If it is painted on any of the PictureBoxes then this method will be called. If you want to know which PictureBox has been painted on then simply write:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
PictureBox picBox_active = sender as PictureBox;
//do some cool stuff!
}
And for making one single method to move just one PictureBox you could give the desired object as a parameter:
void move(PictureBox picBox, int moveX, int moveY)
{
picBox.Location = new Point(moveX, moveY);
}
And you can call this method by:
PictureBox picBoxTest = new PictureBox();
move(picBoxTest, 5, 5);
And to make it clearer with an example:
void pbA_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
PictureBox picBox_active = sender as PictureBox;
move(picBox_active, 5, 5);
}
The PictureBox that has been painted will be moved to the coordinates 5,5.

C# refreshing a chart every few seconds

I have a Windows Forms App written in C#. The idea is, that it draws a chart for 10 numbers after clicking a button. This works fine. I click the button, and I get a nice chart. However I also want to include a sort of "auto refresh" mode, where the chart is refreshed every few seconds. This would be enabled via Checkbox. Here's my code:
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
foreach (var series in ekran.Series) //clear previous values
{
series.Points.Clear();
}
for (int i = 0; i < 10; i++) //draw a chart from ten new values
{
value = rdn1.Next(0, 10); //for testing purpouses the value will be a random number a random number
ekran.Series["seria1"].Points.AddXY(i, value);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while(checkBox1.Checked) //click the chartButton every one second, when the checkbox is checked
{
//rysuj.PerformClick();
chartButton.PerformClick();
Thread.Sleep(1000);
}
}
And now for my problem. When I check the Checkbox, I will not get a chart until it finishes every iteration of the while loop. Since it's an infinite loop, I will never get my chart. If I rewrite the code to make only five iterations when the Checkbox is checked, I only get the chart for the fifth one (and after five seconds, as to be expected).
So my question is: how can I force this to draw a chart every time the button is clicked via chartButton.PerformClick()? When I click the button manually, everything works fine, it's just when I try to do it automatically, I get my problem.
EDIT
First of all,thank you for the replies. However, I'm still experiencing the same problem when using a timer. This is how my code looks now:
namespace ChartTest
{
public partial class Form1 : Form
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 1000;
}
void timer_Tick(object sender, EventArgs e)
{
timer.Enabled = false;
chartButton.PerformClick();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while (checkBox1.Checked)
{
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
}
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
ekran.Series.Clear();
var series2 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "Series2",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
this.ekran.Series.Add(series2);
for (int i = 0; i < 100; i++)
{
value = rdn1.Next(0, 10);
series2.Points.AddXY(i, value);
}
}
}
}
Sorry for being a total noob, but I have no idea, what am I doing wrong this time.
This is exactly what a Timer is for. Have the checkbox start/stop or enable/disable the timer, and handle the Timer.Tick event to redraw your chart. In your case, the event handler could simply call chartButton.PerformClick(), or insert whatever code the PerformClick() does.
ETA: If the chart refresh is not instant, you will probably want to push it off to a separate thread. If it's instant, there's not really any need to deal with the threading though.
I would go the route of using a thread with combination of checkbox's checkChange() event. Essentially this will allow your application to keep running while the update code will execute periodically. The refresh is determined by the sleep time, not your manual click or any other value.. Example below on how I to do this:
Thread refreshThread = null;
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (refreshThread == null) //No thread running, assume it starts this way
{
refreshThread = new Thread(chartRefresh);
refreshThread.Start();
}
else //Thread is running, must terminate
{
refreshThread.Abort();
refreshThread = null;
}
}
private void chartRefresh()
{
while (true)
{
//code to refresh chart
Thread.Sleep(10000);
}
}

Flipping a Drawn Image

I'm making a little C# project which requires me to move an image that has already been drawn into the form. Here is the drawing algorithm:
public void DrawImagePoint(PaintEventArgs e)
{
// Create image.
newImage = A_Worm_Nightmare.Properties.Resources.Worm;
// Create Point for upper-left corner of image.
Point ulCorner = new Point(50, 50);
// Draw image to screen.
e.Graphics.DrawImage(newImage, ulCorner);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
DrawImagePoint(e);
}
Question: How do you flip an already drawn object in WinForms, since implementing this method in a timer is not possible? (timer_Tick does not support PaintEventArgs) The fliping is by Cursor.Position.X. Here is the algorithm for a normal `Picturebox":
private void timer1_Tick(object sender, EventArgs e)
{
bool Ok = true;
if (Cursor.Position.X <= 135 && Ok)
{
image.RotateFlip(RotateFlipType.RotateNoneFlipY);
Ok = false;
}
else if (Cursor.Position.X >= 135 && !Ok)
{
Ok = true;
}
}
Thank you in advance
I see what you are trying to do.
You must Call
this.Refresh();
on the timer event.
this will trigger the paint event.
You definitely need a timer. Just change the location in Timer's Tick and call Invalidate, it will make your form to repaint.
private Point location = Point.Empty;
private Image newImage;
private void OnTimerTick(object sender, EventArgs e)
{
location.Offset(1,1);
//Do flipping here
newImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
this.Invalidate();//Makes form to repaint
}
public void DrawImagePoint(PaintEventArgs e)
{
if(newImage == null)
{
newImage = A_Worm_Nightmare.Properties.Resources.Worm;
}
e.Graphics.DrawImage(newImage, location);
}
You can setup a timer with whatever frequency, and that should work.
also note that Resources.Image will create new Image every time you query it. So you should cache the image somewhere to avoid that overhead.

Which form of threading should I use to improve efficiency with recursion?

I have two recursive methods that follow one after the other, each generating data for each node to a tree of any size. (I cannot combine the two because the second is dependent on the first's results - also, the first iteration is from leaf to root and the second, root to leaf.) I need to generate the data for every node addition/position update. I need to draw the tree to a cached bitmap every tree status update.
I currently am using a BackgroundWorker for this, but when the user adds a numerous amount of nodes, things start to lag as if the task was working on the same thread as the UI. (This is the first time I've used a BackgroundWorker but not for handling threads in general)
I came across Matt's post once: BackgroundWorker vs background Thread
Should I revert back to a System.Thread instead or may their be another option? I need to store the resulting tree and an integer value on the main thread when the process is finished.
[Threading]
private void Commence()
{
if (worker.IsBusy)
{
try
{
// I was thinking of fixing this with a timer instead
worker.CancelAsync();
while (worker.IsBusy) Thread.Sleep(10);
}
catch { return; }
}
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// int radMax; TreeNodeEx rTNH;
// Call the two recursive methods
radMax = GenerateDataFromNodes(rTNH, radMax);
UpdateRenderRegion(); // draw result to cached bitmap
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// draw bitmap to screen
if (!e.Cancelled) Invalidate();
}
[Algorithm]
private int GenerateDataFromNodes(TreeNodeEx hierarchy, int max)
{
// set hierarchy values
max = GenerateScaleAndRadius(hierarchy, max);
GenerateStartAndSweep(hierarchy);
return max;
}
private int GenerateScaleAndRadius(TreeNodeEx hierarchy, int max, int radius = 2)
{
for (int i = 0; i < hierarchy.Nodes.Count; i++)
{
max = GenerateScaleAndRadius((TreeNodeEx)hierarchy.Nodes[i], max, radius+1);
}
// generation a
return max;
}
private void GenerateStartAndSweep(TreeNodeEx hierarchy)
{
for (int i = 0; i < hierarchy.Nodes.Count; i++)
{
// generation b-1
}
}
private void node_SweepAngleChagned(object sender, EventArgs e)
{
for (int i = 0; i < ((TreeNode)sender).Nodes.Count; i++)
{
// generation b-2
}
}
[Invalidation]
private void UpdateRenderRegion()
{
if (rTNH != null)
{
renderArea = new Bitmap(radMax * rScale * 2, radMax * rScale * 2);
Graphics gfx = Graphics.FromImage(renderArea);
gfx.SmoothingMode = SmoothingMode.AntiAlias;
// pens, graphicspaths, and pathgradients
// draw parent first
// draw generations next using the same structure as the parent
DrawNodes(gfx, p, rTNH, rScale, w, h);
gfx.Dispose();
}
}
I suppose that this is the problem for your code. If you have a large tree, as you suggest, and your recursion is long process, then when you call Commence() method again, it will wait and do Thread.Sleep(10); producing UI unresponsevness. Rewrite your code like this, if the BackgroundWorker is canceled, it should call Commence() method in RunWorkerCompleted event handler instead of waiting the worker to finish. Check the code:
private void Commence()
{
if (worker.IsBusy)
{
worker.CancelAsync();
}
else
{
worker.RunWorkerAsync();
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgw = (BackgroundWorker)sender;
if (bgw.CancellationPending)
{
e.Cancel = true;
return;
}
else
{
radMax = GenerateDataFromNodes(rTNH, radMax);
if (bgw.CancellationPending)
{
e.Cancel = true;
return;
}
UpdateRenderRegion(); // draw result to cached bitmap
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
Invalidate();
else
Commence();
}
And as a side note, be careful that you don't access the backBuffer (bitmap you are using) at the same time when Invalidate occurs (for whatever reason). I don't know if you already handle access to this bitmap from different threads, but have that in mind.

How to animate a PictureBox in a Win Forms application?

I have a windows form application with a PictureBox control containing an image. I want to move the PictureBox control to the right in a slow movement. Here is my code:
Point currentPoint = pictureBox_Logo.Location;
for (int i = 0; i < 25; i++)
{
pictureBox_Logo.Location = new Point(pictureBox_Logo.Location.X + 1, pictureBox_Logo.Location.Y);
Thread.Sleep(30);
}
The problem here is that when the code executes instead of seeing the picture move, I see a white picture move and the moving stops until the picture appears.
What am I missing and what can I do about it?
Code:
public partial class Form1 : Form
{
void timer_Tick(object sender, EventArgs e)
{
int x = pictureBox1.Location.X;
int y = pictureBox1.Location.Y;
pictureBox1.Location = new Point(x+25, y);
if (x > this.Width)
timer1.Stop();
}
public Form1()
{
InitializeComponent();
timer1.Interval = 10;
timer1.Tick += new EventHandler(timer_Tick);
}
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Show();
timer1.Start();
}
}
original thread is here Move images in C#
Try to use pictureBox_Logo.Refresh() after Thread.Sleep(30);
Or look for standard Timer control.
My code is good written, but what I did wrong was putting the code in an event:
private void Form1_Shown(object sender, EventArgs e);
But when I put my code in a button it works without any problems.

Categories