SlidingListBox –WPF 中 ListBoxItems 的动画






4.91/5 (23投票s)
一个 ListBox,在选择和取消选择时会滑动其项。
引言
本文介绍了一个 ListBox
派生控件,它会在项被选中和取消选中时滑动它们。SlidingListBox
允许您自定义项的动画滑动,以便在应用程序中使其“感觉恰到好处”。SlidingListBox
提供的效果有助于使用户界面对用户更加直观和交互,这总是件好事。
本文提供的代码已在 .NET Framework 3.0 的 RC1 版本上编译和测试。
背景
WPF 的 ListBox
控件非常灵活,并且具有远超标准 WinForms ListBox
的功能。本文不讨论 WPF ListBox
的基础知识,但如果您想了解更多关于它的信息,我推荐 Mike Hillberg 的精彩博文“Brief Anatomy of a ListBox”。
关于默认 ListBox
ItemTemplate
有一个重要的事情需要知道,这对本文很重要。默认情况下,每个项的视觉树将包含一个 Border
,其中包含一个 ContentPresenter
。ContentPresenter
包含与项底层数据对象相关的视觉元素。ContentPresenter
被 Border
包裹这一事实对 SlidingListBox
的内部工作机制非常重要,后面我们会看到。
使用 SlidingListBox
SlidingListBox
的用法与常规 ListBox
相同,只是您还可以另外调整三个属性。下面是一些创建和配置该控件的 XAML:
<jas:SlidingListBox
HorizontalContentAlignment="Right"
SlideDirection="Left"
SlideDistance="50"
SlideDuration="250"
/>
上面的 XAML 创建了一个 SlidingListBox
,其项右对齐到控件的右侧,当项被选中时,它会在 250 毫秒内向左滑动五十个逻辑像素。上面的代码片段显示了 SlidingListBox
类声明的三个依赖属性:
SlideDirection
– 此属性允许您指定选中项应滑动的方向。您可以选择“Up”(向上)、“Down”(向下)、“Right”(向右)或“Left”(向左)。如果控件的ItemsPanel
设置为水平排列项的面板,您可能只会使用“Up”或“Down”选项。默认值为“Right”。SlideDistance
– 此属性允许您指定选中项滑动的正逻辑像素数。默认值为 20.0。SlideDuration
– 此属性允许您指定一个正毫秒数,该数字决定了滑动动画应持续多长时间。默认值为 200。
本文顶部的演示应用程序可以下载,其中展示了这些属性的各种值如何影响滑动动画的行为。如果您计划在应用程序中使用 SlidingListBox
,可以在该演示中尝试调整属性值,以了解您喜欢如何配置该控件。
工作原理
正如我在本文的背景部分提到的,默认的 ListBox ItemTemplate
将项的视觉树包裹在一个 Border
中。SlidingListBox
依赖于该 Border
的存在,因为它通过动画化 Border
的 Padding
属性来实现滑动效果。
例如,假设有一个 SlidingListBox
,当其项被选中时会向右滑动 50 个逻辑像素。当 SlidingListBox
中的一个项被选中时,SlidingListBox
会找到该项视觉树中的 Border
。然后,它会动画化 Border
的 Padding
属性,使 Padding.Left
增加 50。当该 ListBoxItem
随后被取消选中时,Border
的 Padding
会动画回到其原始状态。
要实现滑动动画,涉及的代码并不多。以下两个方法是 SlidingListBox
类中的成员:
protected override void OnSelectionChanged( SelectionChangedEventArgs e )
{
base.OnSelectionChanged( e );
ItemContainerGenerator generator = this.ItemContainerGenerator;
if( generator.Status != GeneratorStatus.ContainersGenerated )
return;
for( int i = 0; i < this.Items.Count; ++i )
{
ListBoxItem item = generator.ContainerFromIndex( i ) as ListBoxItem;
if( VisualTreeHelper.GetChildrenCount( item ) == 0 )
continue;
Border rootBorder = VisualTreeHelper.GetChild( item, 0 ) as Border;
if( rootBorder == null )
continue;
this.AnimateItem( item, rootBorder );
}
}
void AnimateItem( ListBoxItem item, Border rootBorder )
{
// The default Left of a ListBoxItem's root Border's Padding
// is 2, so the animation logic ensures that the Padding's Left
// is always at 2 or the "slide distance".
Thickness thickness;
if( item.IsSelected )
{
ListBoxItemSlideDirection direction = this.SlideDirection;
if( direction == ListBoxItemSlideDirection.Up )
thickness = new Thickness( 2, 0, 0, this.SlideDistance );
else if( direction == ListBoxItemSlideDirection.Right )
thickness = new Thickness( 2 + this.SlideDistance, 0, 0, 0 );
else if( direction == ListBoxItemSlideDirection.Down )
thickness = new Thickness( 2, this.SlideDistance, 0, 0 );
else
thickness = new Thickness( 2, 0, this.SlideDistance, 0 );
}
else
{
thickness = new Thickness( 2, 0, 0, 0 );
}
TimeSpan timeSpan = TimeSpan.FromMilliseconds( this.SlideDuration );
Duration duration = new Duration( timeSpan );
ThicknessAnimation anim = new ThicknessAnimation( thickness, duration );
rootBorder.BeginAnimation( Border.PaddingProperty, anim );
}
滑动动画代码必须处理的一个怪癖是,ListBoxItem
默认 ItemTemplate
中的 Border
的 Padding.Left
设置为 2。创建动画中使用的 Thickness
的代码通过始终确保 Thickness
的 Left
至少为 2 来解决此问题。
欢迎在您的 WPF 应用程序中使用 SlidingListBox
。我只要求保留 SlidingListBox.cs 文件顶部的版权声明。如果您想到任何很酷的新功能或(天哪)发现任何错误,请告诉我。