Constant color change using WPF - c#

Hi I created 4 rectangles and gave them a color each. I am now trying to use a button when pressed to randomly change the colors of these 4 boxes constantly (creating some kind of blinking effect). Worked out the following code but the program jams when I press the button.
Read up that System.Windows.Threading.DispatcherTimer will be of use in this situation but not exactly sure how to use it. Looking for advice on how to go about using it. Thank you.
namespace testDisco
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Random r;
Rectangle[] rects;
SolidColorBrush blue;
SolidColorBrush red;
SolidColorBrush yellow;
SolidColorBrush green;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
r = new Random();
rects = new Rectangle[4];
blue = new SolidColorBrush(Color.FromArgb(255,0,232,255));
red = new SolidColorBrush(Color.FromArgb(129,56,56,255));
yellow = new SolidColorBrush(Color.FromArgb(0,232,255,255));
green = new SolidColorBrush(Color.FromArgb(176,207,176,255));
rects[0] = rect1;
rects[1] = rect2;
rects[2] = rect3;
rects[3] = rect4;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int randomNumber = r.Next(4);
System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
while (true)
{
rects[randomNumber].Fill = red;
}
}
}
}

You're almost there. Slow down your loop a bit so it doesn't crash your program.
System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
dt.Interval = TimeSpan.FromSeconds(1); //or whatever interval you want
dt.Tick += (s, e) =>
{
rects[r.Next(0,3)].Fill = red;
}
Though that will eventually just make them all red because while you're choosing a random rectangle, you aren't choosing a random color.

You may want to take a look at the WPF animation system as defined here:
Animation Overview - MSDN
It's a bit complicated to post an answer here, but I found it a while back when I was trying to do something similar. It works better than trying to do it yourself, though. In fact, there's even a ColorAnimation that might do exactly what you need.

Related

Do you have to change the background image every second?

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.

Hover effect CSS in a Button or Panel C# [duplicate]

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

Fading effect on Splash sreen

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();
}

Background image shows, but pen lines don't

I set a background of a picturebox as an image, which is fine.
I try to draw pen lines over the background image, but the lines do not appear on top of the image
This should happen: (I changed the code and not a clue what I changed to get this to happen, but this is without resetting the picturebox)
The picturebox is updated every 2000ms to show the flag signals, but can't for the life of me get the lines to appear.
Here are the bits of code:
lblSelectedChar.Text = inputchar.ToString();
picSemaphore.BackgroundImage = semaphore.Properties.Resources.human;
leftHandDown();
rightHandLow();
The leftHandDown() and rightHandLow() methods draw the lines, but I think they are drawn behind the image. I would like them drawn in front. Any idea how I would do this?
private void leftHandDown()
{
lblLeftHand.Hide();
display.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
}
Also, when the label is moved, it leaves a white space where it last was. Any idea how to stop this happening?
As LarsTech suggested, use the Paint() event of your PictureBox to draw the lines with the supplied Graphics via the e.Graphics value. You'd call picSemaphore.Refresh(); to make the lines update.
Here's a quick example to give you the basic idea:
public partial class Form1 : Form
{
private bool displayLeftArmLine = false;
private bool displayRightArmLine = false;
public Form1()
{
InitializeComponent();
picSemaphore.Paint += new PaintEventHandler(picSemaphore_Paint);
}
void picSemaphore_Paint(object sender, PaintEventArgs e)
{
if (displayLeftArmLine)
{
e.Graphics.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
}
if (displayRightArmLine)
{
e.Graphics.DrawLine(penRightArm, centXCoord, centYCoord, RHDownXCoord, RHDownYCoord);
}
}
private void leftHandDown()
{
lblLeftHand.Hide();
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
displayLeftArmLine = true;
picSemaphore.Refresh(); // force Paint() event for "picSemaphore" to fire
}
}

How to slow down animated gif

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);
}
}

Categories