diff --git a/ModernWpf.Controls/ProgressRing/ProgressRing.cs b/ModernWpf.Controls/ProgressRing/ProgressRing.cs
index 54c468af..63739f4e 100644
--- a/ModernWpf.Controls/ProgressRing/ProgressRing.cs
+++ b/ModernWpf.Controls/ProgressRing/ProgressRing.cs
@@ -7,19 +7,12 @@
namespace ModernWpf.Controls
{
- [TemplateVisualState(GroupName = SizeStatesGroup, Name = LargeState)]
- [TemplateVisualState(GroupName = SizeStatesGroup, Name = SmallState)]
- [TemplateVisualState(GroupName = ActiveStatesGroup, Name = InactiveState)]
- [TemplateVisualState(GroupName = ActiveStatesGroup, Name = ActiveState)]
public class ProgressRing : Control
{
- private const string SizeStatesGroup = "SizeStates";
- private const string LargeState = "Large";
- private const string SmallState = "Small";
-
- private const string ActiveStatesGroup = "ActiveStates";
- private const string InactiveState = "Inactive";
- private const string ActiveState = "Active";
+ const string s_ActiveStateName = "Active";
+ const string s_InactiveStateName = "Inactive";
+ const string s_SmallStateName = "Small";
+ const string s_LargeStateName = "Large";
static ProgressRing()
{
@@ -28,12 +21,9 @@ static ProgressRing()
public ProgressRing()
{
- TemplateSettings = new ProgressRingTemplateSettings();
- }
+ SetValue(TemplateSettingsPropertyKey, new ProgressRingTemplateSettings());
- protected override AutomationPeer OnCreateAutomationPeer()
- {
- return new ProgressRingAutomationPeer(this);
+ SizeChanged += OnSizeChanged;
}
#region IsActive
@@ -49,47 +39,102 @@ public bool IsActive
nameof(IsActive),
typeof(bool),
typeof(ProgressRing),
- new FrameworkPropertyMetadata(OnIsActiveChanged));
+ new FrameworkPropertyMetadata(OnIsActivePropertyChanged));
+
+ private static void OnIsActivePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ ((ProgressRing)sender).OnIsActivePropertyChanged(args);
+ }
+
+ #endregion
+
+ #region TemplateSettings
+
+ private static readonly DependencyPropertyKey TemplateSettingsPropertyKey =
+ DependencyProperty.RegisterReadOnly(
+ nameof(TemplateSettings),
+ typeof(ProgressRingTemplateSettings),
+ typeof(ProgressRing),
+ null);
+
+ public static readonly DependencyProperty TemplateSettingsProperty =
+ TemplateSettingsPropertyKey.DependencyProperty;
- private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ public ProgressRingTemplateSettings TemplateSettings
{
- ((ProgressRing)d).ChangeVisualState(true);
+ get => (ProgressRingTemplateSettings)GetValue(TemplateSettingsProperty);
}
#endregion
- public ProgressRingTemplateSettings TemplateSettings { get; }
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ProgressRingAutomationPeer(this);
+ }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
- ChangeVisualState(false);
+ ChangeVisualState();
}
- protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
- base.OnRenderSizeChanged(sizeInfo);
-
- double maxSideLength = Math.Min(sizeInfo.NewSize.Width, sizeInfo.NewSize.Height);
- double ellipseDiameter = 0.1 * maxSideLength;
- if (maxSideLength <= 40)
- {
- ellipseDiameter += 1;
- }
+ ApplyTemplateSettings();
+ ChangeVisualState();
+ }
- var templateSettings = TemplateSettings;
- templateSettings.EllipseDiameter = ellipseDiameter;
- templateSettings.EllipseOffset = new Thickness(0, maxSideLength / 2 - ellipseDiameter, 0, 0);
- templateSettings.MaxSideLength = maxSideLength;
+ void OnIsActivePropertyChanged(DependencyPropertyChangedEventArgs args)
+ {
+ ChangeVisualState();
+ }
- ChangeVisualState(true);
+ void ChangeVisualState()
+ {
+ VisualStateManager.GoToState(this, IsActive ? s_ActiveStateName : s_InactiveStateName, true);
+ VisualStateManager.GoToState(this, TemplateSettings.MaxSideLength < 60 ? s_SmallStateName : s_LargeStateName, true);
}
- private void ChangeVisualState(bool useTransitions)
+ void ApplyTemplateSettings()
{
- VisualStateManager.GoToState(this, IsActive ? ActiveState : InactiveState, useTransitions);
- VisualStateManager.GoToState(this, TemplateSettings.MaxSideLength < 60 ? SmallState : LargeState, useTransitions);
+ // TemplateSetting properties from WUXC for backwards compatibility.
+ var templateSettings = TemplateSettings;
+
+ var (width, diameterValue, anchorPoint) = calcSettings();
+ (double, double, double) calcSettings()
+ {
+ if (ActualWidth != 0)
+ {
+ double width = Math.Min(ActualWidth, ActualHeight);
+
+ double diameterAdditive;
+ {
+ double init()
+ {
+ if (width <= 40.0)
+ {
+ return 1.0;
+ }
+ return 0.0;
+ }
+ diameterAdditive = init();
+ }
+
+ double diamaterValue = (width * 0.1) + diameterAdditive;
+ double anchorPoint = (width * 0.5) - diamaterValue;
+ return (width, diamaterValue, anchorPoint);
+ }
+
+ return (0.0, 0.0, 0.0);
+ };
+
+ templateSettings.EllipseDiameter = diameterValue;
+
+ Thickness thicknessEllipseOffset = new Thickness(0, anchorPoint, 0, 0);
+
+ templateSettings.EllipseOffset = thicknessEllipseOffset;
+ templateSettings.MaxSideLength = width;
}
}
}
diff --git a/ModernWpf.Controls/ProgressRing/ProgressRing.xaml b/ModernWpf.Controls/ProgressRing/ProgressRing.xaml
index 76129b94..5ce132b5 100644
--- a/ModernWpf.Controls/ProgressRing/ProgressRing.xaml
+++ b/ModernWpf.Controls/ProgressRing/ProgressRing.xaml
@@ -7,12 +7,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/ModernWpfTestApp/ProgressBarReTemplatePage.xaml.cs b/test/ModernWpfTestApp/ProgressBarReTemplatePage.xaml.cs
new file mode 100644
index 00000000..cc3a3d29
--- /dev/null
+++ b/test/ModernWpfTestApp/ProgressBarReTemplatePage.xaml.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Data;
+using System.Windows.Shapes;
+
+namespace MUXControlsTestApp
+{
+ public sealed partial class ProgressBarReTemplatePage : TestPage
+ {
+ public ProgressBarReTemplatePage()
+ {
+ this.InitializeComponent();
+ Loaded += ProgressBarReTemplatePage_Loaded;
+ }
+
+ private void ProgressBarReTemplatePage_Loaded(object sender, RoutedEventArgs e)
+ {
+ var layoutRoot = (Grid)VisualTreeHelper.GetChild(TestProgressBar, 0);
+
+ ((VisualStateGroup)VisualStateManager.GetVisualStateGroups(layoutRoot)[0]).CurrentStateChanged += this.ProgressBarReTemplatePage_CurrentStateChanged;
+ VisualStateText.Text = ((VisualStateGroup)VisualStateManager.GetVisualStateGroups(layoutRoot)[0]).CurrentState.Name;
+
+ var progressBarRoot = VisualTreeHelper.GetChild(layoutRoot, 0);
+ var clip = VisualTreeHelper.GetChild(progressBarRoot, 0);
+ var stackPanel = VisualTreeHelper.GetChild(clip, 0);
+
+ Loaded -= ProgressBarReTemplatePage_Loaded;
+ }
+
+ private void Indicator_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ IndicatorWidthText.Text = ((Rectangle)sender).ActualWidth.ToString();
+ }
+
+ private void ProgressBarReTemplatePage_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
+ {
+ VisualStateText.Text = e.NewState.Name;
+ }
+
+ public void UpdateMinMax_Click(object sender, RoutedEventArgs e)
+ {
+ TestProgressBar.Maximum = String.IsNullOrEmpty(MaximumInput.Text) ? Double.Parse(MaximumInput.PlaceholderText) : Double.Parse(MaximumInput.Text);
+ TestProgressBar.Minimum = String.IsNullOrEmpty(MinimumInput.Text) ? Double.Parse(MinimumInput.PlaceholderText) : Double.Parse(MinimumInput.Text);
+ }
+
+ public void UpdateWidth_Click(object sender, RoutedEventArgs e)
+ {
+ TestProgressBar.Width = String.IsNullOrEmpty(WidthInput.Text) ? Double.Parse(WidthInput.PlaceholderText) : Double.Parse(WidthInput.Text);
+ }
+
+ public void UpdateValue_Click(object sender, RoutedEventArgs e)
+ {
+ TestProgressBar.Value = String.IsNullOrEmpty(ValueInput.Text) ? Double.Parse(ValueInput.PlaceholderText) : Double.Parse(ValueInput.Text);
+ }
+
+ public void ChangeValue_Click(object sender, RoutedEventArgs e)
+ {
+ if (TestProgressBar.Value + 1 > TestProgressBar.Maximum)
+ {
+ TestProgressBar.Value = (int)(TestProgressBar.Minimum + 0.5);
+ }
+ else
+ {
+ TestProgressBar.Value += 1;
+ }
+ }
+
+ public void UpdatePadding_Click(object sender, RoutedEventArgs e)
+ {
+ double paddingLeft = String.IsNullOrEmpty(PaddingLeftInput.Text) ? Double.Parse(PaddingLeftInput.PlaceholderText) : Double.Parse(PaddingLeftInput.Text);
+ double paddingRight = String.IsNullOrEmpty(PaddingRightInput.Text) ? Double.Parse(PaddingRightInput.PlaceholderText) : Double.Parse(PaddingRightInput.Text);
+
+ TestProgressBar.Padding = new Thickness(paddingLeft, 0, paddingRight, 0);
+ }
+ }
+}
diff --git a/test/ModernWpfTestApp/ProgressRingPage.xaml b/test/ModernWpfTestApp/ProgressRingPage.xaml
new file mode 100644
index 00000000..a49b0c64
--- /dev/null
+++ b/test/ModernWpfTestApp/ProgressRingPage.xaml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/ModernWpfTestApp/ProgressRingPage.xaml.cs b/test/ModernWpfTestApp/ProgressRingPage.xaml.cs
new file mode 100644
index 00000000..3baf5409
--- /dev/null
+++ b/test/ModernWpfTestApp/ProgressRingPage.xaml.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace MUXControlsTestApp
+{
+ [TopLevelTestPage(Name = "ProgressRing")]
+ public sealed partial class ProgressRingPage : TestPage
+ {
+ public ProgressRingPage()
+ {
+ this.InitializeComponent();
+ Loaded += ProgressRingPage_Loaded;
+
+ //NavigateToCustomLottieSourcePage.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ProgressRingCustomLottieSourcePage), 0); };
+ NavigateToStoryboardAnimationPage.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ProgressRingStoryboardAnimationPage), 0); };
+ }
+
+ private void ProgressRingPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ var layoutRoot = (FrameworkElement)VisualTreeHelper.GetChild(TestProgressRing, 0);
+
+ var commonStatesGroup = (VisualStateGroup)VisualStateManager.GetVisualStateGroups(layoutRoot)[0];
+ commonStatesGroup.CurrentStateChanged += this.ProgressRingPage_CurrentStateChanged;
+ VisualStateText.Text = commonStatesGroup.CurrentState.Name;
+ foreach (var state in commonStatesGroup.States.Cast().Where(s => s.Storyboard != null))
+ {
+ // Change the animation to 0 duration to avoid timing issues in the test.
+ state.Storyboard.Children[0].Duration = new Duration(TimeSpan.FromSeconds(0));
+ }
+
+ //var animatedVisualPlayer = (ModernWpf.Controls.AnimatedVisualPlayer)VisualTreeHelper.GetChild(layoutRoot, 0);
+
+ //IsPlayingText.Text = animatedVisualPlayer.IsPlaying.ToString();
+ OpacityText.Text = layoutRoot.Opacity.ToString();
+
+ Loaded -= ProgressRingPage_Loaded;
+ }
+
+ private void ProgressRingPage_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
+ {
+ VisualStateText.Text = e.NewState.Name;
+
+ var layoutRoot = (FrameworkElement)VisualTreeHelper.GetChild(TestProgressRing, 0);
+ //var animatedVisualPlayer = (ModernWpf.Controls.AnimatedVisualPlayer)VisualTreeHelper.GetChild(layoutRoot, 0);
+ //IsPlayingText.Text = animatedVisualPlayer.IsPlaying.ToString();
+ OpacityText.Text = layoutRoot.Opacity.ToString();
+ }
+
+ public void UpdateWidth_Click(object sender, RoutedEventArgs e)
+ {
+ TestProgressRing.Width = String.IsNullOrEmpty(WidthInput.Text) ? Double.Parse(WidthInput.PlaceholderText) : Double.Parse(WidthInput.Text);
+ TestProgressRing.Height = String.IsNullOrEmpty(WidthInput.Text) ? Double.Parse(WidthInput.PlaceholderText) : Double.Parse(WidthInput.Text);
+ }
+ }
+}
diff --git a/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml b/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml
new file mode 100644
index 00000000..9f6f5ae7
--- /dev/null
+++ b/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml
@@ -0,0 +1,268 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml.cs b/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml.cs
new file mode 100644
index 00000000..de24a15f
--- /dev/null
+++ b/test/ModernWpfTestApp/ProgressRingStoryboardAnimationPage.xaml.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace MUXControlsTestApp
+{
+ public sealed partial class ProgressRingStoryboardAnimationPage : TestPage
+ {
+ public ProgressRingStoryboardAnimationPage()
+ {
+ this.InitializeComponent();
+ Loaded += ProgressRingStoryboardAnimationPage_Loaded;
+
+ NavigateToMainPage.Click += delegate { Frame.NavigateWithoutAnimation(typeof(ProgressRingPage), 0); };
+ }
+
+ private void ProgressRingStoryboardAnimationPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ var layoutRoot = (FrameworkElement)VisualTreeHelper.GetChild(TestStoryboardAnimationProgressRing, 0);
+
+ var commonStatesGroup = (VisualStateGroup)VisualStateManager.GetVisualStateGroups(layoutRoot)[1];
+ commonStatesGroup.CurrentStateChanged += this.ProgressRingStoryboardAnimationPage_CurrentStateChanged;
+ VisualStateText.Text = commonStatesGroup.CurrentState.Name;
+
+ Loaded -= ProgressRingStoryboardAnimationPage_Loaded;
+ }
+
+ private void ProgressRingStoryboardAnimationPage_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
+ {
+ VisualStateText.Text = e.NewState.Name;
+ }
+
+ public void UpdateWidth_Click(object sender, RoutedEventArgs e)
+ {
+ TestStoryboardAnimationProgressRing.Width = String.IsNullOrEmpty(WidthInput.Text) ? Double.Parse(WidthInput.PlaceholderText) : Double.Parse(WidthInput.Text);
+ TestStoryboardAnimationProgressRing.Height = String.IsNullOrEmpty(WidthInput.Text) ? Double.Parse(WidthInput.PlaceholderText) : Double.Parse(WidthInput.Text);
+ }
+ }
+}
diff --git a/test/TestAppUtils/BindExtension.cs b/test/TestAppUtils/BindExtension.cs
new file mode 100644
index 00000000..e4f0325a
--- /dev/null
+++ b/test/TestAppUtils/BindExtension.cs
@@ -0,0 +1,80 @@
+using System.ComponentModel;
+using System.Windows.Data;
+using System.Windows.Markup;
+using System.Xaml;
+
+namespace System.Windows
+{
+ [MarkupExtensionReturnType(typeof(object))]
+ public class BindExtension : MarkupExtension
+ {
+ public BindExtension()
+ {
+ }
+
+ public BindExtension(string path)
+ {
+ if (path != null)
+ {
+ Path = new PropertyPath(path, null);
+ }
+ }
+
+ public PropertyPath Path { get; set; }
+
+ [DefaultValue(BindingMode.OneTime)]
+ public BindingMode Mode { get; set; } = BindingMode.OneWay;
+
+ [DefaultValue(null)]
+ public IValueConverter Converter { get; set; }
+
+ [DefaultValue(UpdateSourceTrigger.PropertyChanged)]
+ public UpdateSourceTrigger UpdateSourceTrigger { get; set; } = UpdateSourceTrigger.PropertyChanged;
+
+ public override object ProvideValue(IServiceProvider serviceProvider)
+ {
+ var rootObject = (serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider)?.RootObject;
+ if (rootObject == null)
+ throw new InvalidOperationException("Cannot find RootObject");
+
+ PropertyPath effectivePath = Path;
+ string elementName = null;
+
+ if (rootObject is FrameworkElement rootElement && rootElement.TemplatedParent == null)
+ {
+ if (Path != null && Path.PathParameters.Count == 0)
+ {
+ string path = Path.Path;
+ if (!string.IsNullOrEmpty(path))
+ {
+ var names = path.Split('.');
+ if (names.Length == 2)
+ {
+ elementName = names[0];
+ effectivePath = new PropertyPath(names[1]);
+ }
+ }
+ }
+ }
+
+ var binding = new Binding
+ {
+ Path = effectivePath,
+ Mode = Mode,
+ UpdateSourceTrigger = UpdateSourceTrigger,
+ Converter = Converter
+ };
+
+ if (elementName != null)
+ {
+ binding.ElementName = elementName;
+ }
+ else
+ {
+ binding.Source = rootObject;
+ }
+
+ return binding.ProvideValue(serviceProvider);
+ }
+ }
+}
diff --git a/test/TestAppUtils/Controls/TextBoxEx.cs b/test/TestAppUtils/Controls/TextBoxEx.cs
index 7fc9bf77..4fca08ee 100644
--- a/test/TestAppUtils/Controls/TextBoxEx.cs
+++ b/test/TestAppUtils/Controls/TextBoxEx.cs
@@ -16,6 +16,30 @@ public CornerRadius CornerRadius
#endregion
+ #region Header
+
+ public static readonly DependencyProperty HeaderProperty = ControlHelper.HeaderProperty.AddOwner(typeof(TextBoxEx));
+
+ public object Header
+ {
+ get => GetValue(HeaderProperty);
+ set => SetValue(HeaderProperty, value);
+ }
+
+ #endregion
+
+ #region PlaceholderText
+
+ public static readonly DependencyProperty PlaceholderTextProperty = ControlHelper.PlaceholderTextProperty.AddOwner(typeof(TextBoxEx));
+
+ public string PlaceholderText
+ {
+ get => (string)GetValue(PlaceholderTextProperty);
+ set => SetValue(PlaceholderTextProperty, value);
+ }
+
+ #endregion
+
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
diff --git a/test/TestAppUtils/Properties/AssemblyInfo.cs b/test/TestAppUtils/Properties/AssemblyInfo.cs
index 35c2ac19..78bdc615 100644
--- a/test/TestAppUtils/Properties/AssemblyInfo.cs
+++ b/test/TestAppUtils/Properties/AssemblyInfo.cs
@@ -23,4 +23,5 @@
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Controls")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "System.Windows.Controls.Primitives")]
+[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml", "System.Windows")]
[assembly: XmlnsDefinition("http://schemas.modernwpf.com/2019", "ModernWpf.Controls")]