2009-08-30 6 views
6

Tôi đang cố gắng để thích ứng với hành vi của ListBox cho nhu cầu của tôi và tôi chạy vào một số vấn đềSilverlight 3 - ListBox: làm thế nào để đạt được Scroll mượt mà và bắt MouseDown/MouseUp sự kiện

1) Làm thế nào bạn có thể thiết lập programatically vị trí di chuyển của ListBox
các ListBox không cung cấp một accessor để ScrollViewer bên trong của nó vì thế mà bạn không thể di chuyển nó đến bất cứ nơi nào bạn muốn.

2) Làm thế nào để thiết lập một cách chính xác các di chuyển dọc (nghĩa là làm thế nào để có một di chuyển mượt mà)?
Theo mặc định, không có cách nào để cuộn danh sách khác bằng cách cuộn một phần tử hoàn chỉnh cùng một lúc (Hộp danh sách sẽ luôn đảm bảo rằng phần tử đầu tiên được hiển thị hoàn toàn)

Hành vi này là ok trong hầu hết các trường hợp, nhưng không phải của tôi: Tôi muốn một chuyển động trơn tru ...),

Có một giải pháp cho vấn đề này với WPF, nhưng không phải với Silverlight (xem câu hỏi "is-it-possible-to-implement-smooth-scroll-in-a-wpf-listview").

3) Cách nắm bắt các sự kiện MouseDown và MouseUp:
Nếu bạn phân lớp ListBox, bạn có thể nắm bắt các sự kiện MouseUp và MouseMove. Tuy nhiên sự kiện MouseUp không bao giờ bị sa thải (tôi nghi ngờ nó đang được ăn bởi các phần tử con ListBox)

Trả lời

8

Tôi đã tìm thấy câu trả lời, vì vậy tôi sẽ trả lời bản thân mình.


1) Làm thế nào để làm cho cuộn ListBox suôn sẻ:
Vấn đề này đã không xảy ra trong Silverlight 2, và nó chỉ xảy ra với Silverlight 3, trong đó VirtualizedStackPanel đã được giới thiệu.
Các VirtualizedStackPanel cho phép nhanh hơn nhiều làm mới trong trường hợp danh sách khổng lồ (như chỉ các yếu tố có thể nhìn thấy được rút)

Có một workaround cho điều này (hãy cẩn thận, nó không nên được sử dụng trên danh sách khổng lồ): bạn xác định lại của ListBox ItemPanelTemplate, vì vậy mà nó sử dụng 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) Làm thế nào để programatically thay đổi vị trí di chuyển
Xem các lớp con của ListBox bên dưới: nó cung cấp một accessor để ScrollViewer nội bộ của ListBox


3) Làm thế nào để bắt MouseDown/Move/Up sự kiện trong listbox:

Tạo một lớp con của ListBox như hình dưới đây. 3 phương pháp:

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

sẽ được gọi và bạn có thể làm bất cứ điều gì bạn muốn với chúng. Có một thủ thuật tinh tế đó là phương thức OnMouseLeftButtonDown của ListBox không bao giờ được gọi là: bạn cần triển khai một lớp con của ListBoxItem, nơi bạn có thể xử lý sự kiện này.

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

    } 
    } 
}