Silverlight 3 - ListBox: How to Get Smooth Scrolling and Capture MouseDown / MouseUp Events

I am trying to adapt the behavior of a ListBox to my needs, and I ran into several problems.

1) How can you programmatically set the scroll position of a ListBox
The ListBox does not provide an accessory for the internal ScrollViewer, so you cannot scroll where you want it.

2) How to set vertical scrolling exactly (for example, how to have smooth scrolling)?
By default, it is not possible to scroll the list to others by scrolling one full item at a time (Listbox will always make sure that the first item is shown in full)

In most cases, this is normal, but not mine: I want a smooth movement ...),

There is a solution to this problem with WPF, but not with Silverlight (see the question "is-it-possible-to-implement-smooth-scroll-in-a-wpf -listview" ).

3) How to catch MouseDown and MouseUp events:
If you subclass the ListBox, you can catch the MouseUp and MouseMove events. However, the MouseUp event never fires (I suspect the ListBox subelements are eating it)

+6
silverlight listbox smooth-scrolling
source share
1 answer

I found the answer, so I will answer myself.


1) How to make ListBox scroll smoothly:
This issue did not occur in SilverLight 2, and it only happens with SilverLight 3, in which VirtualizedStackPanel was introduced.
VirtualizedStackPanel provides much faster updates in case of huge lists (since only visible items are displayed)

There is a workaround for this (beware, it cannot be used in huge lists): you redefine the ListBox ItemPanelTemplate, so it uses a StackPanel:

<navigation:Page.Resources> <ItemsPanelTemplate x:Key="ItemsPanelTemplate"> <StackPanel/> </ItemsPanelTemplate> </navigation:Page.Resources> <StackPanel Orientation="Vertical" x:Name="LayoutRoot"> <ListBox x:Name="list" ItemsPanel="{StaticResource ItemsPanelTemplate}"> </ListBox> </StackPanel> 

2) How to programmatically change the scroll position
See ListBox Subclass below: it provides an accessory for an internal ScrollViewer in a ListBox


3) How to catch MouseDown / Move / Up events in the list:

Subclass the ListBox as shown below. 3 methods:

  internal void MyOnMouseLeftButtonDown(MouseButtonEventArgs e) protected override void OnMouseMove(MouseEventArgs e) protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) 

will be called, and you can do whatever you want with them. There is one subtle trick in that the OnMouseLeftButtonDown ListBox method is never called: you need to implement a ListBoxItem subclass where you can handle this event.

 using System; using System.Collections.Generic; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace MyControls { //In order for this class to be usable as a control, you need to create a folder //named "generic" in your project, and a "generic.xaml" file in this folder //(this is where you can edit the default look of your controls) // /* * Typical content of an "empty" generic.xaml file : <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:VideoControls"> </ResourceDictionary> */ public class MyListBox : ListBox { public MyListBox() { DefaultStyleKey = typeof(ListBox); } public override void OnApplyTemplate() { base.OnApplyTemplate(); } #region ScrollViewer / unlocking access related code private ScrollViewer _scrollHost; public ScrollViewer ScrollViewer { get { if (_scrollHost == null) _scrollHost = FindVisualChildOfType<ScrollViewer>(this); return _scrollHost; } } public static childItemType FindVisualChildOfType<childItemType>(DependencyObject obj) where childItemType : DependencyObject { // Search immediate children first (breadth-first) for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is childItemType) return (childItemType)child; else { childItemType childOfChild = FindVisualChildOfType<childItemType>(child); if (childOfChild != null) return childOfChild; } } return null; } #endregion //Modify MyListBox so that it uses MyListBoxItem instead of ListBoxItem protected override DependencyObject GetContainerForItemOverride() { MyListBoxItem item = new MyListBoxItem(this); if (base.ItemContainerStyle != null) { item.Style = base.ItemContainerStyle; } return item; } //OnMouseLeftButtonUp is never reached, since it is eaten by the Items in the list... /* protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); e.Handled = false; } */ internal void MyOnMouseLeftButtonDown(MouseButtonEventArgs e) { } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); } } public class MyListBoxItem : ListBoxItem { MyListBox _customListBoxContainer; public MyListBoxItem() { } public MyListBoxItem(MyListBox customListBox) { this._customListBoxContainer = customListBox; } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); if (this._customListBoxContainer != null) { this._customListBoxContainer.MyOnMouseLeftButtonDown(e); } } } } 
+8
source share

All Articles