WPF FlipView 控件开发指南

在现代的应用程序开发中,用户界面的流畅性和交互性是至关重要的。Windows Store应用中的FlipView控件因其流畅的翻页效果和直观的导航体验而受到用户的青睐。本文将介绍如何在WPF中开发一个类似的FlipView控件,以实现类似的用户体验。

尽管WPF(Windows Presentation Foundation)是.NET框架的一部分,用于构建Windows客户端应用程序,但许多开发者更倾向于使用WinRT(Windows Runtime)来开发触摸友好的应用。然而,WPF同样能够实现WinRT中的许多特性和行为,而且不需要发布到商店进行分发。因此,将WinRT中的控件移植到WPF中是一个值得考虑的选择。

基本概念

在深入探讨FlipView控件的开发之前,需要对以下概念有一个基本的了解:

基本结构

FlipView控件的基本结构相对简单。它包含三个容器,分别用于存储当前项、下一项和上一项。当用户点击导航按钮或执行滑动操作时,整个模板的根网格会进行动画效果,并且根据SelectedIndex更新项。

以下是FlipView控件的ControlTemplate示例:

<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid ClipToBounds="True" x:Name="PART_Container"> <local:FlipViewPanel x:Name="PART_Root" IsManipulationEnabled="True" Background="Transparent"> <ContentControl x:Name="PART_PreviousItem" ContentTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" /> <ContentControl x:Name="PART_NextItem" ContentTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" /> <ContentControl x:Name="PART_CurrentItem" ContentTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" /> </local:FlipViewPanel> <Grid VerticalAlignment="Center" x:Name="PART_ButtonPanel" Visibility="Collapsed"> <RepeatButton x:Name="PART_NextButton" FontFamily="Segoe UI Symbol" Content="?" FontSize="18" Style="{StaticResource NavigationButtonStyle}" Command="{x:Static local:FlipView.NextCommand}" HorizontalAlignment="Right" /> <RepeatButton x:Name="PART_PreviousButton" FontFamily="Segoe UI Symbol" Content="?" FontSize="18" Style="{StaticResource NavigationButtonStyle}" Command="{x:Static local:FlipView.PreviousCommand}" HorizontalAlignment="Left" /> </Grid> </Grid> </Border>

动画工厂

动画工厂类负责根据目标值和起始值生成动画。默认情况下,动画的目标属性设置为Translation.X值,因为动画只需要水平方向的平移。动画中会创建并添加两个EasingDoubleKeyFrame,第二个关键帧的KeyTime设置为500毫秒,以控制动画的速度。

public Storyboard GetAnimation(DependencyObject target, double to, double from) { Storyboard story = new Storyboard(); Storyboard.SetTargetProperty(story, new PropertyPath("(TextBlock.RenderTransform).(TranslateTransform.X)")); Storyboard.SetTarget(story, target); var doubleAnimation = new DoubleAnimationUsingKeyFrames(); EasingDoubleKeyFrame fromFrame = new EasingDoubleKeyFrame(from); fromFrame.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut }; fromFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); EasingDoubleKeyFrame toFrame = new EasingDoubleKeyFrame(to); toFrame.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut }; toFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)); doubleAnimation.KeyFrames.Add(fromFrame); doubleAnimation.KeyFrames.Add(toFrame); story.Children.Add(doubleAnimation); return story; }

导航

当用户点击“下一项”或“上一项”按钮时,会使用AnimationFactory生成一个故事板并启动它。目标值和起始值将基于SelectedIndex。RoutedUICommands可用于控制“下一项”/“上一项”按钮的操作。

public static RoutedUICommand NextCommand = new RoutedUICommand("Next", "Next", typeof(FlipView)); public static RoutedUICommand PreviousCommand = new RoutedUICommand("Previous", "Previous", typeof(FlipView));

一旦控件到达末尾,“下一项”按钮将被禁用。这是通过RoutedUICommand的CanExecute回调控制的。

<Trigger Property="IsEnabled" Value="false"> <Setter Property="Visibility" Value="Collapsed" /> </Trigger>

触摸手势

用户可以使用滑动手势来导航项。这由WPF内置的操作API控制。在滑动控件时,框架会触发OnManipulationDelta回调。平移值将应用于根容器的渲染变换。

private void OnRootManipulationDelta(object sender, ManipulationDeltaEventArgs e) { if (!(this.PART_Root.RenderTransform is MatrixTransform)) { this.PART_Root.RenderTransform = new MatrixTransform(); } Matrix matrix = ((MatrixTransform)this.PART_Root.RenderTransform).Matrix; var delta = e.DeltaManipulation; if ((this.SelectedIndex == 0 && delta.Translation.X > 0 && this.elasticFactor > 0) || (this.SelectedIndex == this.Items.Count - 1 && delta.Translation.X < 0 && this.elasticFactor > 0)) { this.elasticFactor -= 0.05; } matrix.Translate(delta.Translation.X * elasticFactor, 0); this.PART_Root.RenderTransform = new MatrixTransform(matrix); e.Handled = true; }

弹性效果

如果控件的SelectedIndex为0,则表示选择了第一项,用户不允许导航到上一项。因此,向用户指示无法滑动到上一项是很重要的。控件将维护一个double值的因子变量。在滑动控件时,这个因子将乘以OffsetX值,然后应用于控件。因此,在无法导航到上一项的情况下,因子将减少0.05。因此,用户将感觉到拖动的困难。在离开手指时,将运行一个动画并将其带回原始位置。

if ((this.SelectedIndex == 0 && delta.Translation.X > 0 && this.elasticFactor > 0) || (this.SelectedIndex == this.Items.Count - 1 && delta.Translation.X < 0 && this.elasticFactor > 0)) { this.elasticFactor -= 0.05; } matrix.Translate(delta.Translation.X * elasticFactor, 0);

数据绑定

FlipView控件继承自Selector,因此所有基础框架操作都可以使用这个控件。可以像传统的ItemsControl一样将集合绑定到控件。同时,DataTemplate可以应用于装饰视觉。

<controls:FlipView ItemsSource="{Binding Movies}" SelectedIndex="0"> <controls:FlipView.ItemTemplate> <DataTemplate> <Image Source="{Binding Image}" Stretch="Fill" /> </DataTemplate> </controls:FlipView.ItemTemplate> </controls:FlipView>
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485