WPF实现无缝循环滚动

如果用ScrollViewer来进行滚动的话,会出现滚动到底部,然后直接跳到最开始,这样跳动的情况发生。
有时候我们需求的是无缝滚动,滚动到最底部,最顶部可以连着最底部接着滚动。
基于Canvas的TranslateTransform实现,并且结合WPF的  CompositionTarget.Rendering 事件实现无缝循环滚动。
XMAL代码

<Window
    x:Class="FlowDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:FlowDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    Background="DodgerBlue"
    mc:Ignorable="d">
    <ScrollViewer
        Width="200"
        Height="300"
        CanContentScroll="False"
        VerticalScrollBarVisibility="Hidden">
        <Canvas x:Name="canvas" Background="Black" />
    </ScrollViewer>

</Window>

CS代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace FlowDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            CompositionTarget.Rendering += CompositionTarget_Rendering;
            stopwatch.Start();
            InitView();
        }
        Queue<Button> ReadyQueue = new Queue<Button>();
        List<Button> ShowBtn = new List<Button>();
        Button lastBtn;
        float speed = 20;
        private void InitView()
        {
            for (int i = 0; i < 7; i++)
            {
                Button button = new Button();
                button.Width = 50;
                button.Height = 60;
                button.Click += Button_Click;
                button.Content = i.ToString();
                button.MouseEnter += Button_MouseEnter;
                button.MouseLeave += Button_MouseLeave;
                TranslateTransform trans = new TranslateTransform();
                button.RenderTransform = trans;
                trans.Y = i * button.Height;
                if (trans.Y<=300)
                {
                    ShowBtn.Add(button);

                }
                else { ReadyQueue.Enqueue(button); }
                lastBtn = ShowBtn[ShowBtn.Count - 1];
                canvas.Children.Add(button);
            }
        }

        private void Button_MouseLeave(object sender, MouseEventArgs e)
        {
            mouseEnter = false;
        }

        private void Button_MouseEnter(object sender, MouseEventArgs e)
        {
            mouseEnter = true;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var btn = sender as Button;
            MessageBox.Show($"这是按钮{btn.Content}");
        }

        private Stopwatch stopwatch = new Stopwatch();
        private TimeSpan prevTime = TimeSpan.Zero;
        bool mouseEnter = false;
        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            TimeSpan currentTime = this.stopwatch.Elapsed;
            double elapsedTime = (currentTime - this.prevTime).TotalSeconds;
            this.prevTime = currentTime;
            if (!mouseEnter)
            {
                foreach (var item in ShowBtn.ToArray())
                {
                    var trans = item.RenderTransform as TranslateTransform;
                    trans.Y -= speed * elapsedTime;
                    if (trans.Y <= -item.Height)
                    {
                        ShowBtn.Remove(item);
                        ReadyQueue.Enqueue(item);
                    }

                }
                var Lasttrans = lastBtn.RenderTransform as TranslateTransform;
                var Yvalue = Lasttrans.Y + lastBtn.Height;
                if (Yvalue > 300 && Yvalue < 310)
                {
                    var btn = ReadyQueue.Dequeue();
                    var trans1 = btn.RenderTransform as TranslateTransform;
                    trans1.Y = Yvalue;
                    ShowBtn.Add(btn);
                    lastBtn = btn;
                }
            }

        }
    }
}

基本原理是,如果一个button移出可见区域的时候,就把他放进等待队列里面,等到最后一个button即将全部出现的时候,从等待队列里面拿出一个button放在最后一个button下面。
git地址:https://github.com/Exceedingly0/WPFFlowDemo

posted @   Exceedingly  阅读(1171)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示