Click here to Skip to main content
Click here to Skip to main content

WPF FlipView

, 8 Mar 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
A FlipView control for WPF, which behaves exactly as Windows Store XAML FlipView.

Introduction

FlipView control behaves like an ItemsControl and shows items one by one on swipe gesture. Also navigation buttons will be available to navigate using mouse. Many users nowadays are expecting Windows store touch apps to be developed using WPF. That doesn't require to be published on store for distribution. Same time all the features and behaviors that can be done in WinRT can also be done in WPF. So many users prefer that.

Background

So it is better to have most of the controls in WinRT to be ported to WPF. Some third party toolkits support most of the controls. But I found FlipView is missing such way. So I thought of writing a control for that behavior.

It is better to have basic understanding of the following concepts to clearly grasp this control:

  1. Custom Control Development
  2. Touch manipulation

Basic Structure

The basic structure is simple. The template of FlipView will have three containers to store current, next and previous item. On clicking navigation buttons or on swipe, the root grid which holds the entire template will animated and items will be updated based on SelectedIndex.

Below is the ControlTemplate of FlipView.

<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> 

Animation Factory

The Factory class will generate animations based on the to and from values. By default, the animation target property is set to Translation.X value, since the animation translation needs only horizontal direction. Two EasingDoubleKeyFrame will be created and added to animation. The KeyTime for the second frame is set to 500 milliseconds, which controls the speed of animation.

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;
        } 

Navigation

When user clicks the Next/Previous buttons, a storyboard will be generated using AnimationFactory and it will be started. The to and from values will be based on the SelectedIndex. RoutedUICommands are available to control the action of the Next/Previous buttons.

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

Once the control reaches the end, the Next button will be disabled. This is controlled by CanExecution callback of RoutedUICommand.

private void OnNextCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.SelectedIndex < (this.Items.Count - 1);
        } 

If there is no possibility of Navigating to next item (means it reached the end), the Next button will be collapsed. This is achieved by controlling the visibility in Triggers.

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

Touch Gestures

User can navigate items using swipe gestures. This is controlled by WPF built in manipulation APIs. On swiping the control, framework will fire the OnManipulationDelta callback. The translation values will be applied to the render transform of root container.

If user left the finger before he/she fully swiped the item, the control will animate the next item and bring it into position. For this, the control will track the position while user is swiping. This behavior is exactly equal to Windows 8 FlipView.

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;
        } 

Elastic Effect

If SelectedIndex of the control is 0, it means the first item is selected and user is not allowed to navigate to the previous item. So it is important to indicate to the user the impossibility on swiping to previous item. The control will maintain a factor variable of double value. On swiping the control, this factor will be multiplied to the OffsetX value and then applied to the control. So on no possibility of navigating to previous item, the factor will be decremented by 0.05. So user will feel a difficulty in dragging. On leaving finger, an animation will run and bring it to original position.

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 is the exact behavior of WinRT FlipView. This will clearly indicate the impossibility of control to navigate to previous item.

Data Binding

The FlipView is inherited from Selector. So all the base framework operations are possible with this control. A collection can be bound to control as we do for traditional ItemsControl. Also DataTemplate can be applied to decorate the visual.

<controls:FlipView ItemsSource="{Binding Movies}"
                           SelectedIndex="0">
            <controls:FlipView.ItemTemplate>
                <DataTemplate>
                    <Image Source="{Binding Image}"
                           Stretch="Fill"/>
                </DataTemplate>
            </controls:FlipView.ItemTemplate>
</controls:FlipView> 

Note: It is mandatory to set the SelectedIndex. Otherwise, a blank page will be shown.

Points of Interest

For any WPF application we develop to mimic exactly like Windows Store app, this control will be really useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jawahar Suresh Babu
Software Developer (Senior)
India India
Jawahar working as a Senior Development Engineer in Aditi Technologies,Bangalore, India. Specialist in all XAML frameworks. Very passionate on UX Design and Development. Skilled in Expression Blend, Design, WPF, Silverlight, Windows Phone 7/8, Windows 8. Good knowledge in Entity Framework, SQLite and SQL Server also. Also had good experience with PRISM, MVVM, Caliiburn Micro and other design patterns.
 
He developed few products for Syncfusion Inc. Also working on some freelancing projects. Worked as a lead developer of Metro Studio from Syncfusion Inc.
 
An active freelancer. http://xamlfactory.elance.com
 
http://about.me/jawahars
 
http://wpfplayground.com/
Follow on   Twitter   LinkedIn

Comments and Discussions

 
QuestionException in the DLL PinmemberMember 110578463-Sep-14 22:22 
BugTouch behavior not exactly like the Xaml FlipView Pinmemberazweepay9-Jul-14 22:10 
GeneralRe: Touch behavior not exactly like the Xaml FlipView PinmemberJawahar Suresh Babu9-Jul-14 22:27 
QuestionCan I shift those navigation link to upside PinmemberAjendra Prasad23-Jun-14 19:44 
QuestionSimilar implementation with WPF Toolkit-Wizard Page. Pinmemberleiyangge9-Mar-14 16:24 
QuestionNice! PinprofessionalVolynsky Alex9-Mar-14 15:52 
Thumbs Up | :thumbsup: Thumbs Up | :thumbsup:

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 9 Mar 2014
Article Copyright 2014 by Jawahar Suresh Babu
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid