I am trying to add somefading effects to a button, a picturebox and a text box, using Windows Forms.
I know I should use WPF for this, but I never worked with it and it\
Although this might be easier supported in WPF here is solution that works in WinForms.
When I need to animate I find it much easier to have ONE single background thread that coordinates the animation over an instance that keeps track of the progress. Having an Animation class instance that simple gets called frequently on a background worker makes the headache a lot less.
Lets first look at how we can animate a button. For internal housekeeping and keeping track of for which buttons we already have an animation going we use the static method Animate. It takes a button and a direction as parameter and then finds or updates the instance that belongs to the button.
The key feature is the Execute method that is an implementation of the interface ICommandExecutor. That method gets called every time the next step of an animation is needed (it gets called with a rate of 30 fps). In this case I only change the Width based on the direction but if needed more properties can be changed. Don't loop or block this method though because Excecute will run on the UI thread and that thread doesn't like to be held up to much.
// resize a button
private class AnimateButton : ICommandExecutor
{
// keep track of instances of this class
static ConcurrentDictionary
On the form their needs to be a masterlist of animations that need to be processed. The class ActiveAnimations does that. It holds a ConcurrentBag of ICommandExcutor instances.
// for multiple animations
private class Animations
{
// both the UI thread and backgroud thread will use this
static ConcurrentBag activeAnimations = new ConcurrentBag();
public static ConcurrentBag List
{
get
{
return activeAnimations;
}
}
}
To drive the animations forward a Timer is used. I prefer that one because it comes with the capability to switch to the UI thread without the need of extra lines of code. The timer Interval is set to 30 milliseconds.
private void timer1_Tick(object sender, EventArgs e)
{
// loop over all active animations and have them execute one step
foreach (var command in Animations.List)
{
command.Execute();
}
}
Don't forget to start the timer in the Form_Load event:
private void Form1_Load(object sender, EventArgs e)
{
timer1.Enabled = true;
timer1.Start();
}
As we have the plumbing in place we can now create the AnimateButton instances in the events on the buttons:
private void button1_MouseHover(object sender, EventArgs e)
{
AnimateButton.Animate((Button)sender, AnimateButton.Direction.Grow);
}
private void button1_MouseLeave(object sender, EventArgs e)
{
AnimateButton.Animate((Button)sender, AnimateButton.Direction.Shrink);
}
You can have multiple buttons animating. I tested it with 2 buttons and that worked well. You can always consider reducing the fps by increasing the Interval of the timer.
If all is implemented correctly this will be your result: