Silverlight 3 - ListBox: cómo lograr desplazamiento suave y atrapar eventos MouseDown / MouseUp

StackOverflow https://stackoverflow.com/questions/1354708

Pregunta

Estoy intentando adaptar el comportamiento del cuadro de lista para mis necesidades y me encontré con varios problemas

1) ¿Cómo se puede establecer mediante programación la posición de desplazamiento del cuadro de lista
El cuadro de lista no proporciona un descriptor de acceso a su ScrollViewer interior de manera que no puede desplazarse a cualquier lugar que desee.

2) Cómo establecer con precisión el desplazamiento vertical (es decir, cómo tener un desplazamiento suave)?
Por defecto, no hay manera de desplazarse por la lista otra desplazándose un elemento completo a la vez (el cuadro de lista siempre se asegurará de que el primer elemento se muestra en su totalidad)

Este comportamiento es aceptable en la mayoría de los casos, pero no la mía: quiero un movimiento suave ...),

Hay una solución a este problema con WPF, pero no con Silverlight (véase la pregunta " es-it-posible de implementar-liso-scroll-in-a--listview WPF ").

3) ¿Cómo capturar los eventos MouseDown y MouseUp:
Si subclase el cuadro de lista, que puede ser capaz de capturar los eventos MouseUp y MouseMove. Sin embargo, el evento MouseUp no se dispara (sospecho que está siendo comido por los sub-elementos ListBox)

¿Fue útil?

Solución

He encontrado la respuesta, así que voy a contestar yo mismo.


1) ¿Cómo hacer que el desplazamiento ListBox sin problemas:
Este problema no ocurre en Silverlight 2, y ocurre solamente con Silverlight 3, en el que se introdujo el VirtualizedStackPanel.
El VirtualizedStackPanel mucho más rápido permite actualizar en el caso de las listas enormes (ya que sólo los elementos visibles se dibujan)

Hay una solución para esto (ten cuidado, que no debe ser utilizado en grandes listas): se redefine ItemPanelTemplate del cuadro de lista, de modo que utilice 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) ¿Cómo cambiar mediante programación la posición de desplazamiento
Ver la subclase de ListBox a continuación: se proporciona un descriptor de acceso a la ScrollViewer interna de la ListBox


3) ¿Cómo capturar los eventos MouseDown / Mover / Hasta en el cuadro de lista:

Crea una subclase de ListBox como se muestra a continuación. Los 3 métodos:

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

serán llamados y se puede hacer lo que quiera con ellos. Hay un truco sutil, que es que el método de OnMouseLeftButtonDown ListBox nunca es llamado:. Es necesario implementar una subclase de ListBoxItem donde se puede controlar este evento

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

    }
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top