From 5e9b6ddcd93def563e5697f08c9aab8ef1c0f122 Mon Sep 17 00:00:00 2001 From: Dan Johan Kullapa Date: Thu, 2 Apr 2026 13:38:16 +0200 Subject: [PATCH 1/3] Make Durations customizable --- .../Animations/AnimationDurationExtension.cs | 120 ++++++++++++++++++ .../AutoSuggestBox/AutoSuggestBox.xaml | 5 +- .../Controls/CardExpander/CardExpander.xaml | 4 +- src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml | 4 +- src/Wpf.Ui/Controls/ComboBox/ComboBox.xaml | 7 +- .../Controls/ContextMenu/ContextMenu.xaml | 5 +- src/Wpf.Ui/Controls/DataGrid/DataGrid.xaml | 5 +- .../DynamicScrollBar/DynamicScrollBar.xaml | 48 ++++--- src/Wpf.Ui/Controls/Expander/Expander.xaml | 4 +- src/Wpf.Ui/Controls/Menu/MenuItem.xaml | 11 +- .../NavigationView/NavigationLeftFluent.xaml | 15 ++- .../NavigationViewBasePaneButtonStyle.xaml | 7 +- .../NavigationView/NavigationViewCompact.xaml | 21 +-- .../NavigationView/NavigationViewTop.xaml | 11 +- .../Controls/RadioButton/RadioButton.xaml | 19 +-- src/Wpf.Ui/Controls/ScrollBar/ScrollBar.xaml | 40 +++--- .../Controls/TabControl/TabControl.xaml | 6 +- .../Controls/ToggleSwitch/ToggleSwitch.xaml | 27 ++-- src/Wpf.Ui/Controls/ToolBar/ToolBar.xaml | 7 +- .../Controls/TreeView/TreeViewItem.xaml | 13 +- src/Wpf.Ui/Markup/ControlsDictionary.cs | 52 +++++++- 21 files changed, 302 insertions(+), 129 deletions(-) create mode 100644 src/Wpf.Ui/Animations/AnimationDurationExtension.cs diff --git a/src/Wpf.Ui/Animations/AnimationDurationExtension.cs b/src/Wpf.Ui/Animations/AnimationDurationExtension.cs new file mode 100644 index 000000000..4c8889843 --- /dev/null +++ b/src/Wpf.Ui/Animations/AnimationDurationExtension.cs @@ -0,0 +1,120 @@ +// 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; + +/// +/// A XAML markup extension that resolves an key +/// to a concrete value at parse time. +/// This allows Storyboard animations to reference configurable durations +/// without requiring DynamicResource (which cannot be frozen). +/// +/// +/// +/// <!-- Positional syntax --> +/// Duration="{markup:AnimationDuration Slow}" +/// +/// <!-- Named property syntax --> +/// Duration="{markup:AnimationDuration Duration=Slow}" +/// +/// +public class AnimationDurationExtension : MarkupExtension +{ + /// + /// Gets or sets the key to resolve. + /// + public AnimationDuration Duration { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public AnimationDurationExtension() { } + + /// + /// Initializes a new instance of the class. + /// + public AnimationDurationExtension(AnimationDuration animationDuration) + { + Duration = animationDuration; + } + + /// + /// Returns the associated with the current + /// key, including any user-defined override. + /// + public override object ProvideValue(IServiceProvider serviceProvider) + { + return AnimationDurationsDictionary.Get(Duration); + } +} + +/// +/// Defines the speed categories available for control animations. +/// Each key maps to a value held in +/// . +/// +public enum AnimationDuration +{ + /// + /// A longer duration intended for prominent or decorative transitions. + /// + SlowDuration, + + /// + /// The standard duration for most interactive feedback animations. + /// + NormalDuration, + + /// + /// A shorter duration for rapid state changes. + /// + FastDuration, +} + +/// +/// A static registry that maps each key to a +/// value. Defaults are provided out of the box and can be +/// overridden at application startup via or through the +/// corresponding properties on +/// (e.g. <ui:ControlsDictionary SlowDuration="0:0:0.9" />). +/// Overrides must be applied before the control dictionaries are loaded, +/// because resolves values at XAML parse time. +/// +public static class AnimationDurationsDictionary +{ + private static readonly Dictionary Defaults = new() + { + [AnimationDuration.SlowDuration] = new Duration(TimeSpan.FromMilliseconds(333)), + [AnimationDuration.NormalDuration] = new Duration(TimeSpan.FromMilliseconds(167)), + [AnimationDuration.FastDuration] = new Duration(TimeSpan.FromMilliseconds(80)), + }; + + private static readonly Dictionary Overrides = []; + + /// + /// Returns the for the specified , + /// using the user-defined override if one exists, otherwise the built-in default. + /// + /// The animation speed category to look up. + public static Duration Get(AnimationDuration key) + { + return Overrides.TryGetValue(key, out Duration value) + ? value + : Defaults[key]; + } + + /// + /// Overrides the for the specified . + /// Must be called before the WPF UI control dictionaries are parsed. + /// + /// The animation speed category to override. + /// The desired duration. + public static void Set(AnimationDuration key, TimeSpan value) + { + Overrides[key] = new Duration(value); + } +} \ No newline at end of file diff --git a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml index 446d7f002..f810a9b83 100644 --- a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml +++ b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.xaml @@ -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"> 1,1,1,0 @@ -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}" /> @@ -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}" /> diff --git a/src/Wpf.Ui/Controls/CardExpander/CardExpander.xaml b/src/Wpf.Ui/Controls/CardExpander/CardExpander.xaml index 0f66c757b..20db6007c 100644 --- a/src/Wpf.Ui/Controls/CardExpander/CardExpander.xaml +++ b/src/Wpf.Ui/Controls/CardExpander/CardExpander.xaml @@ -64,7 +64,7 @@ Storyboard.TargetName="ChevronGrid" Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)" To="180" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -75,7 +75,7 @@ Storyboard.TargetName="ChevronGrid" Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)" To="0" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> diff --git a/src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml b/src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml index 594c08670..3acc5b191 100644 --- a/src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml +++ b/src/Wpf.Ui/Controls/CheckBox/CheckBox.xaml @@ -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"> + Duration="{animations:AnimationDuration NormalDuration}" />--> @@ -340,13 +341,13 @@ Storyboard.TargetProperty="(UIElement.Opacity)" From="1.0" To="0.0" - Duration="0:0:0.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" />--> diff --git a/src/Wpf.Ui/Controls/RadioButton/RadioButton.xaml b/src/Wpf.Ui/Controls/RadioButton/RadioButton.xaml index 20477ce10..53329072a 100644 --- a/src/Wpf.Ui/Controls/RadioButton/RadioButton.xaml +++ b/src/Wpf.Ui/Controls/RadioButton/RadioButton.xaml @@ -11,7 +11,8 @@ + xmlns:system="clr-namespace:System;assembly=System.Runtime" + xmlns:animations="clr-namespace:Wpf.Ui.Animations"> 11 20 @@ -119,12 +120,12 @@ Storyboard.TargetName="CheckGlyph" Storyboard.TargetProperty="(Ellipse.LayoutTransform).(ScaleTransform.ScaleY)" To="1.2" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -169,13 +170,13 @@ Storyboard.TargetProperty="(Ellipse.LayoutTransform).(ScaleTransform.ScaleY)" From="0.7" To="1.0" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -197,13 +198,13 @@ Storyboard.TargetProperty="(Ellipse.LayoutTransform).(ScaleTransform.ScaleY)" From="1.2" To=".95" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -214,12 +215,12 @@ Storyboard.TargetName="CheckGlyph" Storyboard.TargetProperty="(Ellipse.LayoutTransform).(ScaleTransform.ScaleY)" To="1.2" - Duration="00:00:00.167" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> diff --git a/src/Wpf.Ui/Controls/ScrollBar/ScrollBar.xaml b/src/Wpf.Ui/Controls/ScrollBar/ScrollBar.xaml index 7c84072b9..5a22cdf6f 100644 --- a/src/Wpf.Ui/Controls/ScrollBar/ScrollBar.xaml +++ b/src/Wpf.Ui/Controls/ScrollBar/ScrollBar.xaml @@ -9,11 +9,9 @@ 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:sys="clr-namespace:System;assembly=mscorlib"> - 0:0:0.16 - 0:0:0.16 - 12 12 @@ -55,7 +53,7 @@ Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Opacity)" From="0.0" To="1.0" - Duration="{StaticResource ButtonHoverAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -67,7 +65,7 @@ Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Opacity)" From="1.0" To="0.0" - Duration="{StaticResource ButtonHoverAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -175,25 +173,25 @@ Storyboard.TargetProperty="Width" From="6" To="8" - Duration="{StaticResource ScrollAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -205,25 +203,25 @@ Storyboard.TargetProperty="Width" From="8" To="6" - Duration="{StaticResource ScrollAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -293,25 +291,25 @@ Storyboard.TargetProperty="Height" From="6" To="8" - Duration="{StaticResource ScrollAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> @@ -323,25 +321,25 @@ Storyboard.TargetProperty="Height" From="8" To="6" - Duration="{StaticResource ScrollAnimationDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> + Duration="{animations:AnimationDuration NormalDuration}" /> diff --git a/src/Wpf.Ui/Controls/TabControl/TabControl.xaml b/src/Wpf.Ui/Controls/TabControl/TabControl.xaml index 8c2449aba..4a9ecccb7 100644 --- a/src/Wpf.Ui/Controls/TabControl/TabControl.xaml +++ b/src/Wpf.Ui/Controls/TabControl/TabControl.xaml @@ -5,7 +5,9 @@ All Rights Reserved. --> - +