Windows窗体中的淡入淡出效果

时间:2015-07-23 19:02:44

标签: c# winforms

我正在尝试使用Windows窗体向按钮,图片框和文本框添加一些淡化效果。

我知道我应该使用WPF,但是我从来没有使用它,这对我来说很复杂,我的项目现在是提升平台切换。

我有这个,但不透明/褪色效果根本不光滑。

public void Animation(Button button1) {
    var expandTimer = new System.Windows.Forms.Timer();
    var contractTimer = new System.Windows.Forms.Timer();
    expandTimer.Interval = 10;
    contractTimer.Interval = 10;
    DateTime animationStarted = DateTime.Now;
    TimeSpan animationDuration = TimeSpan.FromMilliseconds(250);
    int initialWidth = 75;
    int endWidth = 130;

    button1.MouseHover += (_, args) => {
        contractTimer.Stop();
        expandTimer.Start();
        animationStarted = DateTime.Now;
        button1.ForeColor = Color.DimGray;
    };

    button1.MouseLeave += (_, args) => {
        expandTimer.Stop();
        contractTimer.Start();
        animationStarted = DateTime.Now;
        button1.ForeColor = Color.Red;
    };

    expandTimer.Tick += (_, args) => {
        double percentComplete = (DateTime.Now - animationStarted).Ticks
                / (double)animationDuration.Ticks;
        button1.Visible = false;

        if (percentComplete >= 1) {
            expandTimer.Stop();

        } else {
            button1.Width = (int)(initialWidth +
                    (endWidth - initialWidth) * percentComplete);

        }
    };

    contractTimer.Tick += (_, args) => {
        double percentComplete = (DateTime.Now - animationStarted).Ticks
                / (double)animationDuration.Ticks;
        button1.Visible = true;

        if (percentComplete >= 1) {
            contractTimer.Stop();

        } else {
            button1.Width = (int)(endWidth -
                    (endWidth - initialWidth) * percentComplete);
        }
    };
}

如何在不必切换到WPF的情况下修复不那么平滑的过渡?

2 个答案:

答案 0 :(得分:0)

首先让我说,你可能甚至不在乎得到一个答案,因为这个问题差不多是1岁。我觉得有必要发布这个答案,因为我认为它确实有助于回答OP的问题,而不是提供解决方案,而是提供如何获得解决方案的路线图...我认为&# 39;是最好的学习方式。

让我们开始......

转换是不稳定的,因为你的事件处理程序是" MouseHover"当鼠标盘旋在控制器上时,它会持续闪光。这会导致您的动画被中断并使它们看起来不稳定。

我的推荐,使用" MouseEnter"启动计时器的事件。

调查一下,您可能会发现两个计时器之间存在相似之处,并使用单个计时器通过创建自己的计时器来执行这两项任务,该计时器具有可由这两个事件触发的方法,从而使按钮增大或缩小

此外,虽然人们建议使用WPF(我并不反对,但WPF可能很有用),但不要对System.Windows.Forms失去信心。

答案 1 :(得分:-1)

虽然在WPF中可能更容易支持,但这里的解决方案适用于WinForms。

当我需要动画时,我发现让一个单一的后台线程更容易在一个跟踪进度的实例上协调动画。拥有一个简单的动画类实例,后台工作者经常调用这个实例,这让人头疼得多。

AnimateButton

让我们先看看我们如何为按钮设置动画。对于内部管理和跟踪我们已经有动画的按钮,我们使用静态方法Animate。它将按钮和方向作为参数,然后查找或更新属于该按钮的实例。

关键特性是Execute方法,它是接口ICommandExecutor的实现。每次需要动画的下一步(以30 fps的速率调用)时,都会调用该方法。在这种情况下,我只根据方向更改宽度,但如果需要,可以更改更多属性。不要循环或阻止此方法,因为Excecute将在UI线程上运行,并且该线程不喜欢被支持太多。

    // 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

在表单上,​​他们需要成为需要处理的动画的主列表。班级ActiveAnimations就是这样做的。它包含ConcurrentBagICommandExcutor个实例。

    // 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。我更喜欢那个,因为它具有切换到UI线程的能力,而无需额外的代码行。定时器间隔设置为30毫秒。

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

不要忘记在Form_Load事件中启动计时器:

    private void Form1_Load(object sender, EventArgs e)
    {
        timer1.Enabled = true;
        timer1.Start();
    }

悬停和离开事件

当我们有管道时,我们现在可以在按钮上的事件中创建AnimateButton个实例:

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

您可以设置多个按钮动画。我用2个按钮测试它,效果很好。您始终可以考虑通过增加计时器的间隔来减少fps。

如果一切正确实施,那将是您的结果:

animating buttons in action