是否可以在WPF列表视图中实现平滑滚动?

Joa*_*nge 38 .net c# wpf listview scroll

是否可以在WPF中实现平滑滚动,listview就像在Firefox中一样?
当Firefox浏览器包含所有listview项目并按住鼠标中键(但不是释放)并拖动它时,它应该平滑地滚动listview项目.当你释放它应该停止.

看起来这在winforms中是不可能的,但我想知道它是否在WPF中可用?

Pop*_*lin 68

您可以实现平滑滚动,但丢失项目虚拟化,所以基本上只有在列表中包含少量元素时才应使用此技术:

信息在这里:在列表框上平滑滚动

你试过设置:

ScrollViewer.CanContentScroll="False"
Run Code Online (Sandbox Code Playgroud)

在列表框上?

这种方式滚动是由面板而不是listBox处理的...如果你这样做会失去虚拟化,所以如果你有很多内容它可能会变慢.

  • 虚拟化是VirtualizingStackPanel内部的内容(ListBoxs的默认ItemsPanel等)实际上不会呈现项目的布局,除非项目可见.因此,对于众多或视觉上复杂的项目,它可以提供显着的性能提升,因为它一次只产生一小部分的ui.更多信息:http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx (11认同)
  • 谢谢波普。什么是虚拟化? (2认同)

rmo*_*ore 11

确实有可能做你要求的,虽然它需要相当数量的自定义代码.

通常在WPF中,ScrollViewer使用所谓的逻辑滚动,这意味着它将逐项滚动而不是按偏移量滚动.其他答案涵盖了将逻辑滚动行为更改为物理滚动行为的一些方法.另一种方法是使用ScrollViwer和IScrollInfo公开的ScrollToVertialOffset和ScrollToHorizo​​ntalOffset方法.

要实现较大的部分,按下鼠标滚轮时滚动,我们将需要使用MouseDown和MouseMove事件.

<ListView x:Name="uiListView"
          Mouse.MouseDown="OnListViewMouseDown"
          Mouse.MouseMove="OnListViewMouseMove"
          ScrollViewer.CanContentScroll="False">
    ....
</ListView>
Run Code Online (Sandbox Code Playgroud)

在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;
}
Run Code Online (Sandbox Code Playgroud)

它缺少一些领域,因为它只是一个概念证明,但它绝对应该让你开始朝着正确的方向前进.要在鼠标从初始MouseDown点移开后不断滚动,滚动逻辑可以进入DispatcherTimer或类似的东西.


小智 11

我知道这篇文章已经有 13 年历史了,但这仍然是人们想做的事情。在较新版本的 .Net 中,您可以VirtualizingPanel.ScrollUnit="Pixel" 这样设置,这样您就不会丢失虚拟化,并且可以按像素滚动而不是按项目滚动。

  • 谢谢,经过测试,它可以在 WPF .Net 7 上运行! (5认同)

Edd*_*die 5

尝试在ListView上将ScrollViewer.CanContentScroll附加属性设置为false。但是就像Pop Catalin所说的那样,你失去了项目虚拟化,这意味着列表中的所有项目都会立即加载和填充,而不是在需要显示一组项目时 - 所以如果列表很大,它可能会导致一些内存和性能问题。

  • 看看这个相关的问题/答案 - 看起来你必须做一些手动工作来处理你想要的鼠标,但它也可以让你控制你滚动的平滑度/多少像素:http:// stackoverflow.com/questions/1009036/how-can-i-programmatically-scroll-a-wpf-listview (2认同)