Skip to content

XamlFlair is an animation library for UWP and WPF built to facilitate Xaml animations using only attached properties.

License

Notifications You must be signed in to change notification settings

assassin316/XamlFlair

 
 

Repository files navigation

XamlFlair

The goal of the XamlFlair library is to ease the implementation of common animations and allow a developer to easily add a single or combined set of animations with just a few lines of Xaml.

Sample App

Basic Concepts

The basic concept of XamlFlair is based on animations that are categorized as From and To. Any UI element that consists of a From animation will start with one or more arbitrary values, and complete using the default value of the corresponding property. Any UI element that consists of a To animation will start in its current state and animate to one or more arbitrary values.

Example of a From animation (a UI element translating to the default value of a Translation (0)):

From animation

Example of a To animation (a UI element sliding away from its current state):

To animation

Install from Nuget

Platform Package NuGet
UWP XamlFlair.UWP UWPNuGetShield
WPF XamlFlair.WPF WPFNuGetShield

To install XamlFlair, run the following command in the Package Manager Console:

UWP:

Install-Package XamlFlair.UWP

Your app must target a minimum of Windows 10 version 1803 (build 17134)

WPF:

Install-Package XamlFlair.WPF

Requires .Net Framework 4.7.2

Usage

To begin, you need to have the following Xaml namespace reference:

UWP:

xmlns:xf="using:XamlFlair"

WPF:

xmlns:xf="clr-namespace:XamlFlair;assembly=XamlFlair.WPF"

From here on, it's a simple matter of setting an attached property to any FrameworkElement that needs an animation:

<Border xf:Animations.Primary="{StaticResource FadeIn}" />

Note: If your FrameworkElement defines a CompositeTransform in your Xaml, it will be altered during the animation process.

Note: The use of StaticResource is to reference global common animations, which is discussed in the next section.

Base Animation Types

Fade

Fade animation

Warning: Be careful when animating FadeTo since the element remains in the Visual Tree if the Visibility is Visible. There may be cases where you'll need to manually manage IsHitTestVisible to allow the user to tap through the element.

Translate

Translation animation

Scale

Scale animation

Rotate

Rotation animation

Blur

Blur animation

Saturate (UWP-only)

Saturate animation

Tint (UWP-only)

Tint animation

The following lists some notable default values when working with XamlFlair:

  • Kind: FadeTo
  • Duration: 500
  • Easing: Cubic
  • Easing Mode: EaseOut
  • TransformCenterPoint: (0.5, 0.5)
  • Event: Loaded
  • Saturation: 0.5 (UWP-only)
  • Tint: Transparent (UWP-only)

Using a ResourceDictionary for base settings

All common animations should be placed in a global ResourceDictionary (ex: Animations.xaml) and used where needed throughout the app. The goal is to consolidate all the animations into one file with meaningful names so that any developer can understand exactly what animation is applied to a FrameworkElement. Here's a small example of what it looks like:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:xf="using:XamlFlair">

    <x:Double x:Key="PositiveOffset">50</x:Double>
    <x:Double x:Key="NegativeOffset">-50</x:Double>
    <x:Double x:Key="SmallScaleFactor">0.75</x:Double>
    <x:Double x:Key="LargeScaleFactor">1.25</x:Double>

    <xf:AnimationSettings x:Key="FadeIn"
                          Kind="FadeFrom"
                          Opacity="0" />

    <xf:AnimationSettings x:Key="FadeOut"
                          Kind="FadeTo"
                          Opacity="0" />

    <!-- Scale to a larger value -->
    <xf:AnimationSettings x:Key="Expand"
                          Kind="ScaleXTo,ScaleYTo"
                          ScaleX="{StaticResource LargeScaleFactor}"
                          ScaleY="{StaticResource LargeScaleFactor}" />

    <!-- Scale from a larger value -->
    <xf:AnimationSettings x:Key="Contract"
                          Kind="ScaleXFrom,ScaleYFrom"
                          ScaleX="{StaticResource LargeScaleFactor}"
                          ScaleY="{StaticResource LargeScaleFactor}" />
    .
    .
    .
    </ResourceDictionary>

To setup this set of pre-configured AnimationSettings already available in your app, perform the following steps:

  1. Right-click on your project, then click Add > New Item...
  2. Choose Resource Dictionary and name it Animations.xaml
  3. In your App.xaml, add the following:
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Animations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
  1. In Animations.xaml, copy & paste the contents from the corresponding links below:

Your app now has a global set of common animations ready to use.

Combining animations

Animations can be combined, and as previously mentioned, any of these combined animations that are commonly used should be placed in the global ResourceDictionary (ex: Animations.xaml):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:xf="using:XamlFlair">
    .
    .
    .
    <xf:AnimationSettings x:Key="FadeInAndSlideFromLeft"
                          Kind="FadeFrom,TranslateXFrom"
                          Opacity="0"
                          OffsetX="{StaticResource NegativeOffset}" />

    <xf:AnimationSettings x:Key="FadeInAndSlideFromTop"
                          Kind="FadeFrom,TranslateYFrom"
                          Opacity="0"
                          OffsetY="{StaticResource NegativeOffset}" />

    <xf:AnimationSettings x:Key="FadeInAndGrow"
                          Kind="FadeFrom,ScaleXFrom,ScaleXFrom"
                          Opacity="0"
                          ScaleX="{StaticResource SmallScaleFactor}"
                          ScaleY="{StaticResource SmallScaleFactor}" />

    <xf:AnimationSettings x:Key="FadeInAndGrowHorizontally"
                          Kind="FadeFrom,ScaleXFrom"
                          Opacity="0"
                          ScaleX="{StaticResource SmallScaleFactor}" />
    .
    .
    .
    </ResourceDictionary>

This demonstrates a combined animation of a FadeFrom and TranslateFrom:

Fade and translation animation

This demonstrates a combined animation of a FadeFrom, TranslateFrom, and ScaleFrom:

Fade, translation, and scale snimation

Overriding values

Animations can have their settings overridden directly on the FrameworkElement. This is commonly done to alter values for Delay and Duration so that we don't over-populate the Animations.xaml file with repeated resources. To achieve overriding, use the Animate markup extension paired with the BasedOn property:

<Border xf:Animations.Primary="{xf:Animate BasedOn={StaticResource ScaleFromBottom}, Delay=500}">

Compound animation

A compound animation is simply a multi-step animation using the CompoundSettings class. Each inner animation executes once the previous one completes, hence they're sequential animations:

Compound animation

<xf:CompoundSettings x:Key="Compound">
    <xf:CompoundSettings.Sequence>
        <xf:AnimationSettings Kind="ScaleXTo"
                              ScaleX="1.25"
                              Duration="1250" />
        <xf:AnimationSettings Kind="ScaleXTo"
                              ScaleX="1"
                              Duration="1250" />
        <xf:AnimationSettings Kind="RotateTo"
                              Rotation="360"
                              Duration="1250" />
    </xf:CompoundSettings.Sequence>
</xf:CompoundSettings>

Note: CompoundSettings support the Event property, which is discussed in a later section.

Repeating animations

An animation can be repeated by using the IterationBehavior and IterationCount properties (default values of Count and 1 respectively).

Compound animation

The following demonstrates how to run an animation only 5 times:

<Border xf:Animations.Primary="{StaticResource FadeIn}"
        xf:Animations.IterationCount="5" />

The following demonstrates how to run an animation indefinitely:

<Border xf:Animations.Primary="{StaticResource FadeIn}"
        xf:Animations.IterationBehavior="Forever" />

Also note that it is also possible to repeat a Compound animation. For example, using the compound animation (named Progress) from the previous section:

<Border xf:Animations.Primary="{StaticResource Progress}"
        xf:Animations.IterationCount="5" />

Warning: When using repeating animations, you cannot set a Secondary animation on the element.

Events and Bindings

By default, all animations execute once the UI element fires its Loaded event. This behavior can be overridden by setting the Event property. Event can be one of the following values:

  • Loaded (default value)
  • Loading (UWP-only)
  • None
  • Visibility (triggers only when Visibility == Visible)
  • DataContextChanged
  • PointerOver
  • PointerExit
  • GotFocus
  • LostFocus

When specifying None, you will manually need to trigger your animations using the PrimaryBinding or SecondaryBinding properties. These properties are of type bool and expect a value of True in order to execute the corresponding animation. The following is an example of triggering an animation based off the IsChecked of the CheckBox control:

<CheckBox x:Name="SampleCheckBox"
          IsChecked="False" />

<Rectangle xf:Animations.PrimaryBinding="{Binding IsChecked, ElementName=SampleCheckBox}"
           xf:Animations.Primary="{xf:Animate BasedOn={StaticResource FadeIn}, Event=None}" />

The above animation will only execute when the IsChecked is True. If None was not specified for Event, the animation would then execute on Loaded and on the binding.

Using the StartWith property

There will be cases when you will need your UI element to start in a specific state, for example, the element needs to be shrunk before its animation executes. This is achieved using the StartWith property:

<Rectangle xf:Animations.Primary="{xf:Animate BasedOn={StaticResource ScaleFromBottom}, Delay=500}"
           xf:Animations.StartWith="{StaticResource ScaleFromBottom}" />

In the above example, since the element is scaling from the bottom, but with a delay, we need to start in the scaled position, so we use the StartWith property to set its initial state. What StartWith essentially does is setup the initial values on the element as soon as it has loaded.

Using the AllowOpacityReset property (WPF-only)

The .Net documentation states the following:

In some cases, it might appear that you can't change the value of a property after it has been animated. ...you must stop the animation from influencing the property.

There may be cases when you animate the opacity, in which the opacity animation suddenly resets it's value instead of animating, or doesn't behave as you intend. In cases, you may need to set AllowOpacityReset = False (the default value of AllowOpacityReset is True) to achieve the intended behavior:

<Image xf:Animations.Primary="{StaticResource FadeInThenFadeOut}"
       xf:Animations.AllowOpacityReset="False"
       Source="/Assets/..." />

Using the ClipToBounds property (UWP-only)

A helpful property that exists in WPF, ClipToBounds is a helpful property that exists in WPF, but unfortunately not in UWP. Therefore, it has been added in XamlFlair due to its ease of use and handiness. To clip child content to the bounds of the containing element, simply set ClipToBounds to True on the containing element:

<Border xf:Layout.ClipToBounds="True">
.
.
.
<Border>

Logging animations

The XamlFlair library abstracts its logging using LibLog. LibLib supports the major logging frameworks, which allows a developer using the XamlFlair library to choose their preferred logging system. Below is a logging example using Serilog in a UWP app:

public App()
{
    this.InitializeComponent();
    .
    .
    .
    // Setup the Serilog logger
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .WriteTo.Debug()
        .CreateLogger();
}

To output the values of one or more animations, simply set True to the EnableLogging property on the target FrameworkElement:

<Rectangle xf:Animations.EnableLogging="True"
           xf:Animations.Primary="{StaticResource SlideFromLeft}" />

Doing so will provide you with the following similar console output (differs slightly for WPF):

Element = Windows.UI.Xaml.Controls.Button
Kind = FadeFrom, TranslateFrom
TargetProperty = Translation
Duration = 500
Delay = 0
Opacity = 0
OffsetX = 0
OffsetY = 50
OffsetZ = 0
ScaleX = 1
ScaleY = 1
ScaleZ = 1
Rotation = 0
Blur = 0
TransformCenterPoint = 0.5,0.5
Easing = Cubic
EasingMode = EaseOut

As each storyboard executes, it's kept in an internal list until it completes (or gets stopped). To output this internal list, temporarily add the following in your app startup code:

Animations.EnableActiveTimelinesLogging = true;

Doing so will provide you with the following similar console output:

---------- ALL ACTIVE TIMELINES --------
Active timeline removed at 12:42:26:43222

Element = Button,  Key = d69f826a-1978-4a4e-b516-4a6b0469238b,  ElementGuid = 195d8c13-1dd7-4fef-a7f3-fc78bdab1cd7
    State = Running,  IsSequence = False,  IsIterating = False,  IterationBehavior = Count,  IterationCount = 0
------------------------------------

---------- ACTIVE TIMELINE --------
Guid d69f826a-1978-4a4e-b516-4a6b0469238b - Updated state to: Completed at 12:42:26:88616
------------------------------------

---------- ALL ACTIVE TIMELINES --------
Active timeline removed at 12:42:26:89614

NO ACTIVE TIMELINES!
------------------------------------

ListViewBase (UWP) and ListBox-based (WPF) animations

ListView item animations

In order to properly implement item animations on list items, it was not enough to simply create attached properties against the ListViewBase (UWP) and ListBox (WPF) controls. Instead, inherited controls were created: AnimatedListView and AnimatedGridView for UWP, and AnimatedListView and AnimatedListBox for WPF, all available from the XamlFlair.Controls namespace:

UWP namespace:

xmlns:xfc="using:XamlFlair.Controls"

WPF namespace:

xmlns:xfc="clr-namespace:XamlFlair.Controls;assembly=XamlFlair.WPF"

Animating items in lists is slightly different than animating a typical UI element. The main reason for this difference is that the Event value on the corresponding AnimationSettings cannot be changed from its default value. Therefore the following is invalid:

<xfc:AnimatedListView ItemsSource="Binding SampleDataList"
                      xf:Animations:Items="{xf:Animate BasedOn={StaticResource FadeIn}, Event=Visibility}" />

List item animations, by default, animate based on the Loaded event of each visible item, and also based on an update to the ItemsSource property of the list control. In order to disable these default behaviors, the following two properties can be used independently:

<xfc:AnimatedListView ItemsSource="Binding SampleDataList"
                      xf:Animations.AnimateOnLoad="False"
                      ... />

<xfc:AnimatedListView ItemsSource="Binding SampleDataList"
                      xf:Animations.AnimateOnItemsSourceChange="False"
                      ... />

By default, item animations have a delay of 25 milliseconds between each item. This value can be changed using the InterElementDelay property:

<xfc:AnimatedListView ItemsSource="Binding SampleDataList"
                      xf:Animations.InterElementDelay="50"
                      ... />

Just like PrimaryBinding and SecondaryBinding, item animations can be triggered by a binding with the use of the ItemsBinding property:

<xfc:AnimatedListView ItemsSource="Binding SampleDataList"
                      xf:Animations.AnimateOnLoad="False"
                      xf:Animations.ItemsBinding="{Binding MyViewModelProperty}"
                      xf:Animations:Items="{xf:Animate BasedOn={StaticResource FadeIn}}" />

Warning (UWP-only): Be aware that if you have any ItemContainerTransitions set on the AnimatedListView or AnimatedGridView, they will be cleared. This is done to avoid conflicting item animations.

Note (UWP-only): To avoid any flickers on item animations, there is currently a constraint in place: Items animation must contain a FadeFrom.

About

XamlFlair is an animation library for UWP and WPF built to facilitate Xaml animations using only attached properties.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 95.5%
  • Smalltalk 3.7%
  • Batchfile 0.8%