Silverlight 3 - ListBox:如何实现平滑滚动并捕获 MouseDown/MouseUp 事件

发布于 2024-08-03 04:36:48 字数 601 浏览 3 评论 0原文

我正在尝试根据我的需要调整 ListBox 的行为,但遇到了几个问题

1) 如何以编程方式设置 ListBox 的滚动位置
ListBox 不提供对其内部 ScrollViewer 的访问器,因此您无法将其滚动到您想要的任何位置。

2)如何准确设置垂直滚动(即如何平滑滚动)?
默认情况下,没有办法通过一次滚动一个完整的元素来滚动列表(列表框将始终确保第一个元素完全显示)

这种行为在大多数情况下都可以,但不是我的:我想要一个平滑移动...),

WPF 有一个解决此问题的方法,但 Silverlight 没有(请参阅问题 “是否可以实现-smooth-scroll-in-a-wpf-listview”)。

3)如何捕获MouseDown和MouseUp事件:
如果您子类化 ListBox ,您也许能够捕获 MouseUp 和 MouseMove 事件。但是 MouseUp 事件永远不会被触发(我怀疑它被 ListBox 子元素吃掉)

I'm trying to adapt the behaviour of the ListBox for my needs and I ran into several problems

1) How can you programatically set the scrolling position of the ListBox
The ListBox does not provide an accessor to its inner ScrollViewer so that you cannot scroll it to wherever you want.

2) How to accurately set the vertical scrolling (i.e how to have a smooth scrolling) ?
By default, there is no way to scroll the list other by scrolling one complete element at a time (the Listbox will always make sure that the first element is shown entirely)

This behaviour is ok in most cases, but not mine : I want a smooth movement...),

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

3) How to catch the MouseDown and MouseUp events :
If you subclass the ListBox , you might be able to catch the MouseUp and MouseMove events. However the MouseUp event is never fired (I suspect it is being eaten by the ListBox subelements)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

白馒头 2024-08-10 04:36:48

我已经找到了答案,所以我自己来回答。


1)如何让ListBox平滑滚动:
这个问题在 SilverLight 2 中没有出现,只有在引入了 VirtualizedStackPanel 的 SilverLight 3 中才会出现。
VirtualizedStackPanel 在巨大列表的情况下可以实现更快的刷新(因为仅绘制可见元素)

对此有一个解决方法(注意,它不应该在巨大列表上使用):您重新定义 ListBox 的 ItemPanelTemplate,以便它使用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 子类:它提供了对 ListBox 的内部 ScrollViewer 的访问器


3) 如何捕获列表框中的 MouseDown / Move / Up 事件:

创建 ListBox 的子类,如下所示。这 3 个方法:

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

将被调用,你可以用它们做任何你想做的事情。有一个微妙的技巧,即 ListBox 的 OnMouseLeftButtonDown 方法永远不会被调用:您需要实现 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);
      }

    }
  }
}

I have found the answer, so I will answer myself.


1) How to make the ListBox scroll smoothly :
This problem did not happen in SilverLight 2, and it happens only with SilverLight 3, in which the VirtualizedStackPanel was introduced.
The VirtualizedStackPanel enables much faster refresh in the case of huge lists (as only the visible elements are drawn)

There is a workaround for this (beware, it should not be used on huge lists) : you redefine the ListBox's ItemPanelTemplate, so that it uses 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 programatically change the scrolling position
See the subclass of ListBox below : it provides an accessor to the internal ScrollViewer of the ListBox


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

Create a subclass of ListBox as shown below. The 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 which is that the OnMouseLeftButtonDown method of ListBox is never called : you need to implement a subclass of ListBoxItem 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);
      }

    }
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文