2009-08-30 8 views
6

Je suis en train d'adapter le comportement du ListBox pour mes besoins et je suis tombé sur plusieurs problèmesSilverlight 3 - ListBox: comment atteindre Scroll lisse et attraper les événements MouseDown/MouseUp

1) Comment pouvez-vous programatically fixés la position de défilement du ListBox
Le ListBox ne fournit pas un accesseur à son ScrollViewer interne de sorte que vous ne pouvez pas le faire défiler à l'endroit où vous voulez.

2) Comment régler avec précision le défilement vertical (c'est-à-dire comment avoir un défilement régulier)?
Par défaut, il n'y a pas moyen de faire défiler la liste d'autres en faisant défiler un élément complet à la fois (le Listbox toujours faire en sorte que le premier élément est représenté entièrement)

Ce comportement est correct dans la plupart des cas, mais pas le mien: je veux un mouvement fluide ...),

Il existe une solution à ce problème avec WPF, mais pas avec Silverlight (voir question "is-it-possible-to-implement-smooth-scroll-in-a-wpf-listview").

3) Comment attraper les événements MouseDown et MouseUp:
Si vous sous-classez la ListBox, vous pourrez peut-être attraper les événements MouseUp et MouseMove. Cependant l'événement MouseUp n'est jamais tiré (je soupçonne qu'il est mangé par les sous-éléments de ListBox)

Répondre

8

J'ai trouvé la réponse, donc je me répondrai.


1) Comment faire défiler ListBox en douceur:
Ce problème ne se produisait dans SilverLight 2, et il se produit uniquement avec SilverLight 3, dans lequel le VirtualizedStackPanel a été introduit.
Le VirtualizedStackPanel permet beaucoup rafraîchir plus rapidement dans le cas des listes énormes (car seuls les éléments visibles sont tirés)

Il existe une solution pour cela (attention, il ne doit pas être utilisé sur les listes énormes): vous redéfinissez la zone de liste de ItemPanelTemplate, de sorte qu'il utilise 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) Comment changer programatically la position de défilement
Voir la sous-classe de ListBox ci-dessous: il fournit un accesseur ScrollViewer interne de la zone de liste


3) Comment attraper le MouseDown/Déplacer/Up événements dans la liste:

Créer une sous-classe de ListBox comme indiqué ci-dessous. Les 3 méthodes:

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

sera appelée et vous pouvez faire ce que vous voulez avec eux. Il y a un truc subtil qui est que la méthode OnMouseLeftButtonDown de ListBox n'est jamais appelée: vous devez implémenter une sous-classe de ListBoxItem où vous pouvez gérer cet événement.

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

    } 
    } 
}