Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to implement smooth scroll in a WPF listview?

Is it possible to implement smooth scroll in a WPF listview like how it works in Firefox?
When the Firefox browser contained all listview items and you hold down the middle mouse button (but not release), and drag it, it should smoothly scroll the listview items. When you release it should stop.

It looks like this is not possible in winforms, but I am wondering if it is available in WPF?

like image 816
Joan Venge Avatar asked Jun 23 '09 16:06

Joan Venge


People also ask

How do I enable smooth scrolling?

In the address bar, type chrome://flags and press enter. In the Flags tab, search for the Smooth Scrolling setting. You can do this manually in the Available tab or use the search bar to find it. Once you've located it, press the drop-down menu next to the feature and select Enabled or Disabled.

How do I use smooth scroll plugin?

Search for smooth scroll and then install the MouseWheel Smooth Scroll plugin. When the plugin is done installing, activate it. To set up the plugin, you will need to go to Settings > Smooth Scroll. On this page, you can configure how fast the animation is and how far down the page the smooth scroll will go.

What is a scroll viewer WPF?

There are two predefined elements that enable scrolling in WPF applications: ScrollBar and ScrollViewer. The ScrollViewer control encapsulates horizontal and vertical ScrollBar elements and a content container (such as a Panel element) in order to display other visible elements in a scrollable area.

Should I enable smooth scrolling?

With smooth scrolling, it slides down smoothly, so you can see how much it scrolls. This might not be a huge deal for you but it is a big deal for users who read a lot of long pages. The choppy scroll might be annoying for a lot of users and that's why people are moving towards the smooth scroll option.


2 Answers

You can achieve smooth scrolling but you lose item virtualisation, so basically you should use this technique only if you have few elements in the list:

Info here: Smooth scrolling on listbox

Have you tried setting:

ScrollViewer.CanContentScroll="False" 

on the list box?

This way the scrolling is handled by the panel rather than the listBox... You lose virtualisation if you do that though so it could be slower if you have a lot of content.

like image 77
Pop Catalin Avatar answered Oct 06 '22 00:10

Pop Catalin


It is indeed possible to do what you're asking, though it will require a fair amount of custom code.

Normally in WPF a ScrollViewer uses what is known as Logical Scrolling, which means it's going to scroll item by item instead of by an offset amount. The other answers cover some of the ways you can change the Logical Scrolling behavior into that of Physical Scrolling. The other way is to make use of the ScrollToVertialOffset and ScrollToHorizontalOffset methods exposed by both ScrollViwer and IScrollInfo.

To implement the larger part, the scrolling when the mouse wheel is pressed, we will need to make use of the MouseDown and MouseMove events.

<ListView x:Name="uiListView"           Mouse.MouseDown="OnListViewMouseDown"           Mouse.MouseMove="OnListViewMouseMove"           ScrollViewer.CanContentScroll="False">     .... </ListView> 

In the MouseDown, we are going to record the current mouse position, which we will use as a relative point to determine which direction we scroll in. In the mouse move, we are going to get the ScrollViwer component of the ListView and then Scroll it accordingly.

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; } 

There's some areas it's lacking as it's just a proof of concept but it should definitely get you started in the right direction. To have it constantly scroll once the mouse is moved away from the initial MouseDown point, the scrolling logic could go into a DispatcherTimer or something similar.

like image 35
rmoore Avatar answered Oct 05 '22 23:10

rmoore