how to switch between photos in windows forms using time interval? - c#

I'm having a problem while trying to switch between a group of photos in a Form(1).
I'm using picturebox.Image in order to view the chosen picture, and after certain time interval (let's say 4Sec) , switch to a random photo in the same group of photos.
While switching between each photo, i would like to show another Form(2) for 1Sec, and then go back to Form(1).
my Code in Form(1):
public partial class Form1: Form
{
public static Timer time;
public static Form mod;
public Form1()
{
InitializeComponent();
time = new Timer();
mod = new Form2();
mod.Owner = this;
mod.Show();
this.Hide();
RunForm1();
}
public void RunForm1()
{
for (int i = 0; i < groupSize; i++)
{
mod.Owner = this;
mod.Show();
this.Hide();
}
}
}
my Code in Form(2):
public partial class Form2: Form
{
public static Timer time;
public int index = -1;
public List<Image> images;
public DirectoryInfo dI;
public FileInfo[] fileInfos;
public Form2()
{
InitializeComponent();
images = new List<Image>();
time = new Timer();
dI = new DirectoryInfo(#"C:\Users\Documents\Pictures");
fileInfos = dI.GetFiles("*.jpg", SearchOption.TopDirectoryOnly);
foreach (FileInfo fi in fileInfos)
images.Add(Image.FromFile(fi.FullName));
index = images.Count;
time.Start();
RunForm2();
}
public void RunForm2()
{
Random rand = new Random();
int randomCluster = rand.Next(0, 1);
while (index != 0)
{
pictureBox1.Image = images[Math.Abs(index * randomCluster)];
setTimer();
index--;
}
}
public void setTimer()
{
if (time.Interval == 4000)
{
this.Owner.Show();
this.Close();
}
}
}
My main problems in this code is:
1. The time is not updating, i mean, time.Interval is always set to 100
2. i dont know why, but, the photos, never shows in the picturebox.Image although, in debug mode it shows that the photos are being selected as properly.
Thank you for you help!
Roy.

You need to use the Tick event from the timer to know when the time has elapsed.
you check if the interval equals (==) 4000, but you need to set it to 4000 (time.Interval = 4000) and then start the timer. Then the Tick event will fire after 4 seconds.
And the problem of the image not being showed could be solved by calling pictureBox1.UpdateLayout();

Related

How to make an animation look consistent and smooth

`So I have a total of 6 images that when animated, creates the illusion of a person walking. The problem is its not smooth, refresh(), invalidate(),update() have failed me. How do I go about this.
namespace Runner
{
public partial class Form1 : Form
{
Keys moveRight;
Keys moveLeft;
public static bool isMovingR = false;
public static bool isMovingL = false;
Bitmap stnd = new Bitmap(Properties.Resources.Standing);
static Bitmap wlk_1_RL = new Bitmap(Properties.Resources.Walk_1_RL);
static Bitmap wlk_2_RL = new Bitmap(Properties.Resources.Walk_2_RL);
static Bitmap wlk_3_RL = new Bitmap(Properties.Resources.Walk_3_RL);
static Bitmap wlk_4_LL = new Bitmap(Properties.Resources.Walk_4_LL);
static Bitmap wlk_5_LL = new Bitmap(Properties.Resources.Walk_5__LL);
static Bitmap wlk_6_LL = new Bitmap(Properties.Resources.Walk_6_LL);
Graphics gfx;
Animation animate = new Animation(new Bitmap[] { wlk_1_RL, wlk_2_RL, wlk_3_RL,
wlk_4_LL, wlk_5_LL, wlk_6_LL });
Timer timer = new Timer();
int imageX = 5;
int imageY = 234;
public Form1()
{
InitializeComponent();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
moveRight = Keys.D;
moveLeft = Keys.A;
if (Keys.D == moveRight)
{
isMovingR = true;
timer.Enabled = true;
timer.Interval = 50;
timer.Tick += timer1_Tick;
//imageX += 5;
Refresh();
} else if (Keys.A == moveLeft)
{
isMovingL = true;
imageX -= 5;
Refresh();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
gfx = this.CreateGraphics();
gfx.DrawImage(animate.Frame2Draw(), imageX, imageY);
//Refresh(); Invalidate(); Update();
}
}
}
Now, the problem is being able to make the animation consistent and smooth
UPDATE...animation class
public class Animation
{
int slctdImg = 0;
public static Bitmap[] images;
Bitmap frame2Draw;
public Animation(Bitmap[] frames)
{
foreach (Bitmap btm in frames)
{
btm.MakeTransparent();
}
images = frames;
}
public Bitmap Frame2Draw()
{
if (slctdImg < images.Length)
{
frame2Draw = images[slctdImg];
slctdImg++;
}
if (slctdImg >= images.Length)
{
slctdImg = 0;
frame2Draw = images[slctdImg];
}
return frame2Draw;
}
}
Many issues:
I wonder why you would call MakeTransparent on each Tick?? I doubt it does what you expect.. It is changing pixels and rather expensive; you ought to cache the images instead.
Nor why you create an array of bitmap on each Tick??? And since it is always created the same way it always displays only the 1st image.. This should answer your question.
Further issues:
Using this.CreateGraphics(); will fail to create a persistent result although that may not be your aim as you try to animate.
Remember that a Timer.Interval can't run faster than 15-30 ms; also that winforms is notoriously bad at animation.
Remember that c# is zero-based, so this slctdImg > images.Length should probably be slctdImg >= images.Length
Here is what you should do instead:
Move the instantiation either to the form load or maybe to a key event.
Move the drawing to the Paint event of the form.
In the Tick count up frames and/or position and trigger the Paint by calling Invalidate on the form!
Update:
One more issue is the way you hook up the Tick event each time the right key is pressed. Hooking up an event multiple times will result in it running multiple times; this will create to gaps/jumps in your animation..
Either add an unhook each time before hooking up (it will fail quietly the 1st time)
timer.Tick -= timer1_Tick;
timer.Tick += timer1_Tick;
or (better) hook it up only once in the original set up!

Display random pictures in picturebox each 0,5sec

I am kinda stuck and I need help.
My goal is to make a little "game". It should have 3 pictureboxes and it should randomly display/change numbers (pictures) 1 to 6 every 0,5sec. When I hit STOP, it should stop the numbers and I should get points (score) based on nubmers. 3 same numbers = +10 points, 2 same numbers = +5 points, no same numbers = -5 points. Then it should display highest score achieved (Max Score).
http://i.imgur.com/kubQBST.png
Please, give me some tips what to do.
Thanks a lot, regards Peter
You can use a Timer with random something like this :
Random rnd1 = new Random(Environment.TickCount);
Image[] Images = new Image[6];
int[] CurrentStatus = new int [3];
Images[0] = Image.FromFile("FileNameFornumber1");
Images[1] = Image.FromFile("FileNameFornumber2");
Images[2] = Image.FromFile("FileNameFornumber3");
Images[3] = Image.FromFile("FileNameFornumber4");
Images[4] = Image.FromFile("FileNameFornumber5");
Images[5] = Image.FromFile("FileNameFornumber6");
//change numbers every tick
private Timer_TickHandler(object sender, EventArgs e)
{
this.CurrentState[0] = rnd1.Next(1, 6);
this.CurrentState[1] = rnd1.Next(1, 6);
this.CurrentState[2] = rnd1.Next(1, 6);
this.PictureBox1.Image = Images[this.CurrentStatus[0]-1];
this.PictureBox2.Image = Images[this.CurrentStatus[1]-1];
this.PictureBox3.Image = Images[this.CurrentStatus[2]-1];
}
you have to write an event for the stop button to deactivate the timer and calculate the score based on the CurrentStatus Array and I think the calculation alg must be like this :
int score = this.CurrentStatus.Sum();
if (this.CurrentStatus[0] == this.CurrentStatus[1] && this.CurrentStatus[1] == this.CurrentStatus[2])
score +=10;
else
{
for (int i=0; i<3; i++)
{
for (int j=i+1; j<3; j++)
{
if (this.CurrentStatus[i] == this.CurrentStatus[j])
{
score+=5;
break;
}
}
}
}
for start make random picture on button press.
How add images into resources
For example
public partial class Form1 : Form
{
List<Bitmap> picturesList = new List<Bitmap>(); //Array of pictures
Random random = new Random();
public Form1()
{
InitializeComponent();
//Load all pictures from resources into array
picturesList.Add(Properties.Resources.pic1);
picturesList.Add(Properties.Resources.pic2);
picturesList.Add(Properties.Resources.pic3);
//Set random image into picture box
RandomChangeImage();
}
public void RandomChangeImage()
{
//Generate random number. (random index between 0 - array.count )
int randomIndex = random.Next(0, picturesList.Count);
//Set random image from array
YourPictureBoxName.Image = picturesList[randomIndex];
}
}
Now you can use RandomChangeImage(); in your code. Aflter you done you can continue with timer.
Now add Timer from toolbox into designer.
Double click on timer.
It generated this
private void timer1_Tick(object sender, EventArgs e)
{
//Call random change image
RandomChangeImage();
}
in you buttonstart_Click call timer1.Start(); for example
private void btnStart_Click(object sender, EventArgs e)
{
timer1.Start();
}
if you want change tick in timer you can use
timer1.Interval = 500; for 0.5s. where you want.
if you want stop timer use
timer1.Stop();

C# dynamic data display - update LineGraph

In my XAML file, I create a ChartPlotter then I create in c# my LineGraphs and attatch them to my ChartPlotter. I tried to find a way to update these LineGraphs after their creation, but it always failed.
The only solution I found, is that I delete all LineGraphs , re-create them with new values and finally link them to my ChartPlotter.
How can I update LineGraph ?
for (int i = 0; i < lgs.Length; i++)
if (lgs[i] != null)
lgs[i].RemoveFromPlotter();
PS : lgs is my LineGraph array.
To update your LineGraphs, you have to use the ObservableDataSource object instead of the CompositeDataSource. With this object, you can use the method AppendAsync().
public partial class MainWindow : Window
{
public ObservableDataSource<Point> source1 = null;
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Create source
source1 = new ObservableDataSource<Point>();
// Set identity mapping of point in collection to point on plot
source1.SetXYMapping(p => p);
// Add the graph. Colors are not specified and chosen random
plotter.AddLineGraph(source1, 2, "Data row");
// Force everyting to fit in view
plotter.Viewport.FitToView();
// Start computation process in second thread
Thread simThread = new Thread(new ThreadStart(Simulation));
simThread.IsBackground = true;
simThread.Start();
}
private void Simulation()
{
int i = 0;
while (true)
{
Point p1 = new Point(i * i, i);
source1.AppendAsync(Dispatcher, p1);
i++;
Thread.Sleep(1000);
}
}
}
All you want is in the while of the method Simulation.
source1.AppendAsync(Dispatcher, p1);

How to go about making a variable in a class output to a label after clicking on a picturebox?

I'm fairly new to OOP and am not sure how I would go about implementing something in my program. My program is pretty much similar to whack a mole and has an array of picture boxes with an image in and an image of a monster moves randomly between the picture boxes with a time interval applied or will move to a new random picture box whenever the user clicks on the monster in time. I have created an monster and a player sub class to try and add some OOP concepts to the program but am not sure how to implement what I want. Basically I have a label for score on my main form and a score variable in my animal class with a value. I want to be able to add the value of score from the label on my form when the user clicks on the picture box with the mole in and take away the value of score from the label when they don't click on it in time.
Here is my code:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
PictureBox[] boxes;
int initialscore = 0;
int time = 0;
int randPos;
public Form1()
{
InitializeComponent();
}
private void boxes_MouseClick(object sender, MouseEventArgs e)
{
PictureBox pb2 = new PictureBox() { Image = Image.FromFile("sword2.png") };
this.Cursor = new Cursor(((Bitmap)pb2.Image).GetHicon());
for (int x = 0; x < 27; x++)
{
if (sender.Equals(boxes[x]))
{
Image grass = Image.FromFile("swamp.png");
PictureBox temp = (PictureBox)sender;
temp.Image = grass;
}
if (sender.Equals(boxes[x]))
{
PictureBox pb = (PictureBox)sender;
if (pb.Tag == "skeleton.png")
initialscore++;
}
}
label1.Text = " Score: " +initialscore.ToString();
}
public void timer1_Tick(object sender, EventArgs e)
{
boxes[randPos].Image = Image.FromFile("swamp.png");
boxes[randPos].Tag = "swamp.png";
Random r = new Random();
randPos=r.Next(0, 27);
boxes[randPos].Image = Image.FromFile("skeleton.png");
boxes[randPos].Tag = "skeleton.png";
}
private void Form1_Load(object sender, EventArgs e)
{
boxes = new PictureBox[27];
int top = 100;
int left = 100;
for (int x = 0; x < 27; x++)
{
boxes[x] = new PictureBox();
boxes[x].Image = Image.FromFile("swamp.png");
boxes[x].Height = 100;
boxes[x].Width = 100;
if (x % 9 == 0)
{
top += 120;
left = 120;
}
else
left += 120;
boxes[x].Top = top;
boxes[x].Left = (50 + left);
Controls.Add(boxes[x]);
this.boxes[x].MouseClick += new
System.Windows.Forms.MouseEventHandler(this.boxes_MouseClick);
label1.Text = " Score: " + initialscore.ToString();
label2.Text = " Time: " + time.ToString();
}
}
}
namespace WindowsFormsApplication1
{
class Monster
{
protected int score;
public Monster()
{
score = 10;
}
}
}
namespace WindowsFormsApplication1
{
class Player:Monster
{
}
}
Nothing has been added in the player class yet.
What do I need to add or change to be able to get the initial score to change by the value of the score in the monster class when clicking on the moving image?
To unify the updating/incrementing and visualization of the score you should extract that to a method:
public void incrementScore(int increment)
{
initialscore += increment;
label1.Text = " Score: " + initialscore.ToString();
}
in the Form1_Load you call this like:
incrementScore(0);
for the click on the monster you have different possibilities:
if all the monsters have the same points you can make it a static variable in the Monster class.
protected static int Score = 10;
which allows you to use it in the boxes_MouseClick event handler:
incrementScore(Monster.Score);
in case all monsters have another value you have to hold the score variable as an instance variable, identify somehow the instance of the monster class you clicked on and increment with this value

Accurate measurement of download speed of a webclient

I am using C# and the WebClient class.
This is the code I am using, inspired by another post here on SO. This code worked well for large files, it accurately displayed the download speed. However, there is now the limitation of downloading individual files, many of which are small, with small being .5-5 MB. This has caused the speed counter to skyrocket, often into the hundreds of thousands of KBps. I'm not sure what else to try to combat this. I added a second progress bar showing individual file downloads which helps improve the image a bit, but the download speed counter should really be fixed. Is there a different class to use that would solve this problem?
The WebClient in this code is disposed of properly elsewhere.
private class NetSpeedCounter
{
private double[] DataPoints;
private DateTime LastUpdate;
private int NumCounts = 0;
private int PrevBytes = 0;
public double Speed { get; private set; }
public NetSpeedCounter(WebClient webClient, int maxPoints = 10)
{
DataPoints = new double[maxPoints];
Array.Clear(DataPoints, 0, DataPoints.Length);
webClient.DownloadProgressChanged += (sender, e) =>
{
var msElapsed = DateTime.Now - LastUpdate;
int curBytes = (int)(e.BytesReceived - PrevBytes);
PrevBytes = (int)e.BytesReceived;
double dataPoint = ((double)curBytes) / msElapsed.TotalSeconds;
DataPoints[NumCounts++ % maxPoints] = dataPoint;
Speed = DataPoints.Average();
};
}
public void Reset()
{
PrevBytes = 0;
LastUpdate = DateTime.Now;
}
}
I download the files with this code, which is started afterwards by a call to DownloadFileAsync. This code just downloads them in a chain, one after another, asynchronously.
This is setting up for starting the download
Queue recordQ = new Queue(files);
progressBar.Value = 0;
progressBar.Maximum = recordQ.Count;
UpdateStatusText("Downloading " + recordQ.Count + " files");
var record = recordQ.Dequeue();
speedUpdater.Start();
CheckAndCreate(record.AbsolutePath);
Adding the event handler
wc.DownloadFileCompleted += (sender, e) =>
{
var nr = recordQ.Dequeue();
CheckAndCreate(nr.AbsolutePath);
this.Invoke((MethodInvoker)delegate
{
UpdateStatusText("Downloading " + recordQ.Count + " files", lblStatusR.Text);
});
counter.Reset();
// download the next one
wc.DownloadFileAsync(nr.DownloadPath, nr.AbsolutePath);
}
counter.Start();
wc.DownloadFileAsync(record.DownloadPath, record.AbsolutePath);
This last call is what starts everything off.
DateTime.Now is not accurate enough for some scenarios where timespans are recorded frequently(Eric Lippert mentions that they have a precision of 30 ms here), since DateTime.Now will return the previously used DateTime.Now when called quickly in succession. This might result in discrepencies in your speed counter due to inaccurate increases when downloads are finished very quickly. I'd recommend using StopWatch API for that purpose.
EDIT
I have created the following test Winforms application based on your code that works fine for small files. I'm getting a reasonable 200 kbps over my intranet for 5 files that are about 2MB each. Just make sure you're calling the stopwatch classes at the right places.
To replicate, create a winforms app, create 3 labels of Id lblSpeed, lblStatus, lblFile and copy\paste the code and rename the URI's below to the files you want to test on.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Queue<Record> recordQ;
WebClient wc;
NetSpeedCounter counter;
//We store downloaded files in C:\TestDir (hardcoded in the Record class below)
public Form1()
{
InitializeComponent();
recordQ = new Queue<Record>();
//replace the URI string below. Nothing else to replace.
//recordQ.Enqueue(new Record(#"URI1", "SQLtraining.exe"));
//recordQ.Enqueue(new Record(#"URI2", "Project Mgmt.pptx"));
//first uri to process. Second param is the file name that we store.
Record record = new Record(#"URI0","Agile.pptx"); // replace the URI
//Initialize a webclient and download the first record
using (wc = new WebClient())
{
counter = new NetSpeedCounter(wc);
wc.DownloadFileCompleted += (sender, e) =>
{
if (recordQ.Count == 0)
{
UpdateStatusText("Done");
return;
}
var nr = recordQ.Dequeue();
//just create directory. the code uses the same directory
CheckAndCreate(nr.Directory);
//need not even use invoke here. Just a plain method call will suffice.
this.Invoke((MethodInvoker)delegate
{
UpdateStatusText("Left to process: " + recordQ.Count + " files");
});
counter.Reset();
counter.Start();
//continue with rest of records
wc.DownloadFileAsync(nr.DownloadPath, nr.GetFullPath());
this.lblFile.Text = nr.DownloadPath.OriginalString;
};
//just update speed in UI
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
counter.Start();
//display URI we are downloading
this.lblFile.Text = record.DownloadPath.OriginalString;
//start first download
wc.DownloadFileAsync(record.DownloadPath, record.GetFullPath());
}
}
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.lblSpeed.Text = counter.Speed.ToString();
}
public void UpdateStatusText(string msg)
{
this.lblStatus.Text = msg;
}
public void CheckAndCreate(string absPath)
{
if (!Directory.Exists(absPath))
Directory.CreateDirectory(absPath);
}
}
public class NetSpeedCounter
{
private int NumCounts = 0;
private int PrevBytes = 0;
private Stopwatch stopwatch;
public double Speed { get; private set; }
double[] DataPoints;
public NetSpeedCounter(WebClient webClient, int maxPoints = 10)
{
DataPoints = new double[maxPoints];
stopwatch = new Stopwatch();
Array.Clear(DataPoints, 0, DataPoints.Length);
webClient.DownloadProgressChanged += (sender, e) =>
{
var msElapsed = DateTime.Now - LastUpdate;
stopwatch.Stop();
int curBytes = (int)(e.BytesReceived - PrevBytes);
PrevBytes = (int)e.BytesReceived;
//record in kbps
double dataPoint = (double)curBytes / (stopwatch.ElapsedMilliseconds);
DataPoints[NumCounts++ % maxPoints] = dataPoint;
//protect NumCount from overflow
if (NumCounts == Int32.MaxValue)
NumCounts = 0;
Speed = DataPoints.Average();
stopwatch.Start();
};
}
public void Start()
{
stopwatch.Start();
}
public void Reset()
{
PrevBytes = 0;
stopwatch.Reset();
}
}
public class Record
{
public string Directory;
public string File;
public Uri DownloadPath;
public Record(string uriPath, string fileOutputName)
{
this.Directory = #"C:\TestDir\";
this.DownloadPath = new Uri(uriPath);
this.File = fileOutputName;
}
public string GetFullPath()
{
return this.Directory + this.File;
}
}
}
I have come up with a different way of doing it.
Instead of adding a data point for each ProgressChanged event, I now increment a counter with the amount of bytes. This counter stays across multiple files, and then I simply take a datapoint using a timer, and get the time in between using a System.Diagnostic.Stopwatch
It works very good, the accuracy is much much better, I would highly recommend doing it this way.
Here is some code
class NetSpeedCounter
{
private Stopwatch watch;
private long NumCounts = 0;
private int PrevBytes = 0;
private double[] DataPoints;
private long CurrentBytesReceived = 0;
public double Speed { get; private set; }
private System.Timers.Timer ticker = new System.Timers.Timer(100);
public NetSpeedCounter(WebClient webClient, int maxPoints = 5)
{
watch = new System.Diagnostics.Stopwatch();
DataPoints = new double[maxPoints];
webClient.DownloadProgressChanged += (sender, e) =>
{
int curBytes = (int)(e.BytesReceived - PrevBytes);
if (curBytes < 0)
curBytes = (int)e.BytesReceived;
CurrentBytesReceived += curBytes;
PrevBytes = (int)e.BytesReceived;
};
ticker.Elapsed += (sender, e) =>
{
double dataPoint = (double)CurrentBytesReceived / watch.ElapsedMilliseconds;
DataPoints[NumCounts++ % maxPoints] = dataPoint;
Speed = DataPoints.Average();
CurrentBytesReceived = 0;
watch.Restart();
};
}
public void Stop()
{
watch.Stop();
ticker.Stop();
}
public void Start()
{
watch.Start();
ticker.Start();
}
public void Reset()
{
CurrentBytesReceived = 0;
PrevBytes = 0;
watch.Restart();
ticker.Start();
}
}

Categories