c# - 以编程方式滚动带有计时器的 ScrollViewer 变得不稳定

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

我的方法是否有问题导致这种奇怪的抽搐?知道如何解决吗?

最佳答案

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方法。

关于c# - 以编程方式滚动带有计时器的 ScrollViewer 变得不稳定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17930481/

相关文章:

.net - 如何通过.NET Framework源代码进行调试?

.net - 如何根据 namespace 正确命名类?

c# - .NET 6 - 带有 CamelCase 的 AddJsonOptions 不起作用

c# - 使用键盘作为XAML和C#计算器的输入

c# - Datagrid 没有双击编辑列

c# - 如何从列表中删除特定的游戏对象(在数组中)?

c# - 如何在请求验证进入 MVC 4 之前清理输入?

c# - KeyedCollection 和 d :DataContext Design Error

c# - 在 Unity 中替换 Mathf.PerlinNoise

c# - 将 List<object> 绑定(bind)到 Datagrid WPF,C#