是否可以在 WPF listview
中实现类似 Firefox 中平滑滚动的效果?
当 Firefox 浏览器包含了所有的 listview
项,并且你按住鼠标中键(但不释放),然后拖动它,它应该会平滑地滚动 listview
的项。当你释放它时,它应该停止。
看起来在 WinForms 中似乎不可能,但我想知道在 WPF 中是否可行?
你尝试设置了吗:
ScrollViewer.CanContentScroll="False"
在列表框上吗?
这样做可以通过面板来处理滚动,而不是列表框...不过如果你有大量内容的话,你会失去虚拟化,所以速度可能会变慢。
确实可以做到您所要求的,不过需要相当量的自定义代码。
通常,在WPF中,ScrollViewer使用的是所谓的逻辑滚动(Logical Scrolling),这意味着它会按项(item)而不是按偏移量(offset)进行滚动。其他答案涵盖了一些将逻辑滚动行为改变为物理滚动(Physical Scrolling)的方法。另一种方法是利用ScrollViewer和IScrollInfo两者公开的ScrollToVerticalOffset和ScrollToHorizontalOffset方法。
为了实现较大的部分,即按下鼠标滚轮时的滚动,我们需要利用MouseDown和MouseMove事件。
<ListView x:Name="uiListView"
Mouse.MouseDown="OnListViewMouseDown"
Mouse.MouseMove="OnListViewMouseMove"
ScrollViewer.CanContentScroll="False">
....
</ListView>
在MouseDown事件中,我们将记录当前鼠标位置,作为相对点以确定滚动方向。在MouseMove事件中,我们将获取ListView的ScrollViewer组件,然后相应地滚动。
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或类似的东西。
我知道这篇文章已经13年了,但这仍然是人们想要做的事情。
在较新版本的.Net中,您可以设置VirtualizingPanel.ScrollUnit="Pixel"
这样您就不会失去虚拟化,并且您可以按像素而不是按项滚动。
<ScrollViewer IsTabStop="True" VerticalScrollBarVisibility="Auto">
<ListView></ListView>
</ScrollViewer>