i just started learning c# two month ago, and i got this code for my current project (windows forms) to set the background image:
public FormMain()
{
this.BackgroundImage = Properties.Resources.image;
InitializeComponent();
var timer = new Timer();
////change the background image every second
timer.Interval = 1000;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
//add image in list from resource file.
List<Bitmap> lisimage = new List<Bitmap>();
lisimage.Add(Properties.Resources.image);
var indexbackimage = DateTime.Now.Second % lisimage.Count;
this.BackgroundImage = lisimage[indexbackimage];
}
my question is: do you have to change the background image every second, or is it enough if i just write (i have only one single background image):
public FormMain()
{
this.BackgroundImage = Properties.Resources.image;
InitializeComponent();
}
cause it seems to work.
You would only need a timer like that if you were iterating through a series of images in order to create an animation.
What you have is good enough for setting the image once.
As AaronLS wrote, setting the background once would suffice. I'd go a step further and explain why the extra code you have makes very little sense (assuming this is the entire code).
void timer_Tick(object sender, EventArgs e)
{
//add image in list from resource file.
List<Bitmap> lisimage = new List<Bitmap>(); //this line creates a new list
lisimage.Add(Properties.Resources.image); //fill the NEWLY created list with the one image from the resources
//note, that resources are usually static, so it's always the same resource
var indexbackimage = DateTime.Now.Second % lisimage.Count; //choose an index from the list, but the list only contains that one image, so the index will always be 0
this.BackgroundImage = lisimage[indexbackimage]; //pick the same image that was set initially
}
As you can see, the code is rather nonsensical - it doesn't DO anything. It SEEMS someone wanted to create a mechanism to switch images every second, but even THAT is poorly coded.
Related
I would like a “Pause” the chart's series updates to do some job (like i have a button when i click it will suspend the chart update and then when I click resume button, it will update all suspended point in series.
I know about
chart1.Series.SuspendUpdates();
but it does not seem to work with me. I use mschart sample -- realtime data (thread safe).
Here is the full code
public partial class RealTimeSample : Form
{
public RealTimeSample()
{
InitializeComponent();
}
private Thread addDataRunner;
private Random rand = new Random();
public delegate void AddDataDelegate();
public AddDataDelegate addDataDel;
private void RealTimeSample_Load(object sender, System.EventArgs e)
{
// create the Adding Data Thread but do not start until start button clicked
ThreadStart addDataThreadStart = new ThreadStart(AddDataThreadLoop);
addDataRunner = new Thread(addDataThreadStart);
// create a delegate for adding data
addDataDel += new AddDataDelegate(AddData);
}
/// Main loop for the thread that adds data to the chart.
/// The main purpose of this function is to Invoke AddData
/// function every 1000ms (1 second).
private void AddDataThreadLoop()
{
while (true)
{
chart1.Invoke(addDataDel);
Thread.Sleep(1000);
}
}
public void AddData()
{
DateTime timeStamp = DateTime.Now;
foreach (Series ptSeries in chart1.Series)
{
AddNewPoint(timeStamp, ptSeries);
}
}
/// The AddNewPoint function is called for each series in the chart when
/// new points need to be added. The new point will be placed at specified
/// X axis (Date/Time) position with a Y value in a range +/- 1 from the previous
/// data point's Y value, and not smaller than zero.
public void AddNewPoint(DateTime timeStamp, System.Windows.Forms.DataVisualization.Charting.Series ptSeries)
{
double newVal = 0;
if (ptSeries.Points.Count > 0)
{
newVal = ptSeries.Points[ptSeries.Points.Count - 1].YValues[0] + ((rand.NextDouble() * 2) - 1);
}
if (newVal < 0)
newVal = 0;
// Add new data point to its series.
chart1.Series.SuspendUpdates();
ptSeries.Points.AddXY(timeStamp.ToOADate(), rand.Next(10, 20));
chart1.Series.SuspendUpdates();
// remove all points from the source series older than 1.5 minutes.
double removeBefore = timeStamp.AddSeconds((double)(90) * (-1)).ToOADate();
//remove oldest values to maintain a constant number of data points
while (ptSeries.Points[0].XValue < removeBefore)
{
ptSeries.Points.RemoveAt(0);
}
chart1.ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
chart1.ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddMinutes(2).ToOADate();
}
/// Clean up any resources being used.
protected override void Dispose(bool disposing)
{
if ((addDataRunner.ThreadState & ThreadState.Suspended) == ThreadState.Suspended)
{
addDataRunner.Resume();
}
addDataRunner.Abort();
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
private void startTrending_Click_1(object sender, EventArgs e)
{
// Disable all controls on the form
startTrending.Enabled = false;
// and only Enable the Stop button
stopTrending.Enabled = true;
// Predefine the viewing area of the chart
var minValue = DateTime.Now;
var maxValue = minValue.AddSeconds(120);
chart1.ChartAreas[0].AxisX.Minimum = minValue.ToOADate();
chart1.ChartAreas[0].AxisX.Maximum = maxValue.ToOADate();
// Reset number of series in the chart.
chart1.Series.Clear();
// create a line chart series
Series newSeries = new Series("Series1");
newSeries.ChartType = SeriesChartType.Line;
newSeries.BorderWidth = 2;
newSeries.Color = Color.OrangeRed;
newSeries.XValueType = ChartValueType.DateTime;
chart1.Series.Add(newSeries);
// start worker threads.
if (addDataRunner.IsAlive == true)
{
addDataRunner.Resume();
}
else
{
addDataRunner.Start();
}
}
private void stopTrending_Click_1(object sender, EventArgs e)
{
if (addDataRunner.IsAlive == true)
{
addDataRunner.Suspend();
}
// Enable all controls on the form
startTrending.Enabled = true;
// and only Disable the Stop button
stopTrending.Enabled = false;
}
}
EDIT:
I figured out that as long as you set the minmum or the maximum property for the Axis the chart will keep display even if you have used
chart1.Series.SuspendUpdates();
I had to to remove those lines after i call SuspendUpdates() and now i can see the chart series suspended
chart1.ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
chart1.ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddMinutes(2).ToOADate();
MsChart does support this directly and indeed using Series.SuspendUpdates() is a good way but you need to do it right. (See however the update below for a drawback)
MSDN says this:
A call to the Invalidate method will have no effect after the
SuspendUpdates method is called.
If you call the SuspendUpdates method several times, you will need to
call the ResumeUpdates method an equal number of times.
This would explain why it doesn't work for you: Keeping the calls balanced is crucial. You need to keep track of them yourself as there is no counter you could query. But if you overshoot the ResumeUpdates calls, nothing bad happens, extra calls are simply ignored and the next SuspendUpdates will pause again.
Here is an example screenshot, watch the suspension counter..!
Note that normally adding points will automatically triggger an Invalidate. If you are doing other things, like drawing in a Paint event etc.. you may need to call Chart.Invalidate(), which SuspendUpdates will prevent, until cancelled by the same number of ResumeUpdates..
Alternatively you can also use one of these simple workarounds:
The most straightforward will create the DataPoints via a constructor and then either
use series.Add(theNewPoint) for normal, or..
use someList<DataPoint>.Add(theNewPoint) for paused mode.
When setting to pause mode simply add all points to the series.Points before clearing it. Unfortunately there is no points.AddRange so you will have to use a foreach loop. Maybe chart.SuspendLayout could help with performance.
The other workaround that comes to mind may or may not be suitable: You could play with the xAxis.Maximum and maybe xAxis.Minimum values. By setting them to fixed values you would allow addding points to the right without displaying them. To show the whole set of points you would reset them to double.NaN. This may work for you but it may also interfer with what you have.
Update: As noted by OP, the data are updated when he changes the Minimum and/or Maximum of an Axis. The same effect will show on many other occasions:
Calling chart.AreasRecalculateAxesScale();
Changing the chart's Size
Changing any Axis property like Color or Width..
Changing the LegendText of a Series
and many more..
So I guess the updated data are needed whenever the ChartArea is manipulated and forced to update itself..
So, this may well make the 1st workaround the better because the more robust solution.
I am trying to learn .NET programming. As a part of my learning, I tried to make some effects on buttons. It is working... but not as smooth as I imagined! Is there any better way to do this? Thank you in advance!
My need:
There are 3 buttons.
When you hover the mouse over one of them, it expands and when you mouse out from that button, it returns to its initial size.
private void button1_MouseHover(object sender, EventArgs e)
{
button1.BackColor = Color.White;
button1.Width = 130;
button1.BringToFront();
}
private void button1_MouseLeave(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
button1.Width = 75;
}
private void button2_MouseHover(object sender, EventArgs e)
{
button2.BackColor = Color.Gray;
button2.Width = 130;
button2.BringToFront();
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
button2.BackColor = Color.Red;
button2.Width = 75;
}
private void button3_MouseHover(object sender, EventArgs e)
{
button3.BackColor = Color.DimGray;
button3.Width = 130;
button3.BringToFront();
}
private void button3_MouseLeave(object sender, EventArgs e)
{
button3.BackColor = Color.Red;
button3.Width = 75;
}
So first off, you don't want to do the exact same thing 3 times. Create a single method to add the appropriate handlers for a button, and then just write the code once to handle any given button.
Note that you can go into the expand/contract tick handlers and use the percentComplete value to set the height as well, to move the color along a spectrum (this would involve some mathematics of colors to do though) or to alter any other aspect of the button. If you're really motivated to generalize it you could add a parameter to the method of Action<double> that does something to the object based on the given percent progress.
public void AddAnimation(Button button)
{
var expandTimer = new System.Windows.Forms.Timer();
var contractTimer = new System.Windows.Forms.Timer();
expandTimer.Interval = 10;//can adjust to determine the refresh rate
contractTimer.Interval = 10;
DateTime animationStarted = DateTime.Now;
//TODO update as appropriate or make it a parameter
TimeSpan animationDuration = TimeSpan.FromMilliseconds(250);
int initialWidth = 75;
int endWidth = 130;
button.MouseHover += (_, args) =>
{
contractTimer.Stop();
expandTimer.Start();
animationStarted = DateTime.Now;
button.BackColor = Color.DimGray;
};
button.MouseLeave += (_, args) =>
{
expandTimer.Stop();
contractTimer.Start();
animationStarted = DateTime.Now;
button.BackColor = Color.Red;
};
expandTimer.Tick += (_, args) =>
{
double percentComplete = (DateTime.Now - animationStarted).Ticks
/ (double)animationDuration.Ticks;
if (percentComplete >= 1)
{
expandTimer.Stop();
}
else
{
button.Width = (int)(initialWidth +
(endWidth - initialWidth) * percentComplete);
}
};
contractTimer.Tick += (_, args) =>
{
double percentComplete = (DateTime.Now - animationStarted).Ticks
/ (double)animationDuration.Ticks;
if (percentComplete >= 1)
{
contractTimer.Stop();
}
else
{
button.Width = (int)(endWidth -
(endWidth - initialWidth) * percentComplete);
}
};
}
If you are using WinForms, animations are going to be rather painful and you will have to handle them yourself via Timer objects.
If you are getting into .NET and want to make cool-looking applications with animatons and styling, I highly recommend you look at WPF instead. It can do animations very easily though C# or XAML.
While it is still possible in WinForms, it will take far more development time where as those features are built into WPF already (and optimized).
When you modify a controls properties, it takes effect instantaneously. What you desire is something that is usually known as some type of fade or tweening. There might be libraries out there to do this, but if you wanted to write this yourself for fun, you can use a Timer object, and on each tick update the color.
What you would do is set a color as the TargetColor somewhere(this is a variable or property you make up), and then start a timer that ticks maybe every 10 milliseconds. In each tick, you look at the start time, and how long has passed since then. If you want the animation to take place of a full second, then that is 1000 milliseconds. So during each tick, you look at the amount of time that has passed, maybe 200 milliseconds, then divide 200/1000 to get the fraction of time that you have gone into the animation. Then you look at a difference between the Start and Target Color, multiply that difference by the fraction, and add the result to the start color. In other words, 200 milliseconds into an animation that last 1000 milliseconds, means you are 20% into the animation. Thus you want to set the color to be whatever color is 20% from the start color towards the end color.
There's alot you could do to refine this. Perhaps having a subclass Button control that encapsulates the timer and exposes functions/properties to track start/end color, animation transition time, etc. Most animated UI features like this let you specify how long the animation should last, and then it interpolates the inbetween states as it transitions. This is the origin of the term tweening, as it comes from transitioning from one state to another by inbetweening
I am developing an WP8 app and would like to change the background image with several images ; setting time as a variable and showing images in C#,
You can use DispatcherTimer class in order to change the images with respect to time.
Let me suggest you the easiest way. Name your images as numbers like
1.jpg,2.jpg,3.jpg etc and put them inside a folder.
Now you can use either random number class to pick the images in random order or can use the following method to get the sequentially:
DispatcherTimer picture_timer = new DispatcherTimer();
Random rnd = new Random();
picture_timer .Interval = new TimeSpan(0, 0, 3);
picture_timer .Tick += timer_Tick;
picture_timer .Start();
void timer_Tick(object sender, object e)
{
int num = rnd.Next(1, 13); // creates a number between 1 and 12
string image_source = "/Assets/"+num+".jpg";
}
I am trying to make a fading effect on my splash screen, on a WPF application.
The Opacity of the image object is initially 0. This code would modify the Opacity from 0 (min) to 1 (max), but the line img_waves.Opacity just doesn't work. The image opacity remains 0.
private void Splash_ContentRendered(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000);
for (double x = 0; x<=1; x+=0.01d)
{
System.Threading.Thread.Sleep(15);
//MessageBox.Show(x.ToString());
img_waves.Opacity = x;
}
this.Close();
}
But, if I activate the line ´MessageBox.Show(x.ToString());´
as you can see on this image:
The code works, but I have to keep clicking on the message boxes.
My ask is: Why? Why doesn't work without the MessageBox.Show?
Because you're blocking the GUI thread. It never gets a chance to redraw the form. When you add the message box, the message queue is pumped, which allows the drawing.
The simplest way to deal with this would be like this:
private async void Splash_ContentRendered(object sender, EventArgs e)
{
await Task.Delay(3000);
for (double x = 0; x<=1; x+=0.01d)
{
await Task.Delay(15);
img_waves.Opacity = x;
}
this.Close();
}
Do note that this means the form can still be interacted with during the animation. This shouldn't be a problem for a splashscreen, but it could cause you trouble in a "real" form. Still, make sure the form can't be closed during the animation - that could cause exceptions :)
There's also other ways to force the message queue to be pumped, but it's usually frowned upon.
All that said, you're using WPF - why are you doing the animation manually like this? Can't you just handle it as an animation effect in WPF, natively? There's a sample on MSDN.
Whilst I agree with #Luaan explanation as to why as an alternative solution to your loop you can use Storyboard with DoubleAnimation on Opacity property
private void Splash_ContentRendered(object sender, EventArgs e)
{
var sb = new Storyboard();
var da = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(1.5)));
da.BeginTime = TimeSpan.FromSeconds(3);
Storyboard.SetTargetProperty(da, new PropertyPath("Opacity"));
Storyboard.SetTarget(da, img_waves);
sb.Children.Add(da);
sb.Completed += (s1, e1) => this.Close();
sb.Begin();
}
I have a WinForms app that displays an animated gif in the simplest possible way - there is a PictureBox that loads the .gif directly.
The code generated by the WinForms designer looks like this:
//
// pictureBoxHomer
//
this.pictureBoxHomer.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.pictureBoxHomer.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBoxHomer.Image = ((System.Drawing.Image)(resources.GetObject("pictureBoxHomer.Image")));
this.pictureBoxHomer.Location = new System.Drawing.Point(3, 3);
this.pictureBoxHomer.Name = "pictureBoxHomer";
this.pictureBoxHomer.Size = new System.Drawing.Size(905, 321);
this.pictureBoxHomer.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.pictureBoxHomer.TabIndex = 0;
this.pictureBoxHomer.TabStop = false;
The image is, of course, this: http://media.tumblr.com/tumblr_m1di1xvwTe1qz97bf.gif
Problem: while this animated gif displays wondrously in the browser, it is running way too fast in the WinForms app, which is not as happy as needed. So:
Question: is there a way to slow down an animated gif in a WinForms app?
I believe the answer is rather image-related than C#. If you edit that specific image in a tool like GIMP and take a look at the layers, you'll see it's a composition of 10 layers (frames) but no "delay time" between them is set - it has (0ms) in layer's attribute. You can edit layer's attribute and change it by right-clicking on it and selecting that option in menu. Of course, at the end you have to export your new image and save it as a GIF, selecting "animated" in options.
I believe in this case (when no delay time between frames is specified) web browser and C# PicutureBox force their own,different, default values. So, if you put a delay let say 100ms, like described here in step 3, you'll make the animation slow down.
For future reference, it is possible to override the delay time of a GIF in a picture box. Here is a rough example:
public partial class Form1 : Form
{
private FrameDimension dimension;
private int frameCount;
private int indexToPaint;
private Timer timer = new Timer();
public Form1()
{
InitializeComponent();
dimension = new FrameDimension(this.pictureBox1.Image.FrameDimensionsList[0]);
frameCount = this.pictureBox1.Image.GetFrameCount(dimension);
this.pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
indexToPaint++;
if(indexToPaint >= frameCount)
{
indexToPaint = 0;
}
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
this.pictureBox1.Image.SelectActiveFrame(dimension, indexToPaint);
e.Graphics.DrawImage(this.pictureBox1.Image, Point.Empty);
}
}