Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/Wpf.Ui/Animations/AnimationDurationExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.Windows.Markup;

namespace Wpf.Ui.Animations;

/// <summary>
/// A XAML markup extension that resolves an <see cref="AnimationDuration"/> key
/// to a concrete <see cref="Duration"/> value at parse time.
/// This allows Storyboard animations to reference configurable durations
/// without requiring <c>DynamicResource</c> (which cannot be frozen).
/// </summary>
/// <example>
/// <code lang="xml">
/// &lt;!-- Positional syntax --&gt;
/// Duration="{markup:AnimationDuration Slow}"
///
/// &lt;!-- Named property syntax --&gt;
/// Duration="{markup:AnimationDuration Duration=Slow}"
/// </code>
/// </example>
public class AnimationDurationExtension : MarkupExtension
{
/// <summary>
/// Gets or sets the <see cref="AnimationDuration"/> key to resolve.
/// </summary>
public AnimationDuration Duration { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="AnimationDurationExtension"/> class.
/// </summary>
public AnimationDurationExtension() { }

/// <summary>
/// Initializes a new instance of the <see cref="AnimationDurationExtension"/> class.
/// </summary>
public AnimationDurationExtension(AnimationDuration animationDuration)
{
Duration = animationDuration;
}

/// <summary>
/// Returns the <see cref="System.Windows.Duration"/> associated with the current
/// <see cref="Duration"/> key, including any user-defined override.
/// </summary>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return AnimationDurationsDictionary.Get(Duration);
}
}

/// <summary>
/// Defines the speed categories available for control animations.
/// Each key maps to a <see cref="Duration"/> value held in
/// <see cref="AnimationDurationsDictionary"/>.
/// </summary>
public enum AnimationDuration
{
/// <summary>
/// A longer duration intended for prominent or decorative transitions.
/// </summary>
SlowDuration,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's drop the Duration from all, it's already an AnimationDuration.
I also think we can also structure this differently to avoid the Dictionary and ControlsDictionary changes.
Something like

// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.Windows.Markup;

namespace Wpf.Ui.Animations;

/// <summary>
/// A XAML markup extension that resolves an <see cref="AnimationDuration" /> key
/// to a concrete <see cref="Duration" /> value at parse time.
/// This allows Storyboard animations to reference configurable durations
/// without requiring <c>DynamicResource</c> (which cannot be frozen).
/// </summary>
/// <example>
/// <code lang="xml">
/// &lt;!-- Positional syntax --&gt;
/// Duration="{markup:AnimationDuration Slow}"
/// 
/// &lt;!-- Named property syntax --&gt;
/// Duration="{markup:AnimationDuration Duration=Slow}"
/// </code>
/// </example>
public class AnimationDurationExtension : MarkupExtension
{
    /// <summary>
    /// Initializes a new instance of the <see cref="AnimationDurationExtension" /> class.
    /// </summary>
    public AnimationDurationExtension() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="AnimationDurationExtension" /> class.
    /// </summary>
    public AnimationDurationExtension(AnimationDurationType animationDurationType)
    {
        Duration = AnimationDuration.FromType(animationDurationType);
    }

    /// <summary>
    /// Gets or sets the <see cref="AnimationDuration" /> key to resolve.
    /// </summary>
    public AnimationDuration Duration { get; set; }

    /// <summary>
    /// Returns the <see cref="System.Windows.Duration" /> associated with the current
    /// <see cref="Duration" /> key, including any user-defined override.
    /// </summary>
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Duration.Duration;
    }
}

public struct AnimationDuration
{
    private static readonly AnimationDuration[] _durations =
    [
        new() { Duration = TimeSpan.FromMilliseconds(333), Type = AnimationDurationType.Slow },
        new() { Duration = TimeSpan.FromMilliseconds(167), Type = AnimationDurationType.Normal },
        new() { Duration = TimeSpan.FromMilliseconds(80), Type = AnimationDurationType.Fast }
    ];

    public Duration Duration { get; set; }

    public AnimationDurationType Type { get; set; }

    public static AnimationDuration Normal
    {
        get => _durations[1];
        set => _durations[1] = value;
    }

    public static AnimationDuration Fast
    {
        get => _durations[2];
        set => _durations[2] = value;
    }

    public static AnimationDuration Slow
    {
        get => _durations[0];
        set => _durations[0] = value;
    }

    public static AnimationDuration FromType(AnimationDurationType type) => _durations[(int)type];
}

/// <summary>
/// Defines the duration types available for control animations.
/// </summary>
public enum AnimationDurationType
{
    /// <summary>
    /// A longer duration intended for prominent or decorative transitions.
    /// </summary>
    Slow,

    /// <summary>
    /// The standard duration for most interactive feedback animations.
    /// </summary>
    Normal,

    /// <summary>
    /// A shorter duration for rapid state changes.
    /// </summary>
    Fast,
}


/// <summary>
/// The standard duration for most interactive feedback animations.
/// </summary>
NormalDuration,

/// <summary>
/// A shorter duration for rapid state changes.
/// </summary>
FastDuration,
}

/// <summary>
/// A static registry that maps each <see cref="AnimationDuration"/> key to a
/// <see cref="Duration"/> value. Defaults are provided out of the box and can be
/// overridden at application startup via <see cref="Set"/> or through the
/// corresponding properties on <see cref="ControlsDictionary"/>

Check warning on line 82 in src/Wpf.Ui/Animations/AnimationDurationExtension.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'ControlsDictionary' that could not be resolved
/// (e.g. <c>&lt;ui:ControlsDictionary SlowDuration="0:0:0.9" /&gt;</c>).
/// Overrides must be applied before the control dictionaries are loaded,
/// because <see cref="AnimationDurationExtension"/> resolves values at XAML parse time.
/// </summary>
public static class AnimationDurationsDictionary

Check warning on line 87 in src/Wpf.Ui/Animations/AnimationDurationExtension.cs

View workflow job for this annotation

GitHub Actions / build

{
private static readonly Dictionary<AnimationDuration, Duration> Durations = new()
{
[AnimationDuration.SlowDuration] = new Duration(TimeSpan.FromMilliseconds(333)),
[AnimationDuration.NormalDuration] = new Duration(TimeSpan.FromMilliseconds(167)),
[AnimationDuration.FastDuration] = new Duration(TimeSpan.FromMilliseconds(80)),
};

/// <summary>
/// Returns the <see cref="Duration"/> for the specified <paramref name="key"/>,
/// using the user-defined override if one exists, otherwise the built-in default.
/// </summary>
/// <param name="key">The animation speed category to look up.</param>
public static Duration Get(AnimationDuration key)
{
return Durations[key];
}

/// <summary>
/// Overrides the <see cref="Duration"/> for the specified <paramref name="key"/>.
/// Must be called before the WPF UI control dictionaries are parsed.
/// </summary>
/// <param name="key">The animation speed category to override.</param>
/// <param name="value">The desired duration.</param>
public static void Set(AnimationDuration key, TimeSpan value)
{
Durations[key] = new Duration(value);
}
}
5 changes: 3 additions & 2 deletions src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:animations="clr-namespace:Wpf.Ui.Animations"
xmlns:system="clr-namespace:System;assembly=mscorlib">

<Thickness x:Key="AutoSuggestBoxBorderThemeThickness">1,1,1,0</Thickness>
Expand Down Expand Up @@ -68,7 +69,7 @@
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Opacity)"
From="0.0"
To="1.0"
Duration="00:00:00.167" />
Duration="{animations:AnimationDuration NormalDuration}" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
Expand All @@ -80,7 +81,7 @@
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Opacity)"
From="1.0"
To="0.0"
Duration="00:00:00.167" />
Duration="{animations:AnimationDuration NormalDuration}" />
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
Expand Down
4 changes: 2 additions & 2 deletions src/Wpf.Ui/Controls/CardExpander/CardExpander.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
Storyboard.TargetName="ChevronGrid"
Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)"
To="180"
Duration="00:00:00.167" />
Duration="{animations:AnimationDuration NormalDuration}" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
Expand All @@ -75,7 +75,7 @@
Storyboard.TargetName="ChevronGrid"
Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="00:00:00.167" />
Duration="{animations:AnimationDuration NormalDuration}" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
Expand Down
4 changes: 2 additions & 2 deletions src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:converters="clr-namespace:Wpf.Ui.Converters"
xmlns:animations="clr-namespace:Wpf.Ui.Animations"
xmlns:system="clr-namespace:System;assembly=mscorlib">

<!--
Expand All @@ -33,7 +34,6 @@
<system:Double x:Key="CheckBoxHeight">20</system:Double>
<system:Double x:Key="CheckBoxWidth">20</system:Double>
<system:TimeSpan x:Key="CheckBoxAnimationOffset">00:00:00.250</system:TimeSpan>
<Duration x:Key="CheckBoxAnimationDuration">00:00:00.250</Duration>
<converters:ClipConverter x:Key="ClipConverter" />

<Style x:Key="DefaultCheckBoxStyle" TargetType="{x:Type CheckBox}">
Expand Down Expand Up @@ -140,7 +140,7 @@
Storyboard.TargetProperty="Tag"
From="0"
To="1"
Duration="{StaticResource CheckBoxAnimationDuration}">
Duration="{animations:AnimationDuration NormalDuration}">
<DoubleAnimation.EasingFunction>
<CubicEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
Expand Down
7 changes: 4 additions & 3 deletions src/Wpf.Ui/Controls/ComboBox/ComboBox.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:animation="clr-namespace:Wpf.Ui.Animations"
xmlns:system="clr-namespace:System;assembly=System.Runtime">

<Thickness x:Key="ComboBoxPadding">10,8,10,8</Thickness>
Expand Down Expand Up @@ -333,13 +334,13 @@
Storyboard.TargetProperty="(controls:SymbolIcon.RenderTransform).(RotateTransform.Angle)"
From="0"
To="180"
Duration="00:00:00.167" />
Duration="{animation:AnimationDuration NormalDuration}" />
<DoubleAnimation
Storyboard.TargetName="DropDownBorder"
Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.Y)"
From="-90"
To="0"
Duration="00:00:00.167">
Duration="{animation:AnimationDuration NormalDuration}">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
Expand All @@ -355,7 +356,7 @@
Storyboard.TargetProperty="(controls:SymbolIcon.RenderTransform).(RotateTransform.Angle)"
From="180"
To="0"
Duration="00:00:00.167" />
Duration="{animation:AnimationDuration NormalDuration}" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
Expand Down
5 changes: 3 additions & 2 deletions src/Wpf.Ui/Controls/ContextMenu/ContextMenu.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls">
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:animations="clr-namespace:Wpf.Ui.Animations">

<Style x:Key="UiContextMenu" TargetType="{x:Type ContextMenu}">
<Setter Property="TextElement.Foreground" Value="{DynamicResource ContextMenuForeground}" />
Expand Down Expand Up @@ -62,7 +63,7 @@
Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.Y)"
From="-90"
To="0"
Duration="00:00:00.167">
Duration="{animations:AnimationDuration NormalDuration}">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
Expand Down
5 changes: 3 additions & 2 deletions src/Wpf.Ui/Controls/DataGrid/DataGrid.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Wpf.Ui.Controls"
xmlns:animations="clr-namespace:Wpf.Ui.Animations"
xmlns:system="clr-namespace:System;assembly=System.Runtime">

<ResourceDictionary.MergedDictionaries>
Expand Down Expand Up @@ -648,7 +649,7 @@
Storyboard.TargetProperty="(controls:SymbolIcon.RenderTransform).(RotateTransform.Angle)"
From="0"
To="180"
Duration="00:00:00.333" />
Duration="{animations:AnimationDuration SlowDuration}" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
Expand All @@ -660,7 +661,7 @@
Storyboard.TargetProperty="(controls:SymbolIcon.RenderTransform).(RotateTransform.Angle)"
From="180"
To="0"
Duration="00:00:00.333" />
Duration="{animations:AnimationDuration SlowDuration}" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
Expand Down
Loading
Loading