1

I'm trying to implement the collapsible page header. The main idea is I have to change the height of page header (min height 60 and max is 160) depending on the changes in the scrollviewer.

Here is the result I want to achieve.

Initial state of the page header

Main layout

Page header when scrolled down

Main layout when scrolled down

My XAML code:

    <controls:CollapsablePageHeader>
        <controls:CollapsablePageHeader>
            <Grid x:Name="CollapsablePageHeaderGrid" MinHeight="150" Background="{StaticResource ApplicationAccentBackgroundColorBrush}">
                <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="25 905" HorizontalAlignment="Center" FontSize="50" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                        <controls:TengeSignControl FontSize="50" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="/Assets/CreditCard.png" Height="20" Width="20" Margin="0,5,5,0"/>
                        <TextBlock Text="Bank of Arkham City" FontSize="{StaticResource BigTextSize}" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </controls:CollapsablePageHeader>
    </controls:CollapsablePageHeader>
    <ScrollViewer x:Name="MainScrollViewer" Grid.Row="1" IsTabStop="False" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <!--Layout for user favorite payment templates-->
            <toolkitControls:DropShadowPanel Style="{StaticResource DefaultCardDropShadowEffect}">
                <Grid Style="{StaticResource CardConontrolElement}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <TextBlock x:Uid="Favorites" Style="{StaticResource SectionHeaderTextBlock}" HorizontalAlignment="Left" />
                    <GridView x:Name="UserTemplatesSelector" Grid.Row="1" ItemsSource="{Binding PaymentTemplates}" ItemTemplate="{StaticResource UserTemplatesDataTemplate}" Margin="-10,0,-10,0" Padding="10,0,0,0" Style="{StaticResource DefaultHorizontalGridView}" ItemContainerStyle="{StaticResource DefaultHorizontalGridViewItemContainer}">
                        <GridView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal"/>
                            </ItemsPanelTemplate>
                        </GridView.ItemsPanel>
                    </GridView>
                </Grid>
            </toolkitControls:DropShadowPanel>
            <!-- Layout for last operations -->
            <toolkitControls:DropShadowPanel Grid.Row="1" Margin="0,20,0,0" Style="{StaticResource DefaultCardDropShadowEffect}">
                <Grid Style="{StaticResource CardConontrolElement}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <TextBlock x:Uid="LastOperations" Style="{StaticResource SectionHeaderTextBlock}"/>
                    <ListView x:Name="LastOperationsListView" Grid.Row="1" ItemsSource="{Binding LastTransactions}" ItemTemplate="{StaticResource LastOperationsDataTemplate}" Margin="0,10,0,0" ItemContainerStyle="{StaticResource DefaultListViewItemContainer}"/>
                    <Button x:Name="ShowMoreOperationsButton" Content="Показать всё" Grid.Row="2" Style="{StaticResource TiltableAccentButton}"/>
                </Grid>
            </toolkitControls:DropShadowPanel>
            <!-- Layour for bound cards -->
            <toolkitControls:DropShadowPanel Grid.Row="2" Margin="0,20" Style="{StaticResource DefaultCardDropShadowEffect}">
                <Grid Style="{StaticResource CardConontrolElement}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <TextBlock x:Uid="LastOperations" Style="{StaticResource SectionHeaderTextBlock}" Typography.Capitals="AllSmallCaps"/>
                    <ListView x:Name="BoundCardListView" Grid.Row="1" ItemsSource="{Binding BoundCards}" ItemTemplate="{StaticResource BoundCardsDataTemplate}" Margin="0,10,0,0" ItemContainerStyle="{StaticResource DefaultListViewItemContainer}"/>
                    <Button x:Name="ShowMoreCardsButton" Content="Показать всё" Grid.Row="2" Style="{StaticResource TiltableAccentButton}"/>
                </Grid>
            </toolkitControls:DropShadowPanel>
        </Grid>
    </ScrollViewer>`

I have tried to implement this using the Compositor.CreateExpressionAnimation but it did not work well. Could anyone please tell me how to do this?

UPDATE:

Throws an exception when try to animate the Scale.X or Scale.Y value, but I cannot figure out why. Other Visual items work excellent with Scale animation.

        ExpressionNode headerTranslationAnimation = EF.Conditional(progressNode < 1, 0, -scrollingProperties.Translation.Y - clampSizeNode);
        headerVisual.StartAnimation("Offset.Y", headerTranslationAnimation);

        ExpressionNode headerScaleAnimation = EF.Lerp(1, 1.25f, EF.Clamp(scrollingProperties.Translation.Y / 50, 0, 1));
        headerVisual.StartAnimation("Scale.X", headerScaleAnimation);
        headerVisual.StartAnimation("Scale.Y", headerScaleAnimation);

Exception at headerVisual.StartAnimation:

System.ArgumentException: 'The parameter is incorrect.The animation failed to connect.'

Updated xaml:

 <Grid x:Name="LayoutRoot" Style="{StaticResource DefaultLayoutRootStyle}">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <ScrollViewer x:Name="MainScrollViewer" IsTabStop="False" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Hidden">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <Grid x:Name="CollapsiblePageHeader" Height="150" VerticalAlignment="Top">
                <ScrollViewer>
                    <Grid>
                        <Rectangle Height="300" HorizontalAlignment="Left" VerticalAlignment="Top">
                            <Rectangle.Fill>
                                <ImageBrush ImageSource="ms-appx:///Assets/Samples/SampleParallaxImage.jpg"/>
                            </Rectangle.Fill>
                        </Rectangle>
                        <StackPanel>
                            <StackPanel x:Name="TitleContainer" VerticalAlignment="Top" HorizontalAlignment="Center" Orientation="Horizontal">
                                <TextBlock x:Name="TitleText" Text="25 905" FontSize="50" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                                <controls:TengeSignControl x:Name="TitleImage" FontSize="50" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                            </StackPanel>
                            <StackPanel x:Name="SubtitleContainer" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="1" Orientation="Horizontal">
                                <Image x:Name="SubtitleImage" Source="/Assets/CreditCard.png" Height="20" Width="20" Margin="0,5,5,0"/>
                                <TextBlock x:Name="SubtitleText" Text="Bank of Arkham City" FontSize="{StaticResource BigTextSize}" Foreground="{StaticResource ApplicationAlternativeAccentColorBrush}"/>
                            </StackPanel>
                        </StackPanel>
                    </Grid>
                </ScrollViewer>
            </Grid>

            <Grid Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <!--Layout for user favorite payment templates-->
                <toolkitControls:DropShadowPanel Grid.Row="1" Style="{StaticResource DefaultCardDropShadowEffect}">
                    <Grid Style="{StaticResource CardConontrolElement}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock x:Uid="Favorites" Style="{StaticResource SectionHeaderTextBlock}" HorizontalAlignment="Left" />
                        <GridView x:Name="UserTemplatesSelector" Grid.Row="1" ItemsSource="{Binding PaymentTemplates}" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled" ItemTemplate="{StaticResource UserTemplatesDataTemplate}" Margin="-10,0,-10,0" Padding="10,0,0,0" Style="{StaticResource DefaultGridView}" ItemContainerStyle="{StaticResource DefaultGridViewItemContainer}"/>
                    </Grid>
                </toolkitControls:DropShadowPanel>

                <!-- Layout for last operations -->
                <toolkitControls:DropShadowPanel Grid.Row="2" Margin="0,20,0,0" Style="{StaticResource DefaultCardDropShadowEffect}">
                    <Grid Style="{StaticResource CardConontrolElement}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock x:Uid="LastOperations" Style="{StaticResource SectionHeaderTextBlock}"/>
                        <ListView x:Name="LastOperationsListView" Grid.Row="1"  ScrollViewer.VerticalScrollMode="Disabled"  ItemsSource="{Binding LastTransactions}" ItemTemplate="{StaticResource LastOperationsDataTemplate}" Margin="0,10,0,0" ItemContainerStyle="{StaticResource DefaultListViewItemContainer}"/>
                        <Button x:Name="ShowMoreOperationsButton" Content="Показать всё" Grid.Row="2" Style="{StaticResource TiltableAccentButton}"/>
                    </Grid>
                </toolkitControls:DropShadowPanel>

                <!-- Layour for bound cards -->
                <toolkitControls:DropShadowPanel Grid.Row="3" Margin="0,20" Style="{StaticResource DefaultCardDropShadowEffect}">
                    <Grid Style="{StaticResource CardConontrolElement}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="Привязанные карты" Style="{StaticResource SectionHeaderTextBlock}" Typography.Capitals="AllSmallCaps"/>
                        <ListView x:Name="BoundCardListView" Grid.Row="1" ScrollViewer.VerticalScrollMode="Disabled" ItemsSource="{Binding BoundCards}" ItemTemplate="{StaticResource BoundCardsDataTemplate}" Margin="0,10,0,0" ItemContainerStyle="{StaticResource DefaultListViewItemContainer}"/>
                        <Button x:Name="ShowMoreCardsButton" Content="Показать всё" Grid.Row="2" Style="{StaticResource TiltableAccentButton}"/>
                    </Grid>
                </toolkitControls:DropShadowPanel>
            </Grid>
        </Grid>
    </ScrollViewer>

    <CommandBar x:Name="PaymentOperations" Grid.Row="1">
        <AppBarButton x:Name="VendorPayment" Label="Оплата поставщиков" Icon="Shop" Click="MerchantPayment_Click"/>
        <AppBarButton x:Name="p2pPayment" Label="P2P оплата" Icon="Remote" Click="PersonToPersonPayment_Click"/>
        <AppBarButton x:Name="w2wPayment" Label="Перевод с кошелька на кошелек" Icon="Manage" Click="WalletToWalletPayment_Click"/>
    </CommandBar>
</Grid>

Full xaml.cs:

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        // Get the PropertySet that contains the scroll values from MyScrollViewer
        _scrollerPropertySet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MainScrollViewer);
        _compositor = _scrollerPropertySet.Compositor;

        // Create a PropertySet that has values to be referenced in the ExpressionAnimations below
        _props = _compositor.CreatePropertySet();
        _props.InsertScalar("progress", 0);
        _props.InsertScalar("clampSize", 150);
        _props.InsertScalar("scaleFactor", 0.7f);

        // Get references to our property sets for use with ExpressionNodes
        var scrollingProperties = _scrollerPropertySet.GetSpecializedReference<ManipulationPropertySetReferenceNode>();
        var props = _props.GetReference();
        var progressNode = props.GetScalarProperty("progress");
        var clampSizeNode = props.GetScalarProperty("clampSize");
        var scaleFactorNode = props.GetScalarProperty("scaleFactor");

        // Create and start an ExpressionAnimation to track scroll progress over the desired distance
        ExpressionNode progressAnimation = EF.Clamp(-scrollingProperties.Translation.Y / clampSizeNode, 0, 1);
        _props.StartAnimation("progress", progressAnimation);

        // Get the backing visual for the header so that its properties can be animated
        Visual headerVisual = ElementCompositionPreview.GetElementVisual(CollapsiblePageHeader);

        // Create and start an ExpressionAnimation to clamp the header's offset to keep it onscreen
        ExpressionNode headerTranslationAnimation = EF.Conditional(progressNode < 1, 0, -scrollingProperties.Translation.Y - clampSizeNode);
        // Works fine
        headerVisual.StartAnimation("Offset.Y", headerTranslationAnimation);

        ExpressionNode headerScaleAnimation = EF.Lerp(1, 1.25f, EF.Clamp(scrollingProperties.Translation.Y / 50, 0, 1));
        // Exception here
        headerVisual.StartAnimation("Scale.X", headerScaleAnimation);
        headerVisual.StartAnimation("Scale.Y", headerScaleAnimation);


        //Set the header's CenterPoint to ensure the overpan scale looks as desired
        headerVisual.CenterPoint = new Vector3((float)(CollapsiblePageHeader.ActualWidth / 2), (float)CollapsiblePageHeader.ActualHeight, 0);

        // Create and start an ExpressionAnimation to scale the profile image with scroll position
        ExpressionNode scaleAnimation = EF.Lerp(1, scaleFactorNode, progressNode);

        // Get backing visuals for the text blocks so that their properties can be animated
        Visual titleVisual = ElementCompositionPreview.GetElementVisual(TitleText);
        Visual titleImageVisual = ElementCompositionPreview.GetElementVisual(TitleImage);
        Visual subtitleVisual = ElementCompositionPreview.GetElementVisual(SubtitleText);
        Visual subtitleImageVisual = ElementCompositionPreview.GetElementVisual(SubtitleImage);

        // Create an ExpressionAnimation that moves between 1 and 0 with scroll progress, to be used for text block opacity
        ExpressionNode textOpacityAnimation = EF.Clamp(1 - (progressNode * 2), 0, 1);

        // Start opacity and scale animations on the text block visuals
        titleVisual.StartAnimation("Scale.X", scaleAnimation);
        titleVisual.StartAnimation("Scale.Y", scaleAnimation);

        titleImageVisual.StartAnimation("Scale.X", scaleAnimation);
        titleImageVisual.StartAnimation("Scale.Y", scaleAnimation);

        subtitleVisual.StartAnimation("Opacity", textOpacityAnimation);
        subtitleVisual.StartAnimation("Scale.X", scaleAnimation);
        subtitleVisual.StartAnimation("Scale.Y", scaleAnimation);

        subtitleImageVisual.StartAnimation("Opacity", textOpacityAnimation);
        subtitleImageVisual.StartAnimation("Scale.X", scaleAnimation);
        subtitleImageVisual.StartAnimation("Scale.Y", scaleAnimation);

        // Get the backing visuals for the text and button containers so that their properites can be animated
        Visual subtitleContainerVisual = ElementCompositionPreview.GetElementVisual(SubtitleContainer);

        // When the header stops scrolling it is 150 pixels offscreen.  We want the text header to end up with 50 pixels of its content
        // offscreen which means it needs to go from offset 0 to 100 as we traverse through the scrollable region
        ExpressionNode contentOffsetAnimation = progressNode * 100;

        subtitleContainerVisual.StartAnimation("Offset.Y", contentOffsetAnimation);
    }
Community
  • 1
  • 1
  • Have you check [UWPCommunityToolkit](https://github.com/Microsoft/UWPCommunityToolkit)? I remember they had something similar in controls. There is sample app which you can quickly look through and find it. – Andrei Ashikhmin Jun 28 '17 at 04:22
  • Yes, I did. So called ScrollHeader are presented in the toolkit. But I want a little bit different. Moreover I'm still bad at animations, that is why I cannot write it on my own. – Konstantin Chsherbakov Jun 28 '17 at 09:51
  • 1
    My bad, I was thinking about different toolkit. [WindowsUIDevLabs](https://github.com/Microsoft/WindowsUIDevLabs), it has Shy Header in Dynamic Human Interaction section. I believe it looks similar to what you want to achieve. – Andrei Ashikhmin Jun 28 '17 at 10:50
  • Thanks you very much for your hint! This toolkit is extremelly useful. I have found two possible implementations: Shy Header and z-Index scrolling. Thanks again! – Konstantin Chsherbakov Jun 28 '17 at 11:43
  • Glad it helped. Keep in mind that this repo is somewhat "cutting edge", meaning it exists for demonstrating latest capabilities of UWP and most examples only work starting from some specific SDK version. – Andrei Ashikhmin Jun 28 '17 at 11:50
  • Your headerScaleAnimation is a Vector3 so it should be "Scale", not "Scale.X" or "Scale.Y". – Justin XL Jun 29 '17 at 03:11
  • @JustinXL I have changed the property to Scale, but result is the same. – Konstantin Chsherbakov Jun 29 '17 at 03:15

1 Answers1

3

This can be easily done without the community toolkit if your minimum target version is Anniversary Update. Considering you do, let's take you through the solution.

  1. You need to refer to the GitHub repository of the WindowsDevLabs. You can find it here.
  2. Now, once you've downloaded the repo, add the Expression Builder Project to your solution and that's it. You're all set.
  3. Now, for the last part just in the downloaded Repo in the samples section look for "Shrinking Header" page and copy paste the code from codeBehind and make necessary changes in your UI and code behind refering to the Desinger and Code Behind in the "Shrinking Header" Page of the downloaded repo.

Please Note: you can see an active App on the store for the same repo on the store at the Link: Windows Dev Labs UI App

iam.Carrot
  • 4,976
  • 2
  • 24
  • 71
  • 3
    This shrinking behavior should be supported in `10586` too. – Justin XL Jun 28 '17 at 12:04
  • Yes the shrinking will be supported but the rectangle background blurring will only happen on aniversary update and later – iam.Carrot Jun 28 '17 at 13:22
  • 1
    Actually I think that's also supported in `10586`. :) But it shouldn't really matter I guess, still a valid answer. – Justin XL Jun 28 '17 at 13:31
  • no it's not. As soon as you try to create an instance of the GaussianCanvas and run the program it fails saying your Minimum Target version is not set to Anniversary edition – iam.Carrot Jun 28 '17 at 13:40
  • What GaussianCanvas are you talking about? That blur effect is definitely supported in 10586. – Justin XL Jun 28 '17 at 13:47
  • Possibly then. When i was trying to get the blurred effects in my app (in nov 2016) I faced this issue and i had to change the minimum supported version to Anniversary to fix it. They might have changed things around, Microsoft is known to do so – iam.Carrot Jun 28 '17 at 14:07
  • @AdityaSharma could you help me figure the bug in the code, please? I have update my question and added all my code and exception description, check it out. – Konstantin Chsherbakov Jun 29 '17 at 01:47
  • @KonstantinChsherbakov Can you please share your updated xaml as well please – iam.Carrot Jun 29 '17 at 02:43
  • @AdityaSharma thanks for fast reply! I have updated the post and added the xaml code. I'have tried different variants of xaml structure, I also tried the exact same xaml that is provided in the Windows Dev Labs UI Sample App, but the result the same. – Konstantin Chsherbakov Jun 29 '17 at 02:57
  • @AdityaSharma, hmm. I have created a new solution from scratch to share with you and it works! So, that means, the error in my xaml, interesting. Unfortunately I cannot share source code with you due to company's privacy policy. Anyway, thank you for all your help! – Konstantin Chsherbakov Jun 29 '17 at 03:47
  • Great.Glad I could help. – iam.Carrot Jun 29 '17 at 03:49