65.9K
CodeProject 正在变化。 阅读更多。
Home

SlidingListBox –WPF 中 ListBoxItems 的动画

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (23投票s)

2006年10月19日

CPOL

4分钟阅读

viewsIcon

149753

downloadIcon

5244

一个 ListBox,在选择和取消选择时会滑动其项。

Sample Image - SlidingListBox.png

引言

本文介绍了一个 ListBox 派生控件,它会在项被选中和取消选中时滑动它们。SlidingListBox 允许您自定义项的动画滑动,以便在应用程序中使其“感觉恰到好处”。SlidingListBox 提供的效果有助于使用户界面对用户更加直观和交互,这总是件好事。

本文提供的代码已在 .NET Framework 3.0 的 RC1 版本上编译和测试。

背景

WPF 的 ListBox 控件非常灵活,并且具有远超标准 WinForms ListBox 的功能。本文不讨论 WPF ListBox 的基础知识,但如果您想了解更多关于它的信息,我推荐 Mike Hillberg 的精彩博文“Brief Anatomy of a ListBox”。

关于默认 ListBox ItemTemplate 有一个重要的事情需要知道,这对本文很重要。默认情况下,每个项的视觉树将包含一个 Border,其中包含一个 ContentPresenterContentPresenter 包含与项底层数据对象相关的视觉元素。ContentPresenterBorder 包裹这一事实对 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 的存在,因为它通过动画化 BorderPadding 属性来实现滑动效果。

例如,假设有一个 SlidingListBox,当其项被选中时会向右滑动 50 个逻辑像素。当 SlidingListBox 中的一个项被选中时,SlidingListBox 会找到该项视觉树中的 Border。然后,它会动画化 BorderPadding 属性,使 Padding.Left 增加 50。当该 ListBoxItem 随后被取消选中时,BorderPadding 会动画回到其原始状态。

要实现滑动动画,涉及的代码并不多。以下两个方法是 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 中的 BorderPadding.Left 设置为 2。创建动画中使用的 Thickness 的代码通过始终确保 ThicknessLeft 至少为 2 来解决此问题。

欢迎在您的 WPF 应用程序中使用 SlidingListBox。我只要求保留 SlidingListBox.cs 文件顶部的版权声明。如果您想到任何很酷的新功能或(天哪)发现任何错误,请告诉我。

© . All rights reserved.