5

I am making an article reading app (similar to the Bing News app) and I'm using a FlipView to go between the articles. The FlipView has its ItemsSource databound to an ObservableCollection<T> that holds the content of the article.

I only want to keep 3 articles in the ObservableCollection<T> for memory and performance reasons, so I subscribe to the flipView_SelectionChanged event and remove the item at Length - 1 for going back (to the right) and item at 0 for going forward (to the left)

The problem that I'm having is that when I remove the item at 0 after flipping using the touch gesture, the animation plays a second time.

How do I prevent the transition animation from playing a second time?

Here is an example. Create a new Blank Store App, and add the following:

public sealed partial class MainPage : Page
{
    public ObservableCollection<int> Items { get; set; }

    private Random _random = new Random(123);

    public MainPage()
    {
        this.InitializeComponent();

        Items = new ObservableCollection<int>();

        Items.Add(1);
        Items.Add(1);
        Items.Add(1);

        flipview.ItemsSource = Items;
    }

    private void flipview_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (this.flipview.SelectedIndex == 0)
        {
            Items.Insert(0, 1);
            Items.RemoveAt(Items.Count - 1);
        }
        else if (this.flipview.SelectedIndex == this.flipview.Items.Count - 1)
        {
            Items.Add(1);
            Items.RemoveAt(0);
        }
    }
}

and this in the .xaml

<Page.Resources>
    <DataTemplate x:Key="DataTemplate">
        <Grid>
            <Grid.Background>
                <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                    <GradientStop Color="Black"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
        </Grid>
    </DataTemplate>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <FlipView x:Name="flipview"
        SelectionChanged="flipview_SelectionChanged"
        ItemsSource="{Binding}" ItemTemplate="{StaticResource DataTemplate}"/>
</Grid>
joe_coolish
  • 7,201
  • 13
  • 64
  • 111
  • You shouldn't need such an elaborate setup. FlipView uses virtualization so it would not instantiate more article views then needed. Just put all your articles into collection and let FlipView do its magic. – Denis Dec 02 '13 at 21:58
  • If you keep the default template, yes FlipView does indeed do all kinds of magic. BUT! The virtualizing StackPanel breaks some of the things I am working with, so I needed to take it out. Hence, the need to manage the item length myself. Either way, this isn't an elaborate setup. I'm just trying to remove an item from the FlipView's databound list without the flip animation playing! – joe_coolish Dec 03 '13 at 14:37

1 Answers1

4

The easiest solution is to use UseTouchAnimationsForAllNavigation so that items manipulation in the view model does not cause the animation to occur at all.

<FlipView UseTouchAnimationsForAllNavigation="False" />

If the animation is important to you, then you can simply bind the value for UseTouchAnimationsForAllNavigation in your view model, like this:

<FlipView UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}" />

Does this make sense? If you do it in your view model (option 2) you can simply disable it while manipulating the collection, and then re-enable it so you get animations.

Here's a sample.

Using this code:

public class MyColorModel : BindableBase
{
    Color _Color = default(Color);
    public Color Color { get { return _Color; } set { SetProperty(ref _Color, value); } }

    Visibility _Selected = Visibility.Collapsed;
    public Visibility Selected { get { return _Selected; } set { SetProperty(ref _Selected, value); } }

    public event EventHandler RemoveRequested;
    public void RemoveMe()
    {
        if (RemoveRequested != null)
            RemoveRequested(this, EventArgs.Empty);
    }

    public event EventHandler SelectRequested;
    public void SelectMe()
    {
        if (SelectRequested != null)
            SelectRequested(this, EventArgs.Empty);
    }
}

public class MyViewModel : BindableBase
{
    public MyViewModel()
    {
        this.Selected = this.Colors[1];
        foreach (var item in this.Colors)
        {
            item.RemoveRequested += (s, e) =>
            {
                this.ShowAnimations = false;
                this.Colors.Remove(s as MyColorModel);
                this.ShowAnimations = true;
            };
            item.SelectRequested += (s, e) => this.Selected = s as MyColorModel;
        }
    }

    ObservableCollection<MyColorModel> _Colors = new ObservableCollection<MyColorModel>(new[]
    {
      new MyColorModel{ Color=Windows.UI.Colors.Red }, 
      new MyColorModel{ Color=Windows.UI.Colors.Green }, 
      new MyColorModel{ Color=Windows.UI.Colors.Yellow }, 
      new MyColorModel{ Color=Windows.UI.Colors.Blue }, 
      new MyColorModel{ Color=Windows.UI.Colors.White }, 
      new MyColorModel{ Color=Windows.UI.Colors.Brown }, 
      new MyColorModel{ Color=Windows.UI.Colors.SteelBlue }, 
      new MyColorModel{ Color=Windows.UI.Colors.Goldenrod },
    });
    public ObservableCollection<MyColorModel> Colors { get { return _Colors; } }

    MyColorModel _Selected = default(MyColorModel);
    public MyColorModel Selected
    {
        get { return _Selected; }
        set
        {
            if (_Selected != null)
                _Selected.Selected = Visibility.Collapsed;
            value.Selected = Visibility.Visible;
            SetProperty(ref _Selected, value);
        }
    }

    bool _ShowAnimations = true;
    public bool ShowAnimations { get { return _ShowAnimations; } set { SetProperty(ref _ShowAnimations, value); } }
}

public abstract class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (!object.Equals(storage, value))
        {
            storage = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Try this XAML:

<Page.DataContext>
    <local:MyViewModel/>
</Page.DataContext>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="150" />
    </Grid.RowDefinitions>
    <FlipView ItemsSource="{Binding Colors}" SelectedItem="{Binding Selected, Mode=TwoWay}" UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}">
        <FlipView.ItemTemplate>
            <DataTemplate>
                <Rectangle>
                    <Rectangle.Fill>
                        <SolidColorBrush Color="{Binding Color}" />
                    </Rectangle.Fill>
                </Rectangle>
            </DataTemplate>
        </FlipView.ItemTemplate>
    </FlipView>
    <ItemsControl ItemsSource="{Binding Colors}" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <Grid>
                        <Rectangle Height="100" Width="100" Margin="10">
                            <Rectangle.Fill>
                                <SolidColorBrush Color="{Binding Color}" />
                            </Rectangle.Fill>
                            <Interactivity:Interaction.Behaviors>
                                <Core:EventTriggerBehavior EventName="PointerPressed">
                                    <Core:CallMethodAction MethodName="SelectMe" TargetObject="{Binding}"/>
                                </Core:EventTriggerBehavior>
                            </Interactivity:Interaction.Behaviors>
                        </Rectangle>
                        <TextBlock Text="X" VerticalAlignment="Top" HorizontalAlignment="Right" FontSize="50" Margin="20,10" Foreground="Wheat">
                            <Interactivity:Interaction.Behaviors>
                                <Core:EventTriggerBehavior EventName="PointerPressed">
                                    <Core:CallMethodAction MethodName="RemoveMe" TargetObject="{Binding}"/>
                                </Core:EventTriggerBehavior>
                            </Interactivity:Interaction.Behaviors>
                        </TextBlock>
                    </Grid>
                    <Rectangle Height="10" Width="100" Margin="10" 
                               Fill="White" Visibility="{Binding Selected}" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Will look like this:

enter image description here

Best of luck!

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • Thank you for your help! This has worked very well1 – joe_coolish Dec 05 '13 at 00:46
  • I am trying to implement a similar code, but if I disable `UseTouchAnimationsForAllNavigation` right before the remove action and enable it after, the current animation will also stop (I want to remove an item from flipview when the user change pages). Is there anything I can do? – JBernardo Nov 30 '15 at 12:40