以编程方式使用计时器滚动ScrollViewer变得不稳定

时间:2013-07-29 17:33:47

标签: c# .net wpf animation user-interface

动画ScrollViewer中的滚动似乎是一项常见任务。我使用计时器实现它,类似于here找到的方法。这种方法效果很好,非常流畅,看起来很完美。

但是,现在我的ScrollViewer中包含的对象的复杂性和数量增加了,动画看起来非常不稳定。我发现这很奇怪,因为如果我手动滚动它会正常工作。

    public void ShiftLeft(int speed = 11)
    {
        CustomTimer timer = new CustomTimer(); //DispatchTimer with "life"
        timer.Interval = new TimeSpan(0, 0, 0, 0, 5);
        timer.Tick += ((sender, e) =>
        {
            scrollViewer1.ScrollToHorizontalOffset(
                scrollViewer1.HorizontalOffset - (scrollViewer1.ScrollableWidth / (gridColumnCount - 3) / speed));
            if (scrollViewer1.HorizontalOffset == 0) //cant scroll any more
                ((CustomTimer)sender).Stop();
            ((CustomTimer)sender).life++;
            if (((CustomTimer)sender).life >= speed) //reached destination
                ((CustomTimer)sender).Stop();
        });
        timer.Start();
    }

我的方法有问题导致这种奇怪的抽搐吗?知道怎么解决吗?

1 个答案:

答案 0 :(得分:4)

CompositionTarget.Rendering将更适合动画制作,因为每次渲染帧时都会触发。尝试这样的事情:

    public void Shift(ScrollViewer target, double speed = 11, double distance = 20)
    {
        double startOffset = target.HorizontalOffset;
        double destinationOffset = target.HorizontalOffset + distance;

        if (destinationOffset < 0)
        {
            destinationOffset = 0;
            distance = target.HorizontalOffset;
        }

        if (destinationOffset > target.ScrollableWidth)
        {
            destinationOffset = target.ScrollableWidth;
            distance = target.ScrollableWidth - target.HorizontalOffset;
        }

        double animationTime = distance / speed;
        DateTime startTime = DateTime.Now;

        EventHandler renderHandler = null;

        renderHandler = (sender, args) =>
        {
            double elapsed = (DateTime.Now - startTime).TotalSeconds;

            if (elapsed >= animationTime)
            {
                target.ScrollToHorizontalOffset(destinationOffset);
                CompositionTarget.Rendering -= renderHandler;
            }

            target.ScrollToHorizontalOffset(startOffset + (elapsed * speed));
        };

        CompositionTarget.Rendering += renderHandler;
    }

编辑:添加范围检查

使用负距离值向左滚动。

编辑2:

您可能希望使用此CompositionTargetEx实现而不是CompositionTarget,因为它只会在渲染线程实际绘制新帧时触发:

https://stackoverflow.com/a/16334423/612510

编辑3:

由于您使用的是WPF(而不是像我更习惯的Silverlight),您可以使用Stopwatch类来测量经过的秒数而不是我的DateTime.Now方法。