【发布时间】:2025-11-25 14:05:01
【问题描述】:
是否可以像在 Firefox 中一样在 WPF listview 中实现平滑滚动?
当 Firefox 浏览器包含所有 listview 项目并且您按住鼠标中键(但不释放)并拖动它时,它应该平滑滚动 listview 项目。当你释放它应该停止。
看起来这在winforms中是不可能的,但我想知道它是否可以在WPF中使用?
【问题讨论】:
标签: c# .net wpf listview scroll
是否可以像在 Firefox 中一样在 WPF listview 中实现平滑滚动?
当 Firefox 浏览器包含所有 listview 项目并且您按住鼠标中键(但不释放)并拖动它时,它应该平滑滚动 listview 项目。当你释放它应该停止。
看起来这在winforms中是不可能的,但我想知道它是否可以在WPF中使用?
【问题讨论】:
标签: c# .net wpf listview scroll
你可以实现平滑滚动,但你失去了项目虚拟化,所以基本上你应该只在列表中的元素很少时使用这种技术:
这里的信息:Smooth scrolling on listbox
你试过设置吗:
ScrollViewer.CanContentScroll="False"在列表框上?
这样滚动是由面板而不是列表框来处理的......如果你这样做,你会失去虚拟化,所以如果你有很多内容,它可能会变慢。
【讨论】:
确实可以按照您的要求进行操作,但需要大量的自定义代码。
通常在 WPF 中,ScrollViewer 使用所谓的逻辑滚动,这意味着它将逐项滚动,而不是按偏移量滚动。其他答案涵盖了一些可以将逻辑滚动行为更改为物理滚动行为的方法。另一种方法是利用 ScrollViwer 和 IScrollInfo 公开的 ScrollToVertialOffset 和 ScrollToHorizontalOffset 方法。
要实现较大的部分,即按下鼠标滚轮时的滚动,我们需要使用 MouseDown 和 MouseMove 事件。
<ListView x:Name="uiListView"
Mouse.MouseDown="OnListViewMouseDown"
Mouse.MouseMove="OnListViewMouseMove"
ScrollViewer.CanContentScroll="False">
....
</ListView>
在 MouseDown 中,我们将记录当前鼠标位置,我们将使用它作为相对点来确定我们滚动的方向。在鼠标移动中,我们将获取 ListView 的 ScrollViwer 组件和然后相应地滚动它。
private Point myMousePlacementPoint;
private void OnListViewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.MiddleButton == MouseButtonState.Pressed)
{
myMousePlacementPoint = this.PointToScreen(Mouse.GetPosition(this));
}
}
private void OnListViewMouseMove(object sender, MouseEventArgs e)
{
ScrollViewer scrollViewer = ScrollHelper.GetScrollViewer(uiListView) as ScrollViewer;
if (e.MiddleButton == MouseButtonState.Pressed)
{
var currentPoint = this.PointToScreen(Mouse.GetPosition(this));
if (currentPoint.Y < myMousePlacementPoint.Y)
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - 3);
}
else if (currentPoint.Y > myMousePlacementPoint.Y)
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + 3);
}
if (currentPoint.X < myMousePlacementPoint.X)
{
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - 3);
}
else if (currentPoint.X > myMousePlacementPoint.X)
{
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + 3);
}
}
}
public static DependencyObject GetScrollViewer(DependencyObject o)
{
// Return the DependencyObject if it is a ScrollViewer
if (o is ScrollViewer)
{ return o; }
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var child = VisualTreeHelper.GetChild(o, i);
var result = GetScrollViewer(child);
if (result == null)
{
continue;
}
else
{
return result;
}
}
return null;
}
它有一些不足之处,因为它只是一个概念证明,但它肯定会让你朝着正确的方向开始。为了让它在鼠标离开初始 MouseDown 点后不断滚动,滚动逻辑可以进入 DispatcherTimer 或类似的东西。
【讨论】:
尝试在 ListView 上将 ScrollViewer.CanContentScroll 附加属性设置为 false。但是就像 Pop Catalin 说的那样,你失去了项目虚拟化,这意味着列表中的所有项目都会立即加载和填充,而不是在需要显示一组项目时 - 所以如果列表很大,这可能会导致一些内存和性能问题。
【讨论】:
尝试将列表视图的高度设置为自动并将其包装在滚动查看器中。
<ScrollViewer IsTabStop="True" VerticalScrollBarVisibility="Auto">
<ListView></ListView>
</ScrollViewer>
别忘了提一下ScrollViewer的高度 希望这会有所帮助....
【讨论】: