Silverlight 3 - リストボックス:スムーズ スクロールを実現し、MouseDown/MouseUp イベントをキャッチする方法
-
20-09-2019 - |
質問
ListBox の動作を自分のニーズに合わせて調整しようとしているのですが、いくつかの問題が発生しました
1) ListBox のスクロール位置をプログラムで設定するにはどうすればよいですか
ListBox は内部の ScrollViewer へのアクセサーを提供していないため、希望する場所までスクロールできません。
2) 垂直スクロールを正確に設定するにはどうすればよいですか (つまり、スムーズなスクロールを実現するにはどうすればよいですか)?
デフォルトでは、一度に 1 つの完全な要素をスクロールする以外にリストをスクロールする方法はありません (リストボックスは常に最初の要素が完全に表示されるようにします)。
ほとんどの場合、この動作は問題ありませんが、私の場合はそうではありません。スムーズな動きをしたいのですが…)、
WPF にはこの問題の解決策がありますが、Silverlight にはありません (質問を参照) "wpf-listview でスムーズなスクロールを実装することは可能ですか" ).
3) MouseDown イベントと MouseUp イベントをキャッチする方法:
ListBox をサブクラス化すると、MouseUp イベントと MouseMove イベントをキャッチできる可能性があります。ただし、MouseUp イベントは決して発生しません (ListBox サブ要素によって食べられているのではないかと思われます)
解決
答えが見つかったので、自分なりに答えてみます。
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 メソッドが決して呼び出されないという微妙なトリックが 1 つあります。このイベントを処理できる 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);
}
}
}
}