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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南