Silverlight 3 - ListBox: как добиться плавной прокрутки и перехвата событий MouseDown / MouseUp

Я пытаюсь адаптировать поведение ListBox к своим потребностям, и у меня возникло несколько проблем.

1) Как вы можете программно установить позицию прокрутки ListBox
ListBox не предоставляет средства доступа к своему внутреннему ScrollViewer, так что вы не можете прокручивать его туда, где хотите.

2) Как точно установить вертикальную прокрутку (т.е. как добиться плавной прокрутки)?
По умолчанию нет возможности прокручивать список другим путем прокрутки одного полного элемента за раз (список всегда будет следить за тем, чтобы первый элемент показан полностью)

Такое поведение нормально в большинстве случаев, но не для меня: я хочу плавное движение ...),

Есть решение этой проблемы с помощью WPF, но не с помощью Silverlight (см. Вопрос " возможно ли реализовать гладкую прокрутку в a-wpf-listview ").

3) Как перехватить события MouseDown и MouseUp:
Если вы создадите подкласс ListBox, вы сможете перехватить события MouseUp и MouseMove. Однако событие MouseUp никогда не запускается (я подозреваю, что его съедают подэлементы ListBox)


person Pascal T.    schedule 30.08.2009    source источник


Ответы (1)


Я нашел ответ, поэтому отвечу сам.


1) Как заставить ListBox плавно прокручиваться:
Этой проблемы не было в SilverLight 2, и это происходит только с SilverLight 3, в котором была представлена ​​VirtualizedStackPanel.
VirtualizedStackPanel обеспечивает гораздо более быстрое обновление в случае огромные списки (так как рисуются только видимые элементы)

Для этого есть обходной путь (будьте осторожны, его не следует использовать в огромных списках): вы переопределяете ItemPanelTemplate ListBox, чтобы он использовал 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) Как программно изменить положение прокрутки
См. Подкласс ListBox ниже: он предоставляет средство доступа к внутреннему ScrollViewer ListBox


3) Как поймать события MouseDown / Move / Up в списке:

Создайте подкласс ListBox, как показано ниже. 3 метода:

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

будут вызваны, и вы можете делать с ними все, что захотите. Есть одна тонкая уловка, заключающаяся в том, что метод OnMouseLeftButtonDown ListBox никогда не вызывается: вам нужно реализовать подкласс ListBoxItem, в котором вы можете обрабатывать это событие.

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

    }
  }
}
person Pascal T.    schedule 30.08.2009