Fade effects in Windows forms

后端 未结 2 559
-上瘾入骨i
-上瘾入骨i 2021-01-27 15:01

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\

相关标签:
2条回答
  • 2021-01-27 15:26

    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.

    AnimateButton

    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<Button, AnimateButton> dict = new ConcurrentDictionary<Button, AnimateButton>();
    
            // Update or create an animation for a button
            static public void Animate(Button sender, Direction direction)
            {
                AnimateButton animate;
                // find it...
                if (dict.TryGetValue(sender, out animate))
                {
                    animate.SetDirection(direction);
                }
                else
                {
                    // create a new one
                    animate = new AnimateButton(sender);
                    animate.SetDirection(direction);
                    if (dict.TryAdd(sender, animate))
                    {
                        Animations.List.Add(animate);
                    } 
                    else
                    {
                        Trace.WriteLine("button not added ?!?");
                    }
                }
            }
    
            int initialWidth = 75;
            int endWidth = 130;
    
            public enum Direction
            {
                None,
                Shrink,
                Grow
            }
    
            Direction direction = Direction.None;
            readonly Button button;
    
            private AnimateButton(Button button)
            {
                this.button = button;
            }
    
            public void SetDirection(Direction direction )
            {
                this.direction = direction;
            }
    
            // this gets called by the progress event
            public void Execute()
            {
                switch(direction)
                {
                    case Direction.Grow:
                        if (button.Width < endWidth)
                        {
                            button.Width++;
                        }
                        else
                        {
                            direction =  Direction.None;
                        }
                        break;
                    case Direction.Shrink:
                        if (button.Width > initialWidth)
                        {
                            button.Width--;
                        }
                        else
                        {
                            direction = Direction.None;
                        }
                        break;
                }
            }
        }
    

    ActiveAnimations

    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<ICommandExecutor> activeAnimations = new ConcurrentBag<ICommandExecutor>();
    
            public static ConcurrentBag<ICommandExecutor> List
            {
                get
                {
                    return activeAnimations;
                }
            }
        }
    

    Timer

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

    The hover and leave events

    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:

    0 讨论(0)
  • 2021-01-27 15:35

    Let me start by saying, you may not even care anymore about getting an answer as this question is almost 1 year old. I felt the need to post this answer because I think it does help answer the OP's question, and not by providing a solution but by providing a road-map of how to get to the solution...I think that's the best way to learn.

    Let's begin...

    Transitions are choppy because your event handler for "MouseHover" fires continuously as the mouse hovers over the control. This is causing your animations to get interrupted and making them appear choppy.

    My recommendation here, use the "MouseEnter" event to start your timer.

    Investigate a little more and you might find similarities between your two timers and use a single timer to do both tasks by creating your own timer that has a method that can be triggered by these two events to cause the button to grow or shrink.

    Also, though people have suggested WPF (and I don't disagree, WPF can be useful), don't lose faith in System.Windows.Forms.

    0 讨论(0)
提交回复
热议问题