this is my first post here. First off, some background, I'm new to C# and am literally in my teething stages. I'm a chemical engineer and mostly have experience with languages like MATLAB and Mathematica, but i have always liked programming and have decided to learn C# to create some user friendly interfaces for some programs I have been using. Note that i am using windows forms and not WPF.
What i would like to do is have a main menu screen linking to various forms. Now to make it look nicer, what i want to do is this; when i hover over a picturebox(the picture is of a button) in the main window i would like for the button to 'grow' a little bit, and then when i leave it should 'shrink' to its original size. So far my method has been to try and load a gif of this growth animation on the mouseEnter event and then load a shrink animation on the mouseLeave, but this just loops the respective gif over and over. How can i get the gif to play once only?
i tried loading the frames sequentially with a thread sleep in between them as well but all i see when i do this is the last image i try to load. here's an example of the code used for this method, where i try to show one image, and then show another after 0.1 seconds
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
((PictureBox)sender).Image =Image.FromFile("C:/Users/michael/Desktop/131.bmp");
Thread.Sleep(100);
((PictureBox)sender).Image = Image.FromFile("C:/Users/michael/Desktop/131a.bmp");
}
Also is there a way to do this without a gif, such as by using a for loop to increase the size of the button or picturebox?
EDIT 1:
where do i put the timer stop in so that when i start the second animation, the first one stops?
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
Timer timeraf = new Timer();
timeraf.Interval = 10;
timeraf.Tick += new EventHandler(timerAfwd_Tick);
timeraf.Start();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
Timer timerab = new Timer();
timerab.Interval = 10;
timerab.Tick += new EventHandler(timerAbwd_Tick);
timerab.Start();
}
If you make your main thread sleep, I'm pretty sure it both locks out user input and it blocks the thread that handles painting. So your image can't paint any animations. Try using a timer like this:
public partial class Animation : Form
{
Image img1 = Properties.Resources.img1;
Image img2 = Properties.Resources.img2;
Image img3 = Properties.Resources.img3;
List<Image> images = new List<Image>();
Timer timer = new Timer();
public Animation()
{
InitializeComponent();
timer.Interval = 250;
timer.Tick += new EventHandler(timer_Tick);
images.Add(img1);
images.Add(img2);
images.Add(img3);
animated_pbx.Image = img1;
}
private void timer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler(timer_Tick));
}
else
{
// Loop through the images unless we've reached the final one, in that case stop the timer
Image currentImage = animated_pbx.Image;
int currentIndex = images.IndexOf(currentImage);
if (currentIndex < images.Count - 1)
{
currentIndex++;
animated_pbx.Image = images[currentIndex];
animated_pbx.Invalidate();
}
else
{
timer.Stop();
}
}
}
private void animated_pbx_MouseEnter(object sender, EventArgs e)
{
timer.Start();
}
private void animated_pbx_MouseLeave(object sender, EventArgs e)
{
timer.Stop();
animated_pbx.Image = img1;
animated_pbx.Invalidate();
}
}
EDIT: Changed code to add a animation when you hover over the PictureBox. The animation will stay on the final frame while you stay hovered. When the mouse leaves, it resets to the 1st frame. You can use any number of frames you want. You should also handle MouseDown and MouseUp, and create 1 more image to show the depressed or "down" state.
Related
I want to make a picture change itself very fast between 2 pictures. I have the pictures saved in the debug folder of the program.
Here is the image of pictures with there names saved:
https://i.stack.imgur.com/XQ9g5.png
So here is the code were I save a picture in the picturebox so that there is not an empty picturebox
private void Gamble_Load(object sender, EventArgs e)
{
pictureBox1.Image = (Image.FromFile("21.png"));
}
And here is the timer click method I think I need to use for this action
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image = pictureBox1.Image is null;
}
Try putting this in your Gamble_Load:
var t = new Timer(x => pictureBox1.Image = (Image.FromFile("otherimage.png")), null, 1000);
This creates a timer that will fire after 1 second and update the image.
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=net-6.0
So i tried to make a button more animated by making that when i hover my mouse over the button its back colour goes slowly from a darker gray to lighter gray, sadly the the MouseHover didn't worked out great for me because i had to use it with if (while, do and for isn't working at all) so i changed it to MouseMove And well that created its own problems, right now the colour getting lighter only when i move my mouse on it, here is the code:
private void BtnPlay_MouseMove(object sender, MouseEventArgs e)
{
byte coloDat = btnPlay.FlatAppearance.MouseOverBackColor.B;
if (btnPlay.FlatAppearance.MouseOverBackColor != Color.FromArgb(68, 68, 68))
{
coloDat++;
btnPlay.FlatAppearance.MouseOverBackColor = Color.FromArgb(coloDat, coloDat, coloDat);
System.Threading.Thread.Sleep(1);
}
}
Im gonna use the code multiple times in the project so is there a way to do this without making a wall of text?
Edit: For avoiding confusion; im trying to do my project with Button.FlatAppearance.MouseOverBackColor and not Button.BackgroundColour.
If you want to create this in WPF, just create a style with a storyboard. In windows forms, you need to use a timer.
The reason a loop didn't work for you is that the loop was running on the same thread as the UI, so the UI wasn't being updated until after your loop was over. To animate an effect in windows forms, you have to let the event function end so that the UI can update and then have your function called again for the next frame. That is what the timer element does.
I created a demo program with two animated buttons. To create buttons with animated background colors I first set up a start color, end color, the amount I want the color to change in each frame, the button the mouse is currently over and the transition progress of each button. I added that last so that I could have the buttons gradually transition back after the mouse was over something else.
private Color startColor = Color.AliceBlue;
private Color endColor = Color.BlueViolet;
private double step = 0.01;
private Button lastOver = null;
private Dictionary<Button, double> transitionProgress = new Dictionary<Button, double>();
Then I attached the event handlers of both of my buttons to the same functions, the functions below. The first uses ContainsKey so that I can make more buttons animated by just assigning them to these event handler functions.
private void demoButton_MouseHover(object sender, EventArgs e)
{
if (sender != lastOver)
{
lastOver = (Button)sender;
if (!transitionProgress.ContainsKey(lastOver))
{
transitionProgress[lastOver] = 0.0;
}
}
}
private void demoButton_MouseLeave(object sender, EventArgs e)
{
lastOver = null;
}
Then I created a Timer with the following event handler. It goes through each button and transitions it based on whether the mouse is currently over that button. It also only updates the background color if it has changed to improve performance.
private void styleUpdate_Tick(object sender, EventArgs e)
{
for (int i = 0; i < transitionProgress.Count; i++)
{
Button button = transitionProgress.Keys.ElementAt(i);
bool changing = false;
if (button == lastOver)
{
if (transitionProgress[button] < 1.0)
{
transitionProgress[button] = Math.Min(1.0, transitionProgress[button] + step);
changing = true;
}
}
else
{
if (transitionProgress[button] > 0.0)
{
transitionProgress[button] = Math.Max(0.0, transitionProgress[button] - step);
changing = true;
}
}
if (changing)
{
double progress = transitionProgress[button];
button.BackColor = Color.FromArgb(
(int)Math.Floor((endColor.R - startColor.R) * progress + startColor.R),
(int)Math.Floor((endColor.G - startColor.G) * progress + startColor.G),
(int)Math.Floor((endColor.B - startColor.B) * progress + startColor.B)
);
}
}
}
The timer has to be enabled and the interval set to 16
this.styleUpdate.Enabled = true;
this.styleUpdate.Interval = 16;
this.styleUpdate.Tick += new System.EventHandler(this.styleUpdate_Tick);
That does seem like a lot of code, but to add it to another button, you just need two more lines of code.
this.yourButtonName.MouseLeave += new System.EventHandler(this.demoButton_MouseLeave);
this.yourButtonName.MouseHover += new System.EventHandler(this.demoButton_MouseHover);
I made a panel with several buttons in WPF.
When mouse enter in some button, new buttons appear and will disappear 1000 ms after mouse leave.
But I have a strange behavior: 1000 ms in some case become shorter and shorter after each use.
Timer event
private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
{
_myTimerForButtonA.Stop();
miniButton1.Visibility = System.Windows.Visibility.Hidden;
}
private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
{
_myTimerForButtonB.Stop();
miniButton2.Visibility = System.Windows.Visibility.Hidden;
}
WaitTime functions calling timer:
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerButtonA.Tick += new EventHandler(TimerEventProcessorForForButtonA);
_myTimerForForButtonA.Interval = givenTime;
_myTimerForForButtonA.Start();
}
public void WaitThisTimeAndHideMiniButton2(int givenTime)
{
_myTimerForButtonB = new System.Windows.Forms.Timer();
_myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForForButtonB);
_myTimerForForButtonB.Interval = givenTime;
_myTimerForForButtonB.Start();
}
Event when leave button:
private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
}
private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush) new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton2(1000);
}
As described in code, I have buttonA and buttonB. When I enter buttonA, miniButton1 appear - it will disappear 1000 ms after mouse leave event.
Same thing for buttonB that reveal miniButton2.
If I only enter and leave buttonB everything is correct. Same as ButtonA.
The problem: If I enter/leave buttonA and B, then, these 1000ms go shorter and shorter. Disappearance of miniButton also appear before I leave buttonA and B. also happen before I leave the button.
Eveything behave like if timers were confusing each other.
Do you know how to solve it?
You're recreating a new Timer every time a MouseLeave event is fired, if you happen to fire that event twice within 1000ms, you'll be recreating a new Timer before the Tick event of the previous Timer execute, which will make the previous Timer run indefinitely.
Here's what's happening when you fire MouseLeave event twice within 1000ms :
1) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer1), Tick event registered.
2) MouseLeave: _myTimerForButtonA is assigned a new Timer instance (ie: Timer2), Tick event registered.
3) Timer1.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2.
4) Timer2.Tick event fires TimerEventProcessorForButtonA, whichs stops _myTimerForButtonA which points to Timer2 (and is already stopped).
5) Timer1.Tick event fires TimerEventProcessorForButtonA indefinitely, because Timer1 is not referenced anymore and no one will ever call `Stop` on it.
I was able to fix your code by stopping the timer on MouseEnter event
private void TimerEventProcessorForButtonA(Object myObject, EventArgs myEventArgs)
{
Debug.WriteLine("TimerEventProcessorForButtonA");
_myTimerForButtonA.Stop();
_myTimerForButtonA.Dispose();
miniButton1.Visibility = System.Windows.Visibility.Hidden;
}
private void TimerEventProcessorForButtonB(Object myObject, EventArgs myEventArgs)
{
Debug.WriteLine("** TimerEventProcessorForButtonB");
_myTimerForButtonB.Stop();
_myTimerForButtonB.Dispose();
miniButton2.Visibility = System.Windows.Visibility.Hidden;
}
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerForButtonA.Tick += new EventHandler(TimerEventProcessorForButtonA);
_myTimerForButtonA.Interval = givenTime;
_myTimerForButtonA.Start();
}
public void WaitThisTimeAndHideMiniButton2(int givenTime)
{
_myTimerForButtonB = new System.Windows.Forms.Timer();
_myTimerForButtonB.Tick += new EventHandler(TimerEventProcessorForButtonB);
_myTimerForButtonB.Interval = givenTime;
_myTimerForButtonB.Start();
}
private void buttonA_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton1(1000); // hide minibuttons
}
private void buttonB_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Border button = sender as Border;
button.Background = (SolidColorBrush)new BrushConverter().ConvertFromString(_colorOut);
WaitThisTimeAndHideMiniButton2(1000);
}
private void buttonA_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_myTimerForButtonA?.Enabled == true)
_myTimerForButtonA.Stop();
miniButton1.Visibility = System.Windows.Visibility.Visible;
}
private void buttonB_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_myTimerForButtonB?.Enabled == true)
_myTimerForButtonB.Stop();
miniButton2.Visibility = System.Windows.Visibility.Visible;
}
As stated in comments of your post, you shouldn't create a new Timer each time, and you should dispose it once you're done. Also, referencing Forms in a WPF project is a bad idea unless really necessary, which rarely is. You'll probably want to read about Storyboard for UI animation.
So basically this happens on mouse leave:
creating the timer
starting the timer
And this happens when timer expires:
stopping the timer
So if mouse repeatedly enters and leaves a button, it keeps creating and starting new timers. Which causes more than one timer to be running at the same time.
public void WaitThisTimeAndHideMiniButton1(int givenTime)
{
if(_myTimerForButtonA != null && _myTimerForButtonA.Enabled)
{
_myTimerForButtonA.Stop();
}
_myTimerForButtonA = new System.Windows.Forms.Timer();
_myTimerButtonA.Tick += new EventHandler(TimerEventProcessorForForButtonA);
_myTimerForForButtonA.Interval = givenTime;
_myTimerForForButtonA.Start();
}
Although there are more issues here as mentioned in comments, this should fix that problem.
I am using window app and C#.. i have a picture which is invisible at the start of the app.. when some button is clicked, the picture box has to be shown..
i use this coding but the picture box is not visible
private void btnsearch_Click(object sender, EventArgs e)
{
if (cmbproject.Text == "---Select---")
{
MessageBox.Show("Please Select Project Name");
return;
}
else
{
pictureBox1.Visible = true;
pictureBox1.BringToFront();
pictureBox1.Show();
FillReport();
Thread.Sleep(5000);
pictureBox1.Visible = false;
}
}
Don't use Sleep - that blocks the thread, which means no windows messages get processed, and your form won't get repainted.
Instead, you could use a Timer to hide the image after 5 seconds.
Add a timer to your form, and change your code to be something like this:
pictureBox1.Visible = true;
FillReport();
timer1.Interval = 5000;
timer1.Start();
And in the timer event:
private void Timer1_Tick(object sender, EventArgs e) {
pictureBox1.Visible = false;
timer1.Stop();
}
Now your image should be visible for 5 seconds.
However, the form will still not repaint while FillReport is executing. If you need the image to be visible at that point, I suggest using a BackgroundWorker to execute FillReport so that it doesn't block the UI thread. Then you can hide the image in the RunWorkerCompleted event.
Hello I am kind of new to C#... I have a picturebox which is my control. It's meant to be rapidly clicked on. I want to make it so when it is clicked a certain amount of times a second picturebox which has a .gif in it becomes visible. And when a certain amount of time has past without clicking the first picturebox the second disappears. Is there a way to do this? Maybe with timers. Some sample code will help me out A LOT! Thanks to all in advance! :)
Try This:
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
timer1.Interval=60000;//one minute
timer1.Tick += new System.EventHandler(timer1_Tick);
timer1.Start();
private void pictureBox1_Click(object sender, EventArgs e)
{
timer1.Stop();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
//do whatever you want
pictureBox2.Visible = false;
}