Moving picture in C# - c#

I need to write a code which moves a picture1 to the top of the screen after a click of a button. After pictire1 reaches the top 20 pixels of the screen must become invisible and make picture2 visible. This is my wrong code:
private void button1_Click(object sender, EventArgs e)
{
int b = pictureBox1.Top;
for (int i = b; i < 20; i--)
{
pictureBox1.Top = i;
System.Threading.Thread.Sleep(20);
}
if (pictureBox1.Top < 20)
{
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
}
Any ideas how can it be fixed?

This seems wrong:
for (int i = b; i < 20; i--)
{
pictureBox1.Top = i;
System.Threading.Thread.Sleep(20);
}
This would loop while i < 20. From your code, however, one can see that if Top == 20 is reached, you'd like to show another picture. So I guess that should be i >= 20 and the if should be Top <= 20.
Also, you are not refreshing your display. Due to the loop and the Sleep, there will be no UI updates.
Add this, as Cobra_Fast suggests before the Sleep:
this.Invalidate();
this.Refresh();
To sum it up, the following should work (I've also slightly modified the code to make it clearer):
private void button1_Click(object sender, EventArgs e)
{
while (pictureBox1.Top >= 20)
{
pictureBox1.Top = pictureBox1.Top - 1;
this.Invalidate();
this.Refresh();
System.Threading.Thread.Sleep(20);
}
// Here you KNOW that the picture box is at y-position 20, so there's not need
// for the IF
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
The problem with the above code is that it blocks the UI. To keep it responsive, I'd use a timer as follows:
private void button1_Click(object sender, EventArgs e)
{
// Create a threaded timer
System.Timers.Timer animationTimer = new System.Timers.Timer();
animationTimer.Interval = 20;
animationTimer.AutoReset = false; // Only one Ping! We'll activate it if necessary
animationTimer.Elapsed += new ElapsedEventHandler(AnimationStep);
animationTimer.Start();
// Disable the button also, because we don't want another timer instance to
// interfere with our running animation
button1.Enabled = false;
}
Then, create the event that's called when the timer fires:
private void AnimationStep(object source, ElapsedEventArgs e)
{
// The following code needs to be executed in the context of the UI thread.
// We need to use this.Invoke in Forms or this.Dispatcher.Invoke in WPF
this.Invoke((Action)delegate()
{
// Move picture. Note that we don't need to update the display here
// because the UI thread gets time to do its work while the timer waits
// to fire below
if (pictureBox1.Top > 20)
pictureBox1.Top--;
// Show other picture maybe. I use <= here because initially, the
// position of the picture box may already smaller than 20.
if (pictureBox1.Top <= 20)
{
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
// Or let the timer fire again if we still need to animate
else
{
(source as System.Timers.Timer).Start();
}
}
}
This works as follows: A timer is created that fires once after 20ms. It then moves the picture up one pixel and then either shows the other picture if the animation is finished or starts the timer again for moving the picture up another pixel.
This keeps the UI responsive and still allows you to animate your picture. The downside is, the animation may not be as smooth as you might want it to be in case your windows is moved or other "work" needs be done by the UI thread.

What if initially b is greater than 20? The loop won't run because the condition in the loop is that i < 20. Because the loop is not ran, nothing will ever happen.
Consider changing the condition in your loop. You probably want the picture to move up, as you're reducing the Top-property. Let's say that initially the Top of pictureBox1 is 40. Following code will work:
while(pictureBox1.Top >= 20)
{
pictureBox1.Top--;
System.Threading.Thread.Sleep(20);
Invalidate();
Refresh();
}
Since the Top is now less than 20, the if-statement can be omitted and you can just call:
pictureBox1.Visible = false;
pictureBox2.Visible = true;
Complete code:
while(pictureBox1.Top >= 20)
{
pictureBox1.Top--;
System.Threading.Thread.Sleep(20);
Invalidate();
Refresh();
}
pictureBox1.Visible = false;
pictureBox2.Visible = true;

After the pictureBox1.Top assignment, call:
this.Invalidate();
this.Refresh();
Assuming you're working with WinForms.

Related

writing a game in c# winForm and program skips task before completing current one

writing a windows Form program that takes user input, and changes the color of the label accordingly.
and when the program reaches 2 words it repeats itself. it works fine until it reaches the second
word, where there I added a method to restart from the beginning after "validating and changing color", I can see the program validated the word, but when it comes to change the color, it just skips that part and starts from the beginning.
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
string[] listOfWords = { "bike", "car" };
Label[] labelsToDisplay = new Label[2];
string currentWord = "";
string userInput = "";
int increment = 0;
private void Form1_Load(object sender, EventArgs e)
{
labelsToDisplay[0] = this.label1;
labelsToDisplay[1] = this.label2;
for (int i = 0; i < labelsToDisplay.Length; i++)
{
labelsToDisplay[i].Text = listOfWords[i];
}
increment = 0;
currentWord = listOfWords[0];
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == ' ')
{
userInput = textBox1.Text;
if (userInput.Trim() == currentWord)
{
labelsToDisplay[increment].ForeColor = Color.Green;
}
else if (userInput.Trim() != currentWord)
{
labelsToDisplay[increment].ForeColor = Color.Red;
}
increment++;
if (increment < labelsToDisplay.Length)
{
currentWord = listOfWords[increment];
}
else if (increment >= labelsToDisplay.Length)
{
for (int i = 0; i < labelsToDisplay.Length; i++)
{
labelsToDisplay[i].ForeColor = Color.Black;
}
Form1_Load(sender, e);
}
textBox1.Text = "";
userInput = "";
}
}
}
I initialize an array of words and labels.
load Event connects the labels of the designer to the label array.
then make equal the labels to the listOfWords array.
and I initialize currnetWord.
now the user enters data into textBox1.
once the user hits the "space" an event is triggered, that checks if the text the user entered is equal to currentWord. if it is that label turns green. and red if not equal.
I put an "increment" int that adds 1 every time user presses space to know where I'm up to
now once the "increment" is larger than the length of the label array it triggers the load event that starts the process again.
but the issue is that the program is supposed to start the process again only after changing the color of the label but instead it just restarts without changing color.
but when I put a MessageBox before running the LoadEvent or if I remove the load event the label does turn green.
(i tried Thread.Sleep but that just pauses everything)
There needs to be a delay between setting the color to green/red and resetting all label colors to black. Here is a way to do it, however it may not be the best way. I first create a background worker and hook into the DoWork and RunWorkerCompleted events. Inside DoWork we create the delay using the Thread Sleep. Inside of RunWorkerCompleted we reset the label colors and call form load.
private BackgroundWorker bgWorker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bgWorker.DoWork += BgWorker_DoWork;
bgWorker.RunWorkerCompleted += BgWorker_RunWorkerCompleted;
}
private void BgWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(1000);
}
private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Reset();
}
private void Reset()
{
for (int i = 0; i < labelsToDisplay.Length; i++)
{
labelsToDisplay[i].ForeColor = Color.Black;
}
Form1_Load(null, null);
}
and inside of your existing code i changed where the increment exceeds the array bounds
else if (increment >= labelsToDisplay.Length)
{
bgWorker.RunWorkerAsync();
}
This will allow you to see the color before resetting the colors to black.
One thing to note is that the user will still be able to interact with the textbox and fire the KeyPress event. You could add a boolean to track whether the event should fire or not, something like..
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (isResetting)
{
return;
}
.....
}
Just make sure to set that bool to true when starting the background worker, and false once the worker has completed.

Unfocus live-updating chart

I have a project which consists of two forms, MainForm and CrossCorrPlotForm. On CrossCorrPlotForm, I have two charts (CrossCorrExpChart and CrossCorrRefChart) which I enabled scroll wheel zooming on, using the code described here and here.
Everything was working perfectly fine, until I added another chart (histChart), on MainForm, which is updated on each frame incoming from a camera (15-20 FPS), using a BackgroundWorker to collect and plot the data from the images. Now, both my charts on CrossCorrPlotChart are not zoomable anymore.
I presume this has something to do with the live-updating chart taking back the focus on each update. I tried adding histChart.Focus = false in the code but to no avail, as it seems "Control.Focused is read-only".
Does anyone have an idea how to make my charts zoomable again ?
Thanks
EDIT : Here is the code for the BackgroundWorker that updates chartHist :
private void OnFrameReceived(Frame frame)
{
bgw1.RunWorkerAsync(frame);
}
private void bgw1_DoWork(object s, DoWorkEventArgs e)
{
Frame frame = (Frame)e.Argument;
myBitmap = null;
try
{
frame.Fill(ref myBitmap);
mycamera.QueueFrame(frame);
SaveBitmap = myBitmap.Clone(cloneRect, myBitmap.PixelFormat);
BitmapToPrint = myBitmap.Clone(cloneRect, myBitmap.PixelFormat);
}
catch {}
}
private void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.InvokeRequired)
{
BeginInvoke((Action)(() => bgw1_RunWorkerCompleted(sender, e)));
}
else
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
DataFromBitmap DataFromBitmapHist = new DataFromBitmap(SaveBitmap.Clone(cloneRect, SaveBitmap.PixelFormat));
PixelColorCount = DataFromBitmapHist.ColorCountOutput();
}).Start();
chartHist.Titles["Title2"].Visible = false;
chartHist.Series["Pixel count"].Points.Clear();
//Plotting the pixel counter, to detect saturation
for (int i = 0; i < PixelColorCount.Length; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chartHist to warn the user
if (PixelColorCount.Last() > 1)
{
chartHist.Titles["Title1"].Visible = false;
chartHist.Titles["Title2"].Visible = true;
}
else
{
chartHist.Titles["Title1"].Visible = true;
chartHist.Titles["Title2"].Visible = false;
}
}
}
NOTES :
OnFrameReceived is a function from the API of the camera, it contains code that fires when a Frame is received from the camera.
frame.Fill puts the image contained in the Frame object in a Bitmap.
mycamera.QueueFrame sends back the Frame object to the camera, to receive a new image.
I had to use multiple threads, because using the UI thread too much resulted in blocking the reception from the camera.

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..

How to Draw Lines with delay?

How to add a time delay while drawing line in picturebox? I am using C #,visual studio 2010.
Graphics g = picturebox.CreateGraphics();
Pen p = new Pen(Color.Red);
for (int j = 1; j < 10; j++)
{
//Draw Line 1
g.DrawLine(p,j*3,j*3,100,100);
//----->How to put a Delay for 2 seconds So I
// see the first line then see the second after 2 sec
//Draw Line 2
g.DrawLine(p,j*10,j*10,100,100);
}
Use a timer on your drawing form. When you're ready to draw, enable the timer and start keeping track of the various lines that you need to draw (for example, in a list / array). Every time the timer fires draw 1 line in the timer's callback function and increment your "line index" (which line to draw next). When all lines are drawn, disable the timer.
For example:
public partial class DrawingForm : Form
{
Timer m_oTimer = new Timer ();
public DrawingForm ()
{
InitializeComponent ();
m_oTimer.Tick += new EventHandler ( m_oTimer_Tick );
m_oTimer.Interval = 2000;
m_oTimer.Enabled = false;
}
// Enable the timer and call m_oTimer.Start () when
// you're ready to draw your lines.
void m_oTimer_Tick ( object sender, EventArgs e )
{
// Draw the next line here; disable
// the timer when done with drawing.
}
}
You can use a simple Timer (System.Windows.Forms.Timer) and keep track of the current line index.
public partial class Form1 : Form {
private int index;
private void frmBrowser_Load(object sender, EventArgs e) {
index = 0;
timer.Interval = 2000;
timer.Start();
}
private void timer1_Tick(object sender, EventArgs e) {
index++;
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
Pen p = new Pen(Color.Red);
for (int j = 1; j < index; j++) {
g.DrawLine(p,j*3,j*3,100,100);
g.DrawLine(p,j*10,j*10,100,100);
}
}
}
Wrote this from head, it's not tested.
The suggestions in the other answers that add a pause by using a timer are correct but if you want the drawing of a single line to be shown slowly as well you'll need to do more that that.
You could write your own line drawing method and split the drawing of the line into segments and pause between the segments.
A quick alternative is using WPF instead of WinForms:
Add the line to a Canvas with its start and endpoint at the same position.
Add an animation to the endpoint that will move it to the desired location
Upon completion, do the same thing for the next line.
This way you do not have to write the line drawing code nor the timers.
using System.Threading;
Thread.sleep(2000);

Mouse Wheel Scroll - How can I capture the time interval between start and stop of scrolling?

Is there any way to capture the time interval between mouse wheel scroll start and stop? Actually I want to capture the interval between the scrolling start and stop when I very quickly scroll the mouse wheel.
I have already looked at MouseWheel event but it don't fulfill my requirement. In senes that it always gives a value of Delta 120 or -120 but i want to call a function depending on the speed of the mouse scroll for example when i scroll the mouse normally i want to perform function 1 and when i scrolled the mouse very quickly i want to perform the function 2. In other words is there any way to distinguish between the mouse scroll high and normal speed.
Any advice will be appreciated.
can't you capture the mouse wheel events and see how long between them. Basically start a timer when you get a mouse wheel event and then in the next event see what the timer is at (and so how long has elapsed between the events) to determine the speed the wheel is being turned at? If the elapsedtime is smaller than a certain threshold, perform function2 and if it is faster than a certain threshold perform function 1.
You will probably have to set it to perform function 1 if the timer goes off in case they only do a single scroll.
In fact you might be able to do it this way:
start a timer (with an interval that indicates slow mouse wheeling) in the mouse wheel event, then if the timer goes off perform function 1. If the mouse wheel event happens again before the timer has gone off then reset the timer and increment a counter (to keep track of the number in wheel events since you did stuff) then start a second (longer) timer. if the counter is greater then a certain threshold perform function 2. When the second timer elapses, reset the counter. Something along those lines should give you the ability to fire function 1 when slow wheel turning and function 2 when the wheel is rapidly turned through a few 'clicks'.
this code should give a (very dirty) indication of the sort of thing I was thinking of. After playing a little I'm not really sure it's a good solution though....
private void mouseWheelHandler (object sender, MouseEventArgs e)
{
slowTimer.Enabled = false;
slowTimer.Stop ();
slowTimer.Interval = 200;
slowTimer.Start();
slowTimer.Enabled = true;
m_counter++;
Trace.WriteLine(string.Format("counter={0}", m_counter));
if (fastTimer.Enabled==false)
{
fastTimer.Enabled = true;
fastTimer.Interval = 150;
fastTimer.Start ();
}
if (m_counter>5)
{
Trace.WriteLine("called method 2");
m_counter = 0;
fastTimer.Stop ();
slowTimer.Enabled = false;
slowCheckTimer.Stop ();
slowCheckTimer.Interval = 250;
slowCheckTimer.Start();
slowCheckTimer.Enabled = true;
}
}
private void slowTimer_Tick(object sender, EventArgs e)
{
Trace.WriteLine("slow timer ticked");
if (slowCheckTimer.Enabled==false)
{
Trace.WriteLine ("called method 1");
}
slowTimer.Enabled = false;
}
private void fastTimer_Tick(object sender, EventArgs e)
{
fastTimer.Enabled = false;
Trace.WriteLine("fast timer ticked");
m_counter = 0;
fastTimer.Stop ();
}
private void slowCheckTimer_Tick(object sender, EventArgs e)
{
Trace.WriteLine("slow check timer ticked");
slowCheckTimer.Stop ();
slowCheckTimer.Enabled = false;
}
Take a look at the Control.MouseWheel event.
As suggested by the Sam Holder i am posting here a modified verion of his advice to help other programmers facing the same problem.
public partial class Form1 : Form
{
int m_counter = 0;
public Form1()
{
InitializeComponent();
// Attach Mouse Wheel Event
this.MouseWheel += new MouseEventHandler(Form1_MouseWheel);
}
void Form1_MouseWheel(object sender, MouseEventArgs e)
{
// Refresh Slow Timer
slowTimer.Enabled = false;
slowTimer.Stop();
slowTimer.Interval = 150;
slowTimer.Start();
slowTimer.Enabled = true;
// Incremenet counter
m_counter++;
// Start Fast Timer
if (fastTimer.Enabled == false)
{
fastTimer.Enabled = true;
fastTimer.Interval = 50;
fastTimer.Start();
}
// If this returns true call
// the fast scroll implementation
if (m_counter > 4)
{
Console.WriteLine("Quick Method Called");
m_counter = 0;
fastTimer.Stop();
slowTimer.Enabled = false;
slowCheckTimer.Stop();
slowCheckTimer.Interval = 200;
slowCheckTimer.Start();
slowCheckTimer.Enabled = true;
}
}
private void slowTimer_Tick(object sender, EventArgs e)
{
if (slowCheckTimer.Enabled == false)
{
Console.WriteLine("Slow Method Called");
}
slowTimer.Enabled = false;
}
private void fastTimer_Tick(object sender, EventArgs e)
{
fastTimer.Enabled = false;
m_counter = 0;
fastTimer.Stop();
}
private void slowCheckTimer_Tick(object sender, EventArgs e)
{
slowCheckTimer.Stop();
slowCheckTimer.Enabled = false;
}
}

Categories

Resources