From 54208748d6c7e7c1498b92cddfa90378f2b8e133 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Thu, 18 Mar 2021 18:36:55 -0700 Subject: [PATCH 001/299] Adding control width trigger --- .../Triggers/ControlWidthTrigger.cs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs diff --git a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs new file mode 100644 index 000000000..75c89474c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Triggers +{ + /// + /// A conditional state trigger that functions + /// based on the target control's width. + /// + public class ControlWidthTrigger : StateTriggerBase + { + /// + /// Gets or sets a value indicating + /// whether this trigger will be active or not. + /// + public bool CanTrigger + { + get => (bool)GetValue(CanTriggerProperty); + set => SetValue(CanTriggerProperty, value); + } + + /// + /// Identifies the DependencyProperty. + /// + public static readonly DependencyProperty CanTriggerProperty = DependencyProperty.Register( + nameof(CanTrigger), + typeof(bool), + typeof(ControlWidthTrigger), + new PropertyMetadata(true, OnCanTriggerProperty)); + + private static void OnCanTriggerProperty(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ControlWidthTrigger)d).UpdateTrigger(); + } + + /// + /// Gets or sets the max size at which to trigger. + /// + public double MaxWidth + { + get => (double)GetValue(MaxWidthProperty); + set => SetValue(MaxWidthProperty, value); + } + + /// + /// Identifies the DependencyProperty. + /// + public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register( + nameof(MaxWidth), + typeof(double), + typeof(ControlWidthTrigger), + new PropertyMetadata(double.PositiveInfinity, OnMaxWidthPropertyChanged)); + + private static void OnMaxWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ControlWidthTrigger)d).UpdateTrigger(); + } + + /// + /// Gets or sets the min size at which to trigger. + /// + public double MinWidth + { + get => (double)GetValue(MinWidthProperty); + set => SetValue(MinWidthProperty, value); + } + + /// + /// Identifies the DependencyProperty. + /// + public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register( + nameof(MinWidth), + typeof(double), + typeof(ControlWidthTrigger), + new PropertyMetadata(0.0, OnMinWidthPropertyChanged)); + + private static void OnMinWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ControlWidthTrigger)d).UpdateTrigger(); + } + + /// + /// Gets or sets the element whose width will observed + /// for the trigger. + /// + public FrameworkElement TargetElement + { + get => (FrameworkElement)GetValue(TargetElementProperty); + set => SetValue(TargetElementProperty, value); + } + + /// + /// Identifies the DependencyProperty. + /// + /// + /// Using a DependencyProperty as the backing store for TargetElement. This enables animation, styling, binding, etc. + /// + public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register( + nameof(TargetElement), + typeof(FrameworkElement), + typeof(ControlWidthTrigger), + new PropertyMetadata(null, OnTargetElementPropertyChanged)); + + private static void OnTargetElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ControlWidthTrigger)d).UpdateTargetElement((FrameworkElement)e.OldValue, (FrameworkElement)e.NewValue); + } + + // Handle event to get current values + private void OnTargetElementSizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateTrigger(); + } + + private void UpdateTargetElement(FrameworkElement oldValue, FrameworkElement newValue) + { + if (oldValue != null) + { + oldValue.SizeChanged -= OnTargetElementSizeChanged; + } + + if (newValue != null) + { + newValue.SizeChanged += OnTargetElementSizeChanged; + } + + UpdateTrigger(); + } + + // Logic to evaluate and apply trigger value + private void UpdateTrigger() + { + if (TargetElement == null || !CanTrigger) + { + SetActive(false); + return; + } + + SetActive(MinWidth <= TargetElement.ActualWidth && TargetElement.ActualWidth < MaxWidth); + } + } +} From abb3d6b3f7aa4aa92939c1ab3f3286d733b2c63d Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 24 Mar 2021 21:07:40 +0000 Subject: [PATCH 002/299] Added automation peer for TokenizingTextBox --- .../TokenizingTextBox/TokenizingTextBox.cs | 12 +- .../TokenizingTextBoxAutomationPeer.cs | 116 ++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs index 3a388c2b0..184d022ef 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs @@ -2,15 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Microsoft.Toolkit.Uwp.Deferred; +using Microsoft.Toolkit.Uwp.UI.Automation.Peers; using Microsoft.Toolkit.Uwp.UI.Helpers; using Windows.System; using Windows.UI.Core; using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; @@ -486,6 +487,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls Text = edit.Text; // Update our text property. } + /// + /// Creates AutomationPeer () + /// + /// An automation peer for this . + protected override AutomationPeer OnCreateAutomationPeer() + { + return new TokenizingTextBoxAutomationPeer(this); + } + /// /// Remove the specified token from the list. /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs new file mode 100644 index 000000000..b2b01bbcb --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.Toolkit.Uwp.UI.Controls; +using Windows.UI.Xaml.Automation; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers +{ + /// + /// Defines a framework element automation peer for the control. + /// + public class TokenizingTextBoxAutomationPeer : ListViewBaseAutomationPeer + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The that is associated with this . + /// + public TokenizingTextBoxAutomationPeer(TokenizingTextBox owner) + : base(owner) + { + } + + private TokenizingTextBox OwningTokenizingTextBox + { + get + { + return Owner as TokenizingTextBox; + } + } + + /// + /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, + /// differentiates the control represented by this AutomationPeer. + /// + /// The string that contains the name. + protected override string GetClassNameCore() + { + return Owner.GetType().Name; + } + + /// + /// Called by GetName. + /// + /// + /// Returns the first of these that is not null or empty: + /// - Value returned by the base implementation + /// - Name of the owning Carousel + /// - Carousel class name + /// + protected override string GetNameCore() + { + string name = this.OwningTokenizingTextBox.Name; + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + name = AutomationProperties.GetName(this.OwningTokenizingTextBox); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + return base.GetNameCore(); + } + + /// + /// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface. + /// + /// A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration. + /// The object that supports the specified pattern, or null if unsupported. + protected override object GetPatternCore(PatternInterface patternInterface) + { + switch (patternInterface) + { + case PatternInterface.Selection: + return this; + } + + return base.GetPatternCore(patternInterface); + } + + /// + /// Gets the collection of elements that are represented in the UI Automation tree as immediate + /// child elements of the automation peer. + /// + /// The children elements. + protected override IList GetChildrenCore() + { + TokenizingTextBox owner = this.OwningTokenizingTextBox; + + ItemCollection items = owner.Items; + if (items.Count <= 0) + { + return null; + } + + List peers = new List(items.Count); + for (int i = 0; i < items.Count; i++) + { + if (owner.ContainerFromIndex(i) is TokenizingTextBoxItem element) + { + peers.Add(FromElement(element) ?? CreatePeerForElement(element)); + } + } + + return peers; + } + } +} From 5844ec01f5e7ba775119adb800fab081c3aedae9 Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 24 Mar 2021 21:24:48 +0000 Subject: [PATCH 003/299] Added tests for TokenizingTextBoxAutomationPeer --- .../TokenizingTextBoxAutomationPeer.cs | 16 ----- .../Test_TokenizingTextBox_AutomationPeer.cs | 59 +++++++++++++++++++ UnitTests/UnitTests.UWP/UnitTests.UWP.csproj | 1 + 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index b2b01bbcb..49cebb14f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -70,22 +70,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers return base.GetNameCore(); } - /// - /// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface. - /// - /// A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration. - /// The object that supports the specified pattern, or null if unsupported. - protected override object GetPatternCore(PatternInterface patternInterface) - { - switch (patternInterface) - { - case PatternInterface.Selection: - return this; - } - - return base.GetPatternCore(patternInterface); - } - /// /// Gets the collection of elements that are represented in the UI Automation tree as immediate /// child elements of the automation peer. diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs new file mode 100644 index 000000000..1f27dbffc --- /dev/null +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Windows.UI.Xaml.Automation; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Windows.UI.Xaml.Automation.Peers; +using Microsoft.Toolkit.Uwp; +using Microsoft.Toolkit.Uwp.UI.Automation.Peers; +using Microsoft.Toolkit.Uwp.UI.Controls; + +namespace UnitTests.UWP.UI.Controls +{ + [TestClass] + [TestCategory("Test_TokenizingTextBox")] + public class Test_TokenizingTextBox_AutomationPeer : VisualUITestBase + { + [TestMethod] + public async Task ShouldConfigureTokenizingTextBoxAutomationPeerAsync() + { + await App.DispatcherQueue.EnqueueAsync(async () => + { + const string expectedAutomationName = "MyAutomationName"; + const string expectedName = "MyName"; + + var items = new ObservableCollection { new() { Title = "Hello" }, new() { Title = "World" } }; + + var tokenizingTextBox = new TokenizingTextBox { ItemsSource = items }; + + await SetTestContentAsync(tokenizingTextBox); + + var tokenizingTextBoxAutomationPeer = + FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer; + + Assert.IsNotNull(tokenizingTextBoxAutomationPeer, "Verify that the AutomationPeer is TokenizingTextBoxAutomationPeer."); + + // Asserts the automation peer name based on the Automation Property Name value. + tokenizingTextBox.SetValue(AutomationProperties.NameProperty, expectedAutomationName); + Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedAutomationName), "Verify that the UIA name contains the given AutomationProperties.Name of the TokenizingTextBox."); + + // Asserts the automation peer name based on the element Name property. + tokenizingTextBox.Name = expectedName; + Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedName), "Verify that the UIA name contains the given Name of the TokenizingTextBox."); + }); + } + + public class TokenizingTextBoxTestItem + { + public string Title { get; set; } + + public override string ToString() + { + return Title; + } + } + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index f5d6d37c4..cc421cdfa 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -200,6 +200,7 @@ + From 4c40b33c6c6ce30fdce5f1d3ceecd285129bde58 Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 24 Mar 2021 21:39:43 +0000 Subject: [PATCH 004/299] Added IValueProvider capability to TokenizingTextBoxAutomationPeer --- .../TokenizingTextBoxAutomationPeer.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index 49cebb14f..a9c69ddf5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Microsoft.Toolkit.Uwp.UI.Controls; using Windows.UI.Xaml.Automation; using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers @@ -13,7 +14,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers /// /// Defines a framework element automation peer for the control. /// - public class TokenizingTextBoxAutomationPeer : ListViewBaseAutomationPeer + public class TokenizingTextBoxAutomationPeer : ListViewBaseAutomationPeer, IValueProvider { /// /// Initializes a new instance of the class. @@ -26,6 +27,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers { } + /// Gets a value that indicates whether the value of a control is read-only. + /// **true** if the value is read-only; **false** if it can be modified. + public bool IsReadOnly => !this.OwningTokenizingTextBox.IsEnabled; + + /// Gets the value of the control. + /// The value of the control. + public string Value => this.OwningTokenizingTextBox.Text; + private TokenizingTextBox OwningTokenizingTextBox { get @@ -34,6 +43,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers } } + /// Sets the value of a control. + /// The value to set. The provider is responsible for converting the value to the appropriate data type. + public void SetValue(string value) + { + this.OwningTokenizingTextBox.Text = value; + } + /// /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType, /// differentiates the control represented by this AutomationPeer. @@ -50,8 +66,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers /// /// Returns the first of these that is not null or empty: /// - Value returned by the base implementation - /// - Name of the owning Carousel - /// - Carousel class name + /// - Name of the owning TokenizingTextBox + /// - TokenizingTextBox class name /// protected override string GetNameCore() { @@ -62,12 +78,22 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers } name = AutomationProperties.GetName(this.OwningTokenizingTextBox); - if (!string.IsNullOrEmpty(name)) - { - return name; - } + return !string.IsNullOrEmpty(name) ? name : base.GetNameCore(); + } - return base.GetNameCore(); + /// + /// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface. + /// + /// A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration. + /// The object that supports the specified pattern, or null if unsupported. + protected override object GetPatternCore(PatternInterface patternInterface) + { + return patternInterface switch + { + PatternInterface.Value => this, + PatternInterface.Selection => this, + _ => base.GetPatternCore(patternInterface) + }; } /// @@ -97,4 +123,4 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers return peers; } } -} +} \ No newline at end of file From 9e1c0783ae95019ac6f84413d123b711051f95af Mon Sep 17 00:00:00 2001 From: James Croft Date: Thu, 25 Mar 2021 21:09:22 +0000 Subject: [PATCH 005/299] Updated tests for value provider changes --- .../TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs | 1 - .../UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index a9c69ddf5..bd07a348a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -91,7 +91,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers return patternInterface switch { PatternInterface.Value => this, - PatternInterface.Selection => this, _ => base.GetPatternCore(patternInterface) }; } diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs index 1f27dbffc..0224222e9 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs @@ -24,6 +24,7 @@ namespace UnitTests.UWP.UI.Controls { const string expectedAutomationName = "MyAutomationName"; const string expectedName = "MyName"; + const string expectedValue = "Wor"; var items = new ObservableCollection { new() { Title = "Hello" }, new() { Title = "World" } }; @@ -43,6 +44,9 @@ namespace UnitTests.UWP.UI.Controls // Asserts the automation peer name based on the element Name property. tokenizingTextBox.Name = expectedName; Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedName), "Verify that the UIA name contains the given Name of the TokenizingTextBox."); + + tokenizingTextBox.Text = expectedValue; + Assert.IsTrue(tokenizingTextBoxAutomationPeer.Value.Equals(expectedValue), "Verify that the Value contains the given Text of the TokenizingTextBox."); }); } From e41c808af080c78220a26c08b38c1051022fe679 Mon Sep 17 00:00:00 2001 From: James Croft Date: Thu, 25 Mar 2021 21:44:18 +0000 Subject: [PATCH 006/299] Added improvement to automation peer to throw ElementNotEnabledException if attempting to update value when in a readonly state --- .../TokenizingTextBoxAutomationPeer.cs | 6 +++++ .../Test_TokenizingTextBox_AutomationPeer.cs | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index bd07a348a..2f8c781ff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -45,8 +45,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers /// Sets the value of a control. /// The value to set. The provider is responsible for converting the value to the appropriate data type. + /// Thrown if the control is in a read-only state. public void SetValue(string value) { + if (IsReadOnly) + { + throw new ElementNotEnabledException($"Could not set the value of the {nameof(TokenizingTextBox)} "); + } + this.OwningTokenizingTextBox.Text = value; } diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs index 0224222e9..497635d4a 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs @@ -45,11 +45,32 @@ namespace UnitTests.UWP.UI.Controls tokenizingTextBox.Name = expectedName; Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedName), "Verify that the UIA name contains the given Name of the TokenizingTextBox."); - tokenizingTextBox.Text = expectedValue; + tokenizingTextBoxAutomationPeer.SetValue(expectedValue); Assert.IsTrue(tokenizingTextBoxAutomationPeer.Value.Equals(expectedValue), "Verify that the Value contains the given Text of the TokenizingTextBox."); }); } + [TestMethod] + public async Task ShouldThrowElementNotEnabledExceptionIfValueSetWhenDisabled() + { + await App.DispatcherQueue.EnqueueAsync(async () => + { + const string expectedValue = "Wor"; + + var tokenizingTextBox = new TokenizingTextBox { IsEnabled = false }; + + await SetTestContentAsync(tokenizingTextBox); + + var tokenizingTextBoxAutomationPeer = + FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer; + + Assert.ThrowsException(() => + { + tokenizingTextBoxAutomationPeer.SetValue(expectedValue); + }); + }); + } + public class TokenizingTextBoxTestItem { public string Title { get; set; } From 299dc32417c006cdd0f84e2780ef86311efe99b0 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 26 Mar 2021 22:23:57 +0000 Subject: [PATCH 007/299] Fixed documentation styling --- .../TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index 2f8c781ff..8ca1bb750 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -27,7 +27,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers { } - /// Gets a value that indicates whether the value of a control is read-only. + /// Gets a value indicating whether the value of a control is read-only. /// **true** if the value is read-only; **false** if it can be modified. public bool IsReadOnly => !this.OwningTokenizingTextBox.IsEnabled; From 461d0f32a12424fc15c55ceb6d5159ef1cf3fc44 Mon Sep 17 00:00:00 2001 From: James Croft Date: Fri, 2 Apr 2021 08:55:35 +0100 Subject: [PATCH 008/299] Apply suggestions from code review Co-authored-by: Rosario Pulella --- .../TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs index 8ca1bb750..5f049a801 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs @@ -78,13 +78,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers protected override string GetNameCore() { string name = this.OwningTokenizingTextBox.Name; - if (!string.IsNullOrEmpty(name)) + if (!string.IsNullOrWhiteSpace(name)) { return name; } name = AutomationProperties.GetName(this.OwningTokenizingTextBox); - return !string.IsNullOrEmpty(name) ? name : base.GetNameCore(); + return !string.IsNullOrWhiteSpace(name) ? name : base.GetNameCore(); } /// @@ -128,4 +128,4 @@ namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers return peers; } } -} \ No newline at end of file +} From 223437cc70519642e547c301e88fdbf816c8f774 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 8 Jul 2021 12:37:40 -0700 Subject: [PATCH 009/299] Deprecated existing ObjectStorage classes and migrated some to the *.Toolkit package, out of *.Toolkit.Uwp. Replaced Local/BaseObjectStorageHelper classes with ApplicationDataStorageHelper. --- .../ApplicationDataStorageHelper.cs | 242 ++++++++++++++++++ .../ObjectStorage/BaseObjectStorageHelper.cs | 1 + .../ObjectStorage/IObjectSerializer.cs | 3 + .../ObjectStorage/IObjectStorageHelper.cs | 2 + .../ObjectStorage/LocalObjectStorageHelper.cs | 2 + .../Helpers/ObjectStorage/SystemSerializer.cs | 1 + .../ObjectStorage/IFileStorageHelper.cs | 57 +++++ .../ObjectStorage/IObjectSerializer.cs | 28 ++ .../ObjectStorage/ISettingsStorageHelper.cs | 75 ++++++ .../Helpers/ObjectStorage/SystemSerializer.cs | 48 ++++ 10 files changed, 459 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs create mode 100644 Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs create mode 100644 Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs create mode 100644 Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs create mode 100644 Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs new file mode 100644 index 000000000..0d646e116 --- /dev/null +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Toolkit.Helpers; +using Windows.Storage; +using Windows.System; + +namespace Microsoft.Toolkit.Uwp.Helpers.ObjectStorage +{ + /// + /// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints. + /// + public class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper + { + /// + /// Get a new instance using ApplicationData.Current and the provided serializer. + /// + /// Serializer for converting stored values. + /// A new instance of ApplicationDataStorageHelper. + public static ApplicationDataStorageHelper GetCurrent(Toolkit.Helpers.IObjectSerializer objectSerializer) + { + var appData = ApplicationData.Current; + return new ApplicationDataStorageHelper(appData, objectSerializer); + } + + /// + /// Get a new instance using the ApplicationData for the provided user and serializer. + /// + /// App data user owner. + /// Serializer for converting stored values. + /// A new instance of ApplicationDataStorageHelper. + public static async Task GetForUserAsync(User user, Toolkit.Helpers.IObjectSerializer objectSerializer) + { + var appData = await ApplicationData.GetForUserAsync(user); + return new ApplicationDataStorageHelper(appData, objectSerializer); + } + + /// + /// Gets the settings container. + /// + protected ApplicationData AppData { get; private set; } + + private ApplicationDataContainer DefaultSettings => AppData.LocalSettings; + + private StorageFolder DefaultFolder => AppData.LocalFolder; + + private readonly Toolkit.Helpers.IObjectSerializer _serializer; + + /// + /// Initializes a new instance of the class. + /// + /// The data store to interact with. + /// Serializer for converting stored values. + public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer objectSerializer) + { + if (appData == null) + { + throw new ArgumentNullException(nameof(appData)); + } + + AppData = appData; + + _serializer = objectSerializer ?? throw new ArgumentNullException(nameof(objectSerializer)); + } + + /// + public bool KeyExists(string key) + { + return DefaultSettings.Values.ContainsKey(key); + } + + /// + public bool KeyExists(string compositeKey, string key) + { + if (KeyExists(compositeKey)) + { + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + if (composite != null) + { + return composite.ContainsKey(key); + } + } + + return false; + } + + /// + public T Read(string key, T @default = default) + { + if (!DefaultSettings.Values.TryGetValue(key, out var value) || value == null) + { + return @default; + } + + return _serializer.Deserialize(value); + } + + /// + public T Read(string compositeKey, string key, T @default = default) + { + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + if (composite != null) + { + string value = (string)composite[key]; + if (value != null) + { + return _serializer.Deserialize(value); + } + } + + return @default; + } + + /// + public void Save(string key, T value) + { + DefaultSettings.Values[key] = _serializer.Serialize(value); + } + + /// + public void Save(string compositeKey, IDictionary values) + { + if (KeyExists(compositeKey)) + { + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + + foreach (KeyValuePair setting in values) + { + if (composite.ContainsKey(setting.Key)) + { + composite[setting.Key] = _serializer.Serialize(setting.Value); + } + else + { + composite.Add(setting.Key, _serializer.Serialize(setting.Value)); + } + } + } + else + { + ApplicationDataCompositeValue composite = new ApplicationDataCompositeValue(); + foreach (KeyValuePair setting in values) + { + composite.Add(setting.Key, _serializer.Serialize(setting.Value)); + } + + DefaultSettings.Values[compositeKey] = composite; + } + } + + /// + public void Delete(string key) + { + DefaultSettings.Values.Remove(key); + } + + /// + public void Delete(string compositeKey, string key) + { + if (KeyExists(compositeKey)) + { + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + composite.Remove(key); + } + } + + /// + public Task FileExistsAsync(string filePath) + { + return FileExistsAsync(DefaultFolder, filePath); + } + + /// + public Task ReadFileAsync(string filePath, T @default = default) + { + return ReadFileAsync(DefaultFolder, filePath, @default); + } + + /// + public Task> ReadFolderAsync(string folderPath) + { + return ReadFolderAsync(DefaultFolder, folderPath); + } + + /// + public Task SaveFileAsync(string filePath, T value) + { + return SaveFileAsync(DefaultFolder, filePath, value); + } + + /// + public Task SaveFolderAsync(string folderPath) + { + return SaveFolderAsync(DefaultFolder, folderPath); + } + + /// + public Task DeleteItemAsync(string itemPath) + { + return DeleteItemAsync(DefaultFolder, itemPath); + } + + private Task FileExistsAsync(StorageFolder folder, string filePath) + { + return folder.FileExistsAsync(filePath); + } + + private async Task ReadFileAsync(StorageFolder folder, string filePath, T @default = default) + { + string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath); + return (value != null) ? _serializer.Deserialize(value) : @default; + } + + private async Task> ReadFolderAsync(StorageFolder folder, string folderPath) + { + var targetFolder = await folder.GetFolderAsync(folderPath); + var files = await targetFolder.GetFilesAsync(); + return files.Select((f) => f.Path + f.Name).ToList(); + } + + private Task SaveFileAsync(StorageFolder folder, string filePath, T value) + { + return StorageFileHelper.WriteTextToFileAsync(folder, _serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); + } + + private async Task SaveFolderAsync(StorageFolder folder, string folderPath) + { + await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists); + } + + private async Task DeleteItemAsync(StorageFolder folder, string itemPath) + { + var item = await folder.GetItemAsync(itemPath); + await item.DeleteAsync(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/BaseObjectStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/BaseObjectStorageHelper.cs index 528b2ca1c..bf111d6aa 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/BaseObjectStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/BaseObjectStorageHelper.cs @@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Shared implementation of ObjectStorageHelper. /// + [Obsolete("BaseObjectStorageHelper is deprecated and has been superceded by ApplicationDataStorageHelper.")] public abstract class BaseObjectStorageHelper : IObjectStorageHelper { private readonly IObjectSerializer serializer; diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs index ca8aa14ec..c995d7b6f 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace Microsoft.Toolkit.Uwp.Helpers { /// /// A basic serialization service. /// + [Obsolete("IObjectSerializer has been migrated to the *.Toolikit package.")] public interface IObjectSerializer { /// diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs index f4e5c861e..39339502e 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Threading.Tasks; using Windows.Storage; @@ -11,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Service used to store data. /// + [Obsolete("IObjectStorageHelper is deprecated. Please use IDictionaryStorageHelper and IFileStorageHelper interfaces instead.")] public interface IObjectStorageHelper { /// diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs index fedf4b806..d7b5d401f 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Windows.Storage; namespace Microsoft.Toolkit.Uwp.Helpers @@ -9,6 +10,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Store data in the Local environment (only on the current device). /// + [Obsolete("LocalObjectStorageHelper is deprecated and has been superceded by the ApplicationDataStorageHelper.")] public class LocalObjectStorageHelper : BaseObjectStorageHelper { /// diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs index 12369315c..0565867bb 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs @@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// A bare-bones serializer which knows how to deal with primitive types and strings only. It will store them directly based on the API. /// It is recommended for more complex scenarios to implement your own based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration /// + [Obsolete("SystemSerializer has been migrated to the *.Toolikit package.")] public class SystemSerializer : IObjectSerializer { /// diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs new file mode 100644 index 000000000..9aff19738 --- /dev/null +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.Toolkit.Helpers +{ + /// + /// Service interface used to store data in files and folders. + /// + public interface IFileStorageHelper + { + /// + /// Determines whether a file already exists. + /// + /// Key of the file (that contains object). + /// True if a value exists. + Task FileExistsAsync(string filePath); + + /// + /// Retrieves an object from a file. + /// + /// Type of object retrieved. + /// Path to the file that contains the object. + /// Default value of the object. + /// Waiting task until completion with the object in the file. + Task ReadFileAsync(string filePath, T @default = default(T)); + + /// + /// Retrieves all file listings for a folder. + /// + /// The path to the target folder. + /// A list of file names in the target folder. + Task> ReadFolderAsync(string folderPath); + + /// + /// Saves an object inside a file. + /// + /// Type of object saved. + /// Path to the file that will contain the object. + /// Object to save. + /// Waiting task until completion. + Task SaveFileAsync(string filePath, T value); + + /// + /// Saves a folder. + /// + /// The path and name of the target folder. + /// Waiting task until completion. + Task SaveFolderAsync(string folderPath); + + /// + /// Deletes a file or folder item. + /// + /// The path to the item for deletion. + /// Waiting task until completion. + Task DeleteItemAsync(string itemPath); + } +} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs new file mode 100644 index 000000000..23095ee45 --- /dev/null +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Helpers +{ + /// + /// A basic serialization service. + /// + public interface IObjectSerializer + { + /// + /// Serialize an object into a string. It is recommended to use strings as the final format for objects. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// The serialized object. + object Serialize(T value); + + /// + /// Deserialize a primitive or string into an object of the given type. + /// + /// The type of the deserialized object. + /// The string to deserialize. + /// The deserialized object. + T Deserialize(object value); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs new file mode 100644 index 000000000..511adbfd5 --- /dev/null +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; + +namespace Microsoft.Toolkit.Helpers +{ + /// + /// Service interface used to store data using key value pairs. + /// + public interface ISettingsStorageHelper + { + /// + /// Determines whether a setting already exists. + /// + /// Key of the setting (that contains object). + /// True if a value exists. + bool KeyExists(string key); + + /// + /// Determines whether a setting already exists in composite. + /// + /// Key of the composite (that contains settings). + /// Key of the setting (that contains object). + /// True if a value exists. + bool KeyExists(string compositeKey, string key); + + /// + /// Retrieves a single item by its key. + /// + /// Type of object retrieved. + /// Key of the object. + /// Default value of the object. + /// The T object + T Read(string key, T @default = default(T)); + + /// + /// Retrieves a single item by its key in composite. + /// + /// Type of object retrieved. + /// Key of the composite (that contains settings). + /// Key of the object. + /// Default value of the object. + /// The T object. + T Read(string compositeKey, string key, T @default = default(T)); + + /// + /// Saves a single item by its key. + /// + /// Type of object saved. + /// Key of the value saved. + /// Object to save. + void Save(string key, T value); + + /// + /// Saves a group of items by its key in a composite. + /// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application + /// and for groups of settings which need to be treated in an atomic way. + /// + /// Type of object saved. + /// Key of the composite (that contains settings). + /// Objects to save. + void Save(string compositeKey, IDictionary values); + + /// + /// Deletes a single item by its key. + /// + /// Key of the object. + void Delete(string key); + + /// + /// Deletes a single item by its key in composite. + /// + /// Key of the composite (that contains settings). + /// Key of the object. + void Delete(string compositeKey, string key); + } +} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs new file mode 100644 index 000000000..06d5c9d3a --- /dev/null +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; + +namespace Microsoft.Toolkit.Helpers +{ + /// + /// A bare-bones serializer which knows how to deal with primitive types and strings only. + /// It is recommended for more complex scenarios to implement your own based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration + /// + public class SystemSerializer : IObjectSerializer + { + /// + /// Take a primitive value from storage and return it as the requested type using the API. + /// + /// Type to convert value to. + /// Value from storage to convert. + /// Deserialized value or default value. + public T Deserialize(object value) + { + var type = typeof(T); + var typeInfo = type.GetTypeInfo(); + + if (typeInfo.IsPrimitive || type == typeof(string)) + { + return (T)Convert.ChangeType(value, type); + } + + return ThrowNotSupportedException(); + + static T ThrowNotSupportedException() => throw new NotSupportedException("This serializer can only handle primitive types and strings. Please implement your own IObjectSerializer for more complex scenarios."); + } + + /// + /// Returns the value so that it can be serialized directly. + /// + /// Type to serialize from. + /// Value to serialize. + /// String representation of value. + public object Serialize(T value) + { + return value; + } + } +} \ No newline at end of file From 5629f4db9969c9aed10276f6fb2910c239f403c6 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 8 Jul 2021 12:39:04 -0700 Subject: [PATCH 010/299] Code style tweak --- .../Helpers/ObjectStorage/ApplicationDataStorageHelper.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 0d646e116..c23a52fa4 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -58,13 +58,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers.ObjectStorage /// Serializer for converting stored values. public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer objectSerializer) { - if (appData == null) - { - throw new ArgumentNullException(nameof(appData)); - } - - AppData = appData; - + AppData = appData ?? throw new ArgumentNullException(nameof(appData)); _serializer = objectSerializer ?? throw new ArgumentNullException(nameof(objectSerializer)); } From c26c3f1851e6378cc8eec6d2c66e3ea2ce46ee87 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 8 Jul 2021 13:21:50 -0700 Subject: [PATCH 011/299] Updated headers and bad namespace --- .../Helpers/ObjectStorage/ApplicationDataStorageHelper.cs | 2 +- Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs | 4 ++++ .../Helpers/ObjectStorage/ISettingsStorageHelper.cs | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index c23a52fa4..2bdf8fa29 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -10,7 +10,7 @@ using Microsoft.Toolkit.Helpers; using Windows.Storage; using Windows.System; -namespace Microsoft.Toolkit.Uwp.Helpers.ObjectStorage +namespace Microsoft.Toolkit.Uwp.Helpers { /// /// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints. diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index 9aff19738..c402dc76b 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Collections.Generic; using System.Threading.Tasks; diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index 511adbfd5..0c54261b2 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Collections.Generic; namespace Microsoft.Toolkit.Helpers From 41da215006f1ef211d4e3c8c45b06ae5b99c05e8 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 8 Jul 2021 13:42:30 -0700 Subject: [PATCH 012/299] Fixed possible null reference warnings --- Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs | 4 ++-- Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs | 2 +- .../Helpers/ObjectStorage/ISettingsStorageHelper.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs index 0fa2dc962..2b84aa706 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs @@ -23,9 +23,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers public sealed class SystemInformation { /// - /// The instance used to save and retrieve application settings. + /// The instance used to save and retrieve application settings. /// - private readonly LocalObjectStorageHelper _localObjectStorageHelper = new(new SystemSerializer()); + private readonly ApplicationDataStorageHelper _localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); /// /// The starting time of the current application session (since app launch or last move to foreground). diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index c402dc76b..03dd4b20c 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -26,7 +26,7 @@ namespace Microsoft.Toolkit.Helpers /// Path to the file that contains the object. /// Default value of the object. /// Waiting task until completion with the object in the file. - Task ReadFileAsync(string filePath, T @default = default(T)); + Task ReadFileAsync(string filePath, T? @default = default); /// /// Retrieves all file listings for a folder. diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index 0c54261b2..af9936120 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -33,7 +33,7 @@ namespace Microsoft.Toolkit.Helpers /// Key of the object. /// Default value of the object. /// The T object - T Read(string key, T @default = default(T)); + T Read(string key, T? @default = default(T)); /// /// Retrieves a single item by its key in composite. @@ -43,7 +43,7 @@ namespace Microsoft.Toolkit.Helpers /// Key of the object. /// Default value of the object. /// The T object. - T Read(string compositeKey, string key, T @default = default(T)); + T Read(string compositeKey, string key, T? @default = default(T)); /// /// Saves a single item by its key. From e809fabcc9fd101f65b7943df3a86481d22449c3 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 8 Jul 2021 14:00:59 -0700 Subject: [PATCH 013/299] Fixed another potential null --- Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs | 2 +- Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs index 23095ee45..d046ead57 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs @@ -15,7 +15,7 @@ namespace Microsoft.Toolkit.Helpers /// The type of the object to serialize. /// The object to serialize. /// The serialized object. - object Serialize(T value); + object? Serialize(T value); /// /// Deserialize a primitive or string into an object of the given type. diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs index 06d5c9d3a..fbf5d9c1d 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs @@ -40,7 +40,7 @@ namespace Microsoft.Toolkit.Helpers /// Type to serialize from. /// Value to serialize. /// String representation of value. - public object Serialize(T value) + public object? Serialize(T value) { return value; } From 4eaceec5d21eb9949a68eb6b5ea5da25739e048f Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Fri, 9 Jul 2021 11:59:29 -0700 Subject: [PATCH 014/299] Updated storage related unit tests --- UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs | 2 +- .../UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs | 2 +- UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs | 8 ++++---- UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index 35551ee74..c0d492467 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -4,7 +4,7 @@ using System; using System.Reflection; -using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.Toolkit.Helpers; using Newtonsoft.Json; namespace UnitTests.UWP.Helpers diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs index 139f16135..7871089f0 100644 --- a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs @@ -4,7 +4,7 @@ using System; using System.Text.Json; -using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.Toolkit.Helpers; namespace UnitTests.UWP.Helpers { diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index 823753060..4c4cede0e 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; @@ -14,10 +15,9 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { - private LocalObjectStorageHelper _localStorageHelperSystem = new LocalObjectStorageHelper(new SystemSerializer()); - private LocalObjectStorageHelper _localStorageHelperJsonCompat = new LocalObjectStorageHelper(new JsonObjectSerializer()); - - private LocalObjectStorageHelper _localStorageHelperJsonNew = new LocalObjectStorageHelper(new SystemTextJsonSerializer()); + private ISettingsStorageHelper _localStorageHelperSystem = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); + private ISettingsStorageHelper _localStorageHelperJsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); + private ISettingsStorageHelper _localStorageHelperJsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); /// /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs index 78f123789..27488ac1a 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using Windows.ApplicationModel; @@ -57,7 +58,7 @@ namespace UnitTests.Helpers // Simulate a first app startup _ = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true); - LocalObjectStorageHelper localObjectStorageHelper = new(new SystemSerializer()); + ISettingsStorageHelper localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); PackageVersion previousVersion = new() { Build = 42, Major = 1111, Minor = 2222, Revision = 12345 }; localObjectStorageHelper.Save("currentVersion", previousVersion.ToFormattedString()); From be6555d9df7c3cfe534b9a6d6a9fc47b86a6aaef Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Fri, 9 Jul 2021 12:30:54 -0700 Subject: [PATCH 015/299] Updated ambiguous references in comments --- UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index 4c4cede0e..09714a263 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -50,7 +50,7 @@ namespace UnitTests.Helpers } /// - /// If we try and deserialize a complex type with the , we do a check ourselves and will throw our own exception. + /// If we try and deserialize a complex type with the , we do a check ourselves and will throw our own exception. /// [TestCategory("Helpers")] [TestMethod] @@ -68,7 +68,7 @@ namespace UnitTests.Helpers } /// - /// The doesn't support complex types, since it just passes through directly. + /// The doesn't support complex types, since it just passes through directly. /// We'll get the argument exception from the API. /// [TestCategory("Helpers")] From d9e43fb4edf5d6d61acdb3dd9d8376053e0265eb Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 12 Jul 2021 14:01:42 -0700 Subject: [PATCH 016/299] Updated ObjectStorage references in samples --- .../Controls/SampleAppMarkdownRenderer.cs | 2 +- .../Models/Sample.cs | 2 +- .../Models/Samples.cs | 2 +- .../Object Storage/ObjectStorageCode.bind | 19 +++++++++---------- .../Object Storage/ObjectStoragePage.xaml.cs | 3 ++- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs index 715a6668d..c3edb07a1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs @@ -417,7 +417,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls /// /// The Local Storage Helper. /// - private LocalObjectStorageHelper storage = new LocalObjectStorageHelper(new SystemSerializer()); + private ApplicationDataStorageHelper storage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); /// /// DocFX note types and styling info, keyed by identifier. diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs index f57a430d3..ad4587b11 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -44,7 +44,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp public static async void EnsureCacheLatest() { - var settingsStorage = new LocalObjectStorageHelper(new SystemSerializer()); + var settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); var onlineDocsSHA = await GetDocsSHA(); var cacheSHA = settingsStorage.Read(_cacheSHAKey); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs index 8105e859b..c99bc11b8 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs @@ -21,7 +21,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp private static SemaphoreSlim _semaphore = new SemaphoreSlim(1); private static LinkedList _recentSamples; - private static LocalObjectStorageHelper _localObjectStorageHelper = new LocalObjectStorageHelper(new SystemSerializer()); + private static ApplicationDataStorageHelper _localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); public static async Task GetCategoryBySample(Sample sample) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStorageCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStorageCode.bind index 50d77bc5e..0b0c4d926 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStorageCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStorageCode.bind @@ -1,25 +1,24 @@ -var localObjectStorageHelper = new LocalObjectStorageHelper(); -var roamingObjectStorageHelper = new RoamingObjectStorageHelper(); +ApplicationDataStorageHelper appDataStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); // Read and Save with simple objects string keySimpleObject = "simple"; -string result = localObjectStorageHelper.Read(keySimpleObject); -localObjectStorageHelper.Save(keySimpleObject, 47); +string result = appDataStorageHelper.Read(keySimpleObject); +appDataStorageHelper.Save(keySimpleObject, 47); // Read and Save with complex/large objects -string keyLargeObject = "large"; -var result = localObjectStorageHelper.ReadFileAsync(keyLargeObject); +string complexObjectKey = "complexObject"; +var complexObject = await appDataStorageHelper.ReadFileAsync(complexObjectKey); -var o = new MyLargeObject +var myComplexObject = new MyComplexObject() { ... }; -localObjectStorageHelper.SaveFileAsync(keySimpleObject, o); +await appDataStorageHelper.SaveFileAsync(complexObjectKey, myComplexObject); // Complex object -public class MyLargeObject +public class MyComplexObject { public string MyContent { get; set; } public List MyContents { get; set; } - public List MyObjects { get; set; } + public List MyObjects { get; set; } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs index e2da83f53..e1e0e925b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; using Windows.UI.Xaml; @@ -9,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { public sealed partial class ObjectStoragePage { - private readonly IObjectStorageHelper localStorageHelper = new LocalObjectStorageHelper(new SystemSerializer()); + private readonly ISettingsStorageHelper localStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); public ObjectStoragePage() { From c6184edf507d0b162b0659788c6373fdeedc68b8 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 13 Jul 2021 15:26:54 -0700 Subject: [PATCH 017/299] Update Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs Co-authored-by: Michael Hawker MSFT (XAML Llama) <24302614+michael-hawker@users.noreply.github.com> --- .../Helpers/ObjectStorage/IObjectSerializer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs index c995d7b6f..0f1bfa4d5 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectSerializer.cs @@ -9,7 +9,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// A basic serialization service. /// - [Obsolete("IObjectSerializer has been migrated to the *.Toolikit package.")] + [Obsolete("IObjectSerializer has been migrated to the Microsoft.Toolkit (CommunityToolkit.Common) package.")] public interface IObjectSerializer { /// @@ -28,4 +28,4 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// The deserialized object. T Deserialize(object value); } -} \ No newline at end of file +} From 870c85f32f703aff050748a94432762b79b8697d Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 13 Jul 2021 15:27:38 -0700 Subject: [PATCH 018/299] Update Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs Co-authored-by: Michael Hawker MSFT (XAML Llama) <24302614+michael-hawker@users.noreply.github.com> --- .../Helpers/ObjectStorage/SystemSerializer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs index 0565867bb..7c6ad802c 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/SystemSerializer.cs @@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// A bare-bones serializer which knows how to deal with primitive types and strings only. It will store them directly based on the API. /// It is recommended for more complex scenarios to implement your own based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration /// - [Obsolete("SystemSerializer has been migrated to the *.Toolikit package.")] + [Obsolete("SystemSerializer has been migrated to the Microsoft.Toolkit (CommunityToolkit.Common) package.")] public class SystemSerializer : IObjectSerializer { /// @@ -47,4 +47,4 @@ namespace Microsoft.Toolkit.Uwp.Helpers return value; } } -} \ No newline at end of file +} From e36c95523fcfaa75d2d3035c44b7288e8b5cbfb9 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Wed, 14 Jul 2021 16:05:10 -0700 Subject: [PATCH 019/299] PR updates --- .../ApplicationDataStorageHelper.cs | 42 +++++++++++++++---- .../ObjectStorage/LocalObjectStorageHelper.cs | 2 +- .../ObjectStorage/DirectoryItemType.cs | 27 ++++++++++++ .../ObjectStorage/IFileStorageHelper.cs | 24 +++++++---- 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 2bdf8fa29..59a20edcf 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -164,9 +164,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public Task FileExistsAsync(string filePath) + public Task ItemExistsAsync(string itemName) { - return FileExistsAsync(DefaultFolder, filePath); + return ItemExistsAsync(DefaultFolder, itemName); } /// @@ -176,7 +176,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public Task> ReadFolderAsync(string folderPath) + public Task>> ReadFolderAsync(string folderPath) { return ReadFolderAsync(DefaultFolder, folderPath); } @@ -199,9 +199,27 @@ namespace Microsoft.Toolkit.Uwp.Helpers return DeleteItemAsync(DefaultFolder, itemPath); } - private Task FileExistsAsync(StorageFolder folder, string filePath) + /// + /// Determine the existance of a file at the specified path. + /// To check for folders, use . + /// + /// The name of the file. + /// Whether the file should be searched for recursively. + /// A task with the result of the file query. + public Task FileExistsAsync(string fileName, bool isRecursive = false) { - return folder.FileExistsAsync(filePath); + return FileExistsAsync(DefaultFolder, fileName, isRecursive); + } + + private async Task ItemExistsAsync(StorageFolder folder, string itemName) + { + var item = await folder.TryGetItemAsync(itemName); + return item != null; + } + + private Task FileExistsAsync(StorageFolder folder, string fileName, bool isRecursive) + { + return folder.FileExistsAsync(fileName, isRecursive); } private async Task ReadFileAsync(StorageFolder folder, string filePath, T @default = default) @@ -210,11 +228,19 @@ namespace Microsoft.Toolkit.Uwp.Helpers return (value != null) ? _serializer.Deserialize(value) : @default; } - private async Task> ReadFolderAsync(StorageFolder folder, string folderPath) + private async Task>> ReadFolderAsync(StorageFolder folder, string folderPath) { var targetFolder = await folder.GetFolderAsync(folderPath); - var files = await targetFolder.GetFilesAsync(); - return files.Select((f) => f.Path + f.Name).ToList(); + var items = await targetFolder.GetItemsAsync(); + + return items.Select((item) => + { + var itemType = item.IsOfType(StorageItemTypes.File) ? DirectoryItemType.File + : item.IsOfType(StorageItemTypes.Folder) ? DirectoryItemType.Folder + : DirectoryItemType.None; + + return new Tuple(itemType, item.Name); + }).ToList(); } private Task SaveFileAsync(StorageFolder folder, string filePath, T value) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs index d7b5d401f..45905f730 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/LocalObjectStorageHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Store data in the Local environment (only on the current device). /// - [Obsolete("LocalObjectStorageHelper is deprecated and has been superceded by the ApplicationDataStorageHelper.")] + [Obsolete("LocalObjectStorageHelper is deprecated and has been superceded by the ApplicationDataStorageHelper. To upgrade, simply swap any LocalObjectStorageHelper instances with ApplicationDataStorageHelper.GetCurrent(serializer). The underlying interfaces are nearly identical but now with even more features available, such as deletion and access to user specific data stores!")] public class LocalObjectStorageHelper : BaseObjectStorageHelper { /// diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs new file mode 100644 index 000000000..150fa3770 --- /dev/null +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Helpers +{ + /// + /// Represents the types of items available in a directory. + /// + public enum DirectoryItemType + { + /// + /// The item is neither a file or a folder. + /// + None, + + /// + /// Represents a file type item. + /// + File, + + /// + /// Represents a folder type item. + /// + Folder + } +} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index 03dd4b20c..3835c73aa 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -2,22 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Microsoft.Toolkit.Helpers { /// - /// Service interface used to store data in files and folders. + /// Service interface used to store data in a directory/file-system via files and folders. + /// + /// This interface is meant to help abstract file storage operations across platforms in a library, + /// but the actual behavior will be up to the implementer. Such as, we don't provide a sense of a current directory, + /// so an implementor should consider using full paths to support any file operations. Otherwise, a "directory aware" + /// implementation could be achieved with a current directory field and traversal functions, in which case relative paths would be applicable. /// public interface IFileStorageHelper { /// - /// Determines whether a file already exists. + /// Determines if a directory item already exists. /// - /// Key of the file (that contains object). - /// True if a value exists. - Task FileExistsAsync(string filePath); + /// Key of the file. + /// True if an item exists. + Task ItemExistsAsync(string itemName); /// /// Retrieves an object from a file. @@ -29,11 +35,11 @@ namespace Microsoft.Toolkit.Helpers Task ReadFileAsync(string filePath, T? @default = default); /// - /// Retrieves all file listings for a folder. + /// Retrieves the listings for a folder and the item types. /// /// The path to the target folder. - /// A list of file names in the target folder. - Task> ReadFolderAsync(string folderPath); + /// A list of file types and names in the target folder. + Task>> ReadFolderAsync(string folderPath); /// /// Saves an object inside a file. @@ -45,7 +51,7 @@ namespace Microsoft.Toolkit.Helpers Task SaveFileAsync(string filePath, T value); /// - /// Saves a folder. + /// Ensure a folder exists at the folder path specified. /// /// The path and name of the target folder. /// Waiting task until completion. From 7edade152609516a0615d687fd9ce9e0b507caf7 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Wed, 14 Jul 2021 16:23:37 -0700 Subject: [PATCH 020/299] Renamed SaveFile/FolderAsync to CreateFile/FolderAsync --- .../ObjectStorage/ApplicationDataStorageHelper.cs | 12 ++++++------ .../Helpers/ObjectStorage/IFileStorageHelper.cs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 59a20edcf..58549983d 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -182,15 +182,15 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public Task SaveFileAsync(string filePath, T value) + public Task CreateFileAsync(string filePath, T value) { - return SaveFileAsync(DefaultFolder, filePath, value); + return CreateFileAsync(DefaultFolder, filePath, value); } /// - public Task SaveFolderAsync(string folderPath) + public Task CreateFolderAsync(string folderPath) { - return SaveFolderAsync(DefaultFolder, folderPath); + return CreateFolderAsync(DefaultFolder, folderPath); } /// @@ -243,12 +243,12 @@ namespace Microsoft.Toolkit.Uwp.Helpers }).ToList(); } - private Task SaveFileAsync(StorageFolder folder, string filePath, T value) + private Task CreateFileAsync(StorageFolder folder, string filePath, T value) { return StorageFileHelper.WriteTextToFileAsync(folder, _serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); } - private async Task SaveFolderAsync(StorageFolder folder, string folderPath) + private async Task CreateFolderAsync(StorageFolder folder, string folderPath) { await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists); } diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index 3835c73aa..eae8a3dfb 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -48,14 +48,14 @@ namespace Microsoft.Toolkit.Helpers /// Path to the file that will contain the object. /// Object to save. /// Waiting task until completion. - Task SaveFileAsync(string filePath, T value); + Task CreateFileAsync(string filePath, T value); /// /// Ensure a folder exists at the folder path specified. /// /// The path and name of the target folder. /// Waiting task until completion. - Task SaveFolderAsync(string folderPath); + Task CreateFolderAsync(string folderPath); /// /// Deletes a file or folder item. From bf97b451759591fd1c111a693ca83905cae46a79 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Wed, 14 Jul 2021 16:37:28 -0700 Subject: [PATCH 021/299] Readded SaveFileAsync to match previous implementation --- .../ApplicationDataStorageHelper.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 58549983d..fcd54e9c0 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -184,7 +184,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public Task CreateFileAsync(string filePath, T value) { - return CreateFileAsync(DefaultFolder, filePath, value); + return SaveFileAsync(DefaultFolder, filePath, value); } /// @@ -211,6 +211,18 @@ namespace Microsoft.Toolkit.Uwp.Helpers return FileExistsAsync(DefaultFolder, fileName, isRecursive); } + /// + /// Saves an object inside a file. + /// + /// Type of object saved. + /// Path to the file that will contain the object. + /// Object to save. + /// Waiting task until completion. + public Task SaveFileAsync(string filePath, T value) + { + return SaveFileAsync(DefaultFolder, filePath, value); + } + private async Task ItemExistsAsync(StorageFolder folder, string itemName) { var item = await folder.TryGetItemAsync(itemName); @@ -243,7 +255,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers }).ToList(); } - private Task CreateFileAsync(StorageFolder folder, string filePath, T value) + private Task SaveFileAsync(StorageFolder folder, string filePath, T value) { return StorageFileHelper.WriteTextToFileAsync(folder, _serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); } From 5707c6d6693298f6c1ecad205b0597d9806a8d1c Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Fri, 16 Jul 2021 09:11:20 -0700 Subject: [PATCH 022/299] Fixed typo in IObjectStorageHelper. Updated var name to be more accurate in Samples.cs --- Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs | 6 +++--- .../Helpers/ObjectStorage/IObjectStorageHelper.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs index c99bc11b8..f7e096772 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs @@ -21,7 +21,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp private static SemaphoreSlim _semaphore = new SemaphoreSlim(1); private static LinkedList _recentSamples; - private static ApplicationDataStorageHelper _localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private static ApplicationDataStorageHelper _appDataStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); public static async Task GetCategoryBySample(Sample sample) { @@ -98,7 +98,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp if (_recentSamples == null) { _recentSamples = new LinkedList(); - var savedSamples = _localObjectStorageHelper.Read(_recentSamplesStorageKey); + var savedSamples = _appDataStorageHelper.Read(_recentSamplesStorageKey); if (savedSamples != null) { @@ -144,7 +144,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp } var str = string.Join(";", _recentSamples.Take(10).Select(s => s.Name).ToArray()); - _localObjectStorageHelper.Save(_recentSamplesStorageKey, str); + _appDataStorageHelper.Save(_recentSamplesStorageKey, str); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs index 39339502e..e742fe185 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/IObjectStorageHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Service used to store data. /// - [Obsolete("IObjectStorageHelper is deprecated. Please use IDictionaryStorageHelper and IFileStorageHelper interfaces instead.")] + [Obsolete("IObjectStorageHelper is deprecated. Please use ISettingsStorageHelper and IFileStorageHelper interfaces instead.")] public interface IObjectStorageHelper { /// From f70e3696bed16887126fd5687a125b5b4cc3758b Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 19 Jul 2021 09:51:49 -0700 Subject: [PATCH 023/299] Updated storage var names to be consistent across solution --- .../Controls/SampleAppMarkdownRenderer.cs | 9 +-- .../Models/Sample.cs | 3 +- .../Models/Samples.cs | 7 ++- .../Object Storage/ObjectStoragePage.xaml.cs | 8 +-- .../Helpers/SystemInformation.cs | 57 ++++++++++--------- .../Helpers/Test_StorageHelper.cs | 36 ++++++------ .../Helpers/Test_SystemInformation.cs | 4 +- 7 files changed, 64 insertions(+), 60 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs index c3edb07a1..4085cd815 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Parsers.Markdown; using Microsoft.Toolkit.Parsers.Markdown.Blocks; using Microsoft.Toolkit.Parsers.Markdown.Inlines; @@ -405,19 +406,19 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls { get { - return storage.Read(DesiredLangKey); + return settingsStorage.Read(DesiredLangKey); } set { - storage.Save(DesiredLangKey, value); + settingsStorage.Save(DesiredLangKey, value); } } /// - /// The Local Storage Helper. + /// The local app data storage helper for storing settings. /// - private ApplicationDataStorageHelper storage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); /// /// DocFX note types and styling info, keyed by identifier. diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs index d15a54ce2..e79280195 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -20,6 +20,7 @@ using System.Threading.Tasks; // TODO Reintroduce graph controls // using Microsoft.Toolkit.Graph.Converters; // using Microsoft.Toolkit.Graph.Providers; +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; using Microsoft.Toolkit.Uwp.Input.GazeInteraction; using Microsoft.Toolkit.Uwp.SampleApp.Models; @@ -45,7 +46,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp public static async void EnsureCacheLatest() { - var settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); var onlineDocsSHA = await GetDocsSHA(); var cacheSHA = settingsStorage.Read(_cacheSHAKey); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs index f7e096772..c43cf3aeb 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; namespace Microsoft.Toolkit.Uwp.SampleApp @@ -21,7 +22,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp private static SemaphoreSlim _semaphore = new SemaphoreSlim(1); private static LinkedList _recentSamples; - private static ApplicationDataStorageHelper _appDataStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private static ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); public static async Task GetCategoryBySample(Sample sample) { @@ -98,7 +99,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp if (_recentSamples == null) { _recentSamples = new LinkedList(); - var savedSamples = _appDataStorageHelper.Read(_recentSamplesStorageKey); + var savedSamples = _settingsStorage.Read(_recentSamplesStorageKey); if (savedSamples != null) { @@ -144,7 +145,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp } var str = string.Join(";", _recentSamples.Take(10).Select(s => s.Name).ToArray()); - _appDataStorageHelper.Save(_recentSamplesStorageKey, str); + _settingsStorage.Save(_recentSamplesStorageKey, str); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs index e1e0e925b..96079fb89 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs @@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { public sealed partial class ObjectStoragePage { - private readonly ISettingsStorageHelper localStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private readonly ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); public ObjectStoragePage() { @@ -25,9 +25,9 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages } // Read from local storage - if (localStorageHelper.KeyExists(KeyTextBox.Text)) + if (_settingsStorage.KeyExists(KeyTextBox.Text)) { - ContentTextBox.Text = localStorageHelper.Read(KeyTextBox.Text); + ContentTextBox.Text = _settingsStorage.Read(KeyTextBox.Text); } } @@ -44,7 +44,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages } // Save into local storage - localStorageHelper.Save(KeyTextBox.Text, ContentTextBox.Text); + _settingsStorage.Save(KeyTextBox.Text, ContentTextBox.Text); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs index 2b84aa706..81f7a6cac 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Microsoft.Toolkit.Helpers; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.Security.ExchangeActiveSyncProvisioning; @@ -25,7 +26,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// The instance used to save and retrieve application settings. /// - private readonly ApplicationDataStorageHelper _localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private readonly ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); /// /// The starting time of the current application session (since app launch or last move to foreground). @@ -216,7 +217,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers if (LaunchCount > 0) { var subSessionLength = DateTime.UtcNow.Subtract(_sessionStart).Ticks; - var uptimeSoFar = _localObjectStorageHelper.Read(nameof(AppUptime)); + var uptimeSoFar = _settingsStorage.Read(nameof(AppUptime)); return new(uptimeSoFar + subSessionLength); } @@ -232,9 +233,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// The amount to time to add public void AddToAppUptime(TimeSpan duration) { - var uptimeSoFar = _localObjectStorageHelper.Read(nameof(AppUptime)); + var uptimeSoFar = _settingsStorage.Read(nameof(AppUptime)); - _localObjectStorageHelper.Save(nameof(AppUptime), uptimeSoFar + duration.Ticks); + _settingsStorage.Save(nameof(AppUptime), uptimeSoFar + duration.Ticks); } /// @@ -245,8 +246,8 @@ namespace Microsoft.Toolkit.Uwp.Helpers LastResetTime = DateTime.UtcNow; LaunchCount = 0; - _localObjectStorageHelper.Save(nameof(LastResetTime), LastResetTime.ToFileTimeUtc()); - _localObjectStorageHelper.Save(nameof(LaunchCount), LaunchCount); + _settingsStorage.Save(nameof(LastResetTime), LastResetTime.ToFileTimeUtc()); + _settingsStorage.Save(nameof(LaunchCount), LaunchCount); } /// @@ -258,8 +259,8 @@ namespace Microsoft.Toolkit.Uwp.Helpers { if (args.PreviousExecutionState is ApplicationExecutionState.ClosedByUser or ApplicationExecutionState.NotRunning) { - LaunchCount = _localObjectStorageHelper.Read(nameof(LaunchCount)) + 1; - TotalLaunchCount = _localObjectStorageHelper.Read(nameof(TotalLaunchCount)) + 1; + LaunchCount = _settingsStorage.Read(nameof(LaunchCount)) + 1; + TotalLaunchCount = _settingsStorage.Read(nameof(TotalLaunchCount)) + 1; // In case we upgraded the properties, make TotalLaunchCount is correct if (TotalLaunchCount < LaunchCount) @@ -267,21 +268,21 @@ namespace Microsoft.Toolkit.Uwp.Helpers TotalLaunchCount = LaunchCount; } - _localObjectStorageHelper.Save(nameof(LaunchCount), LaunchCount); - _localObjectStorageHelper.Save(nameof(TotalLaunchCount), TotalLaunchCount); + _settingsStorage.Save(nameof(LaunchCount), LaunchCount); + _settingsStorage.Save(nameof(TotalLaunchCount), TotalLaunchCount); LaunchTime = DateTime.UtcNow; - var lastLaunch = _localObjectStorageHelper.Read(nameof(LastLaunchTime)); + var lastLaunch = _settingsStorage.Read(nameof(LastLaunchTime)); LastLaunchTime = lastLaunch != 0 ? DateTime.FromFileTimeUtc(lastLaunch) : LaunchTime; - _localObjectStorageHelper.Save(nameof(LastLaunchTime), LaunchTime.ToFileTimeUtc()); - _localObjectStorageHelper.Save(nameof(AppUptime), 0L); + _settingsStorage.Save(nameof(LastLaunchTime), LaunchTime.ToFileTimeUtc()); + _settingsStorage.Save(nameof(AppUptime), 0L); - var lastResetTime = _localObjectStorageHelper.Read(nameof(LastResetTime)); + var lastResetTime = _settingsStorage.Read(nameof(LastResetTime)); LastResetTime = lastResetTime != 0 ? DateTime.FromFileTimeUtc(lastResetTime) @@ -321,20 +322,20 @@ namespace Microsoft.Toolkit.Uwp.Helpers else { var subSessionLength = DateTime.UtcNow.Subtract(_sessionStart).Ticks; - var uptimeSoFar = _localObjectStorageHelper.Read(nameof(AppUptime)); + var uptimeSoFar = _settingsStorage.Read(nameof(AppUptime)); - _localObjectStorageHelper.Save(nameof(AppUptime), uptimeSoFar + subSessionLength); + _settingsStorage.Save(nameof(AppUptime), uptimeSoFar + subSessionLength); } } private bool DetectIfFirstUse() { - if (_localObjectStorageHelper.KeyExists(nameof(IsFirstRun))) + if (_settingsStorage.KeyExists(nameof(IsFirstRun))) { return false; } - _localObjectStorageHelper.Save(nameof(IsFirstRun), true); + _settingsStorage.Save(nameof(IsFirstRun), true); return true; } @@ -347,13 +348,13 @@ namespace Microsoft.Toolkit.Uwp.Helpers // is ever called. That is, this is either the first time the app has been launched, or the first // time a previously existing app has run this method (or has run it after a new update of the app). // In this case, save the current version and report the same version as previous version installed. - if (!_localObjectStorageHelper.KeyExists(nameof(currentVersion))) + if (!_settingsStorage.KeyExists(nameof(currentVersion))) { - _localObjectStorageHelper.Save(nameof(currentVersion), currentVersion); + _settingsStorage.Save(nameof(currentVersion), currentVersion); } else { - var previousVersion = _localObjectStorageHelper.Read(nameof(currentVersion)); + var previousVersion = _settingsStorage.Read(nameof(currentVersion)); // There are two possible cases if the "currentVersion" key exists: // 1) The previous version is different than the current one. This means that the application @@ -363,7 +364,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers // In this case we have nothing to do and just return the previous version installed to be the same. if (currentVersion != previousVersion) { - _localObjectStorageHelper.Save(nameof(currentVersion), currentVersion); + _settingsStorage.Save(nameof(currentVersion), currentVersion); return (true, previousVersion.ToPackageVersion()); } @@ -374,28 +375,28 @@ namespace Microsoft.Toolkit.Uwp.Helpers private DateTime DetectFirstUseTime() { - if (_localObjectStorageHelper.KeyExists(nameof(FirstUseTime))) + if (_settingsStorage.KeyExists(nameof(FirstUseTime))) { - var firstUse = _localObjectStorageHelper.Read(nameof(FirstUseTime)); + var firstUse = _settingsStorage.Read(nameof(FirstUseTime)); return DateTime.FromFileTimeUtc(firstUse); } DateTime utcNow = DateTime.UtcNow; - _localObjectStorageHelper.Save(nameof(FirstUseTime), utcNow.ToFileTimeUtc()); + _settingsStorage.Save(nameof(FirstUseTime), utcNow.ToFileTimeUtc()); return utcNow; } private PackageVersion DetectFirstVersionInstalled() { - if (_localObjectStorageHelper.KeyExists(nameof(FirstVersionInstalled))) + if (_settingsStorage.KeyExists(nameof(FirstVersionInstalled))) { - return _localObjectStorageHelper.Read(nameof(FirstVersionInstalled)).ToPackageVersion(); + return _settingsStorage.Read(nameof(FirstVersionInstalled)).ToPackageVersion(); } - _localObjectStorageHelper.Save(nameof(FirstVersionInstalled), ApplicationVersion.ToFormattedString()); + _settingsStorage.Save(nameof(FirstVersionInstalled), ApplicationVersion.ToFormattedString()); return ApplicationVersion; } diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index 09714a263..0b27db815 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -15,9 +15,9 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { - private ISettingsStorageHelper _localStorageHelperSystem = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); - private ISettingsStorageHelper _localStorageHelperJsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); - private ISettingsStorageHelper _localStorageHelperJsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); + private ISettingsStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); + private ISettingsStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); + private ISettingsStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); /// /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. @@ -41,10 +41,10 @@ namespace UnitTests.Helpers int input = 42; // Use our previous Json layer to store value - _localStorageHelperJsonCompat.Save(key, input); + _settingsStorage_JsonCompat.Save(key, input); // But try and read from our new system to see if it works - int output = _localStorageHelperSystem.Read(key, 0); + int output = _settingsStorage_System.Read(key, 0); Assert.AreEqual(input, output); } @@ -61,10 +61,10 @@ namespace UnitTests.Helpers DateTime input = new DateTime(2017, 12, 25); - _localStorageHelperJsonCompat.Save(key, input); + _settingsStorage_JsonCompat.Save(key, input); // now read it as int to valid that the change works - DateTime output = _localStorageHelperSystem.Read(key, DateTime.Today); + DateTime output = _settingsStorage_System.Read(key, DateTime.Today); } /// @@ -81,7 +81,7 @@ namespace UnitTests.Helpers // as local and online platforms seem to throw different exception types :( try { - _localStorageHelperSystem.Save("Today", DateTime.Today); + _settingsStorage_System.Save("Today", DateTime.Today); } catch (Exception exception) { @@ -100,10 +100,10 @@ namespace UnitTests.Helpers UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; // simulate previous version by generating json and manually inserting it as string - _localStorageHelperJsonCompat.Save(key, input); + _settingsStorage_JsonCompat.Save(key, input); // now read it as int to valid that the change works - UI.Person output = _localStorageHelperJsonCompat.Read(key, null); + UI.Person output = _settingsStorage_JsonCompat.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); @@ -120,10 +120,10 @@ namespace UnitTests.Helpers UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; // simulate previous version by generating json and manually inserting it as string - _localStorageHelperJsonCompat.Save(key, input); + _settingsStorage_JsonCompat.Save(key, input); // now read it as int to valid that the change works - Person output = _localStorageHelperJsonCompat.Read(key, null); + Person output = _settingsStorage_JsonCompat.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); @@ -138,10 +138,10 @@ namespace UnitTests.Helpers int input = 42; - _localStorageHelperSystem.Save(key, input); + _settingsStorage_System.Save(key, input); // now read it as int to valid that the change works - int output = _localStorageHelperSystem.Read(key, 0); + int output = _settingsStorage_System.Read(key, 0); Assert.AreEqual(input, output); } @@ -154,10 +154,10 @@ namespace UnitTests.Helpers DateTime input = new DateTime(2017, 12, 25); - _localStorageHelperJsonNew.Save(key, input); + _settingsStorage_JsonNew.Save(key, input); // now read it as int to valid that the change works - DateTime output = _localStorageHelperJsonNew.Read(key, DateTime.Today); + DateTime output = _settingsStorage_JsonNew.Read(key, DateTime.Today); Assert.AreEqual(input, output); } @@ -170,10 +170,10 @@ namespace UnitTests.Helpers Person input = new Person() { Name = "Joe Bloggs", Age = 42 }; - _localStorageHelperJsonNew.Save(key, input); + _settingsStorage_JsonNew.Save(key, input); // now read it as int to valid that the change works - Person output = _localStorageHelperJsonNew.Read(key, null); + Person output = _settingsStorage_JsonNew.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs index 27488ac1a..7132f500a 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs @@ -58,10 +58,10 @@ namespace UnitTests.Helpers // Simulate a first app startup _ = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true); - ISettingsStorageHelper localObjectStorageHelper = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); + ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); PackageVersion previousVersion = new() { Build = 42, Major = 1111, Minor = 2222, Revision = 12345 }; - localObjectStorageHelper.Save("currentVersion", previousVersion.ToFormattedString()); + settingsStorage.Save("currentVersion", previousVersion.ToFormattedString()); var systemInformation = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true); var currentAppVersion = Package.Current.Id.Version; From 1f53d9a79eeead70c6c6ba9868e009bb3ad02797 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 19 Jul 2021 13:09:09 -0700 Subject: [PATCH 024/299] Making serializer optional for ApplicationDataStorageHelper and defaulting to SystemSerializer --- .../ObjectStorage/ApplicationDataStorageHelper.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index fcd54e9c0..97b38035d 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -20,9 +20,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Get a new instance using ApplicationData.Current and the provided serializer. /// - /// Serializer for converting stored values. + /// Serializer for converting stored values. Defaults to . /// A new instance of ApplicationDataStorageHelper. - public static ApplicationDataStorageHelper GetCurrent(Toolkit.Helpers.IObjectSerializer objectSerializer) + public static ApplicationDataStorageHelper GetCurrent(Toolkit.Helpers.IObjectSerializer objectSerializer = null) { var appData = ApplicationData.Current; return new ApplicationDataStorageHelper(appData, objectSerializer); @@ -32,9 +32,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// Get a new instance using the ApplicationData for the provided user and serializer. /// /// App data user owner. - /// Serializer for converting stored values. + /// Serializer for converting stored values. Defaults to . /// A new instance of ApplicationDataStorageHelper. - public static async Task GetForUserAsync(User user, Toolkit.Helpers.IObjectSerializer objectSerializer) + public static async Task GetForUserAsync(User user, Toolkit.Helpers.IObjectSerializer objectSerializer = null) { var appData = await ApplicationData.GetForUserAsync(user); return new ApplicationDataStorageHelper(appData, objectSerializer); @@ -55,11 +55,11 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// Initializes a new instance of the class. /// /// The data store to interact with. - /// Serializer for converting stored values. - public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer objectSerializer) + /// Serializer for converting stored values. Defaults to . + public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer objectSerializer = null) { AppData = appData ?? throw new ArgumentNullException(nameof(appData)); - _serializer = objectSerializer ?? throw new ArgumentNullException(nameof(objectSerializer)); + _serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer(); } /// From 8140bff9b44e0861c2c3406024f60a8eaa6e51fd Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 19 Jul 2021 13:50:18 -0700 Subject: [PATCH 025/299] Added support for the LocalCacheFolder to validate extensibility of AppDataStorageHelper --- ...pplicationDataStorageHelper.CacheFolder.cs | 84 +++++++++++++++++++ .../ApplicationDataStorageHelper.cs | 50 ++++++----- 2 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs new file mode 100644 index 000000000..95018f51b --- /dev/null +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Toolkit.Helpers; +using Windows.Storage; + +namespace Microsoft.Toolkit.Uwp.Helpers +{ + /// + /// An extension of ApplicationDataStorageHelper with additional features for interop with the LocalCacheFolder. + /// + public partial class ApplicationDataStorageHelper + { + /// + /// Gets the storage folder. + /// + public StorageFolder CacheFolder => AppData.LocalCacheFolder; + + /// + /// Determines if a directory item already exists in the LocalCacheFolder. + /// + /// Key of the file. + /// True if an item exists. + public Task CacheItemExistsAsync(string itemName) + { + return ItemExistsAsync(CacheFolder, itemName); + } + + /// + /// Retrieves an object from a file in the LocalCacheFolder. + /// + /// Type of object retrieved. + /// Path to the file that contains the object. + /// Default value of the object. + /// Waiting task until completion with the object in the file. + public Task ReadCacheFileAsync(string filePath, T @default = default) + { + return ReadFileAsync(CacheFolder, filePath, @default); + } + + + /// + /// Retrieves the listings for a folder and the item types in the LocalCacheFolder. + /// + /// The path to the target folder. + /// A list of file types and names in the target folder. + public Task>> ReadCacheFolderAsync(string folderPath) + { + return ReadFolderAsync(CacheFolder, folderPath); + } + + /// + /// Saves an object inside a file in the LocalCacheFolder. + /// + /// Type of object saved. + /// Path to the file that will contain the object. + /// Object to save. + /// Waiting task until completion. + public Task CreateCacheFileAsync(string filePath, T value) + { + return SaveFileAsync(CacheFolder, filePath, value); + } + + /// + /// Ensure a folder exists at the folder path specified in the LocalCacheFolder. + /// + /// The path and name of the target folder. + /// Waiting task until completion. + public Task CreateCacheFolderAsync(string folderPath) + { + return CreateFolderAsync(CacheFolder, folderPath); + } + + /// + /// Deletes a file or folder item in the LocalCacheFolder. + /// + /// The path to the item for deletion. + /// Waiting task until completion. + public Task DeleteCacheItemAsync(string itemPath) + { + return DeleteItemAsync(CacheFolder, itemPath); + } + } +} diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 97b38035d..583bbc4a1 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -15,7 +15,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints. /// - public class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper + public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper { /// /// Get a new instance using ApplicationData.Current and the provided serializer. @@ -43,12 +43,18 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Gets the settings container. /// + public ApplicationDataContainer Settings => AppData.LocalSettings; + + /// + /// Gets the storage folder. + /// + public StorageFolder Folder => AppData.LocalFolder; + + /// + /// Gets the storage host. + /// protected ApplicationData AppData { get; private set; } - private ApplicationDataContainer DefaultSettings => AppData.LocalSettings; - - private StorageFolder DefaultFolder => AppData.LocalFolder; - private readonly Toolkit.Helpers.IObjectSerializer _serializer; /// @@ -65,7 +71,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public bool KeyExists(string key) { - return DefaultSettings.Values.ContainsKey(key); + return Settings.Values.ContainsKey(key); } /// @@ -73,7 +79,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers { if (KeyExists(compositeKey)) { - ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; if (composite != null) { return composite.ContainsKey(key); @@ -86,7 +92,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public T Read(string key, T @default = default) { - if (!DefaultSettings.Values.TryGetValue(key, out var value) || value == null) + if (!Settings.Values.TryGetValue(key, out var value) || value == null) { return @default; } @@ -97,7 +103,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public T Read(string compositeKey, string key, T @default = default) { - ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; if (composite != null) { string value = (string)composite[key]; @@ -113,7 +119,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public void Save(string key, T value) { - DefaultSettings.Values[key] = _serializer.Serialize(value); + Settings.Values[key] = _serializer.Serialize(value); } /// @@ -121,7 +127,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers { if (KeyExists(compositeKey)) { - ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; foreach (KeyValuePair setting in values) { @@ -143,14 +149,14 @@ namespace Microsoft.Toolkit.Uwp.Helpers composite.Add(setting.Key, _serializer.Serialize(setting.Value)); } - DefaultSettings.Values[compositeKey] = composite; + Settings.Values[compositeKey] = composite; } } /// public void Delete(string key) { - DefaultSettings.Values.Remove(key); + Settings.Values.Remove(key); } /// @@ -158,7 +164,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers { if (KeyExists(compositeKey)) { - ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)DefaultSettings.Values[compositeKey]; + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; composite.Remove(key); } } @@ -166,37 +172,37 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public Task ItemExistsAsync(string itemName) { - return ItemExistsAsync(DefaultFolder, itemName); + return ItemExistsAsync(Folder, itemName); } /// public Task ReadFileAsync(string filePath, T @default = default) { - return ReadFileAsync(DefaultFolder, filePath, @default); + return ReadFileAsync(Folder, filePath, @default); } /// public Task>> ReadFolderAsync(string folderPath) { - return ReadFolderAsync(DefaultFolder, folderPath); + return ReadFolderAsync(Folder, folderPath); } /// public Task CreateFileAsync(string filePath, T value) { - return SaveFileAsync(DefaultFolder, filePath, value); + return SaveFileAsync(Folder, filePath, value); } /// public Task CreateFolderAsync(string folderPath) { - return CreateFolderAsync(DefaultFolder, folderPath); + return CreateFolderAsync(Folder, folderPath); } /// public Task DeleteItemAsync(string itemPath) { - return DeleteItemAsync(DefaultFolder, itemPath); + return DeleteItemAsync(Folder, itemPath); } /// @@ -208,7 +214,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// A task with the result of the file query. public Task FileExistsAsync(string fileName, bool isRecursive = false) { - return FileExistsAsync(DefaultFolder, fileName, isRecursive); + return FileExistsAsync(Folder, fileName, isRecursive); } /// @@ -220,7 +226,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// Waiting task until completion. public Task SaveFileAsync(string filePath, T value) { - return SaveFileAsync(DefaultFolder, filePath, value); + return SaveFileAsync(Folder, filePath, value); } private async Task ItemExistsAsync(StorageFolder folder, string itemName) From ddcf8c2b7d89620e7d8e12b2b3647950e9bac2c0 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 19 Jul 2021 14:30:45 -0700 Subject: [PATCH 026/299] Adding license header --- .../ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs index 95018f51b..272afab07 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Threading.Tasks; From 655caf655d33b359c8a267879f6bdaa376be45f8 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 19 Jul 2021 15:08:27 -0700 Subject: [PATCH 027/299] Whitespace fix --- .../ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs index 272afab07..d0f6fd1df 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs @@ -42,7 +42,6 @@ namespace Microsoft.Toolkit.Uwp.Helpers return ReadFileAsync(CacheFolder, filePath, @default); } - /// /// Retrieves the listings for a folder and the item types in the LocalCacheFolder. /// From 8476f708f2ced7b826b13c49838feb1d29f7cee1 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Wed, 21 Jul 2021 16:36:45 -0700 Subject: [PATCH 028/299] PR updates --- ...pplicationDataStorageHelper.CacheFolder.cs | 14 +- .../ApplicationDataStorageHelper.cs | 145 +++++++++--------- .../ObjectStorage/IFileStorageHelper.cs | 11 +- .../ObjectStorage/IObjectSerializer.cs | 10 +- .../ObjectStorage/ISettingsStorageHelper.cs | 57 ++----- .../Helpers/ObjectStorage/SystemSerializer.cs | 10 +- Microsoft.Toolkit/Microsoft.Toolkit.csproj | 3 + 7 files changed, 105 insertions(+), 145 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs index d0f6fd1df..ee94dd5bf 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs @@ -16,20 +16,10 @@ namespace Microsoft.Toolkit.Uwp.Helpers public partial class ApplicationDataStorageHelper { /// - /// Gets the storage folder. + /// Gets the local cache folder. /// public StorageFolder CacheFolder => AppData.LocalCacheFolder; - /// - /// Determines if a directory item already exists in the LocalCacheFolder. - /// - /// Key of the file. - /// True if an item exists. - public Task CacheItemExistsAsync(string itemName) - { - return ItemExistsAsync(CacheFolder, itemName); - } - /// /// Retrieves an object from a file in the LocalCacheFolder. /// @@ -47,7 +37,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// The path to the target folder. /// A list of file types and names in the target folder. - public Task>> ReadCacheFolderAsync(string folderPath) + public Task> ReadCacheFolderAsync(string folderPath) { return ReadFolderAsync(CacheFolder, folderPath); } diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 583bbc4a1..4f1ddbb41 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -15,7 +15,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints. /// - public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper + public partial class ApplicationDataStorageHelper : IFileStorageHelper, ISettingsStorageHelper { /// /// Get a new instance using ApplicationData.Current and the provided serializer. @@ -53,9 +53,12 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Gets the storage host. /// - protected ApplicationData AppData { get; private set; } + protected ApplicationData AppData { get; } - private readonly Toolkit.Helpers.IObjectSerializer _serializer; + /// + /// Gets the serializer for converting stored values. + /// + protected Toolkit.Helpers.IObjectSerializer Serializer { get; } /// /// Initializes a new instance of the class. @@ -65,16 +68,54 @@ namespace Microsoft.Toolkit.Uwp.Helpers public ApplicationDataStorageHelper(ApplicationData appData, Toolkit.Helpers.IObjectSerializer objectSerializer = null) { AppData = appData ?? throw new ArgumentNullException(nameof(appData)); - _serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer(); + Serializer = objectSerializer ?? new Toolkit.Helpers.SystemSerializer(); } - /// + /// + /// Determines whether a setting already exists. + /// + /// Key of the setting (that contains object). + /// True if a value exists. public bool KeyExists(string key) { return Settings.Values.ContainsKey(key); } /// + public T Read(string key, T @default = default) + { + if (!Settings.Values.TryGetValue(key, out var valueObj) || valueObj == null) + { + return @default; + } + + return Serializer.Deserialize(valueObj as string); + } + + /// + public void Save(string key, T value) + { + Settings.Values[key] = Serializer.Serialize(value); + } + + /// + public void Delete(string key) + { + Settings.Values.Remove(key); + } + + /// + public void Clear() + { + Settings.Values.Clear(); + } + + /// + /// Determines whether a setting already exists in composite. + /// + /// Key of the composite (that contains settings). + /// Key of the setting (that contains object). + /// True if a value exists. public bool KeyExists(string compositeKey, string key) { if (KeyExists(compositeKey)) @@ -89,18 +130,14 @@ namespace Microsoft.Toolkit.Uwp.Helpers return false; } - /// - public T Read(string key, T @default = default) - { - if (!Settings.Values.TryGetValue(key, out var value) || value == null) - { - return @default; - } - - return _serializer.Deserialize(value); - } - - /// + /// + /// Retrieves a single item by its key in composite. + /// + /// Type of object retrieved. + /// Key of the composite (that contains settings). + /// Key of the object. + /// Default value of the object. + /// The T object. public T Read(string compositeKey, string key, T @default = default) { ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; @@ -109,20 +146,21 @@ namespace Microsoft.Toolkit.Uwp.Helpers string value = (string)composite[key]; if (value != null) { - return _serializer.Deserialize(value); + return Serializer.Deserialize(value); } } return @default; } - /// - public void Save(string key, T value) - { - Settings.Values[key] = _serializer.Serialize(value); - } - - /// + /// + /// Saves a group of items by its key in a composite. + /// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application + /// and for groups of settings which need to be treated in an atomic way. + /// + /// Type of object saved. + /// Key of the composite (that contains settings). + /// Objects to save. public void Save(string compositeKey, IDictionary values) { if (KeyExists(compositeKey)) @@ -133,11 +171,11 @@ namespace Microsoft.Toolkit.Uwp.Helpers { if (composite.ContainsKey(setting.Key)) { - composite[setting.Key] = _serializer.Serialize(setting.Value); + composite[setting.Key] = Serializer.Serialize(setting.Value); } else { - composite.Add(setting.Key, _serializer.Serialize(setting.Value)); + composite.Add(setting.Key, Serializer.Serialize(setting.Value)); } } } @@ -146,20 +184,18 @@ namespace Microsoft.Toolkit.Uwp.Helpers ApplicationDataCompositeValue composite = new ApplicationDataCompositeValue(); foreach (KeyValuePair setting in values) { - composite.Add(setting.Key, _serializer.Serialize(setting.Value)); + composite.Add(setting.Key, Serializer.Serialize(setting.Value)); } Settings.Values[compositeKey] = composite; } } - /// - public void Delete(string key) - { - Settings.Values.Remove(key); - } - - /// + /// + /// Deletes a single item by its key in composite. + /// + /// Key of the composite (that contains settings). + /// Key of the object. public void Delete(string compositeKey, string key) { if (KeyExists(compositeKey)) @@ -169,12 +205,6 @@ namespace Microsoft.Toolkit.Uwp.Helpers } } - /// - public Task ItemExistsAsync(string itemName) - { - return ItemExistsAsync(Folder, itemName); - } - /// public Task ReadFileAsync(string filePath, T @default = default) { @@ -182,7 +212,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public Task>> ReadFolderAsync(string folderPath) + public Task> ReadFolderAsync(string folderPath) { return ReadFolderAsync(Folder, folderPath); } @@ -205,18 +235,6 @@ namespace Microsoft.Toolkit.Uwp.Helpers return DeleteItemAsync(Folder, itemPath); } - /// - /// Determine the existance of a file at the specified path. - /// To check for folders, use . - /// - /// The name of the file. - /// Whether the file should be searched for recursively. - /// A task with the result of the file query. - public Task FileExistsAsync(string fileName, bool isRecursive = false) - { - return FileExistsAsync(Folder, fileName, isRecursive); - } - /// /// Saves an object inside a file. /// @@ -229,24 +247,13 @@ namespace Microsoft.Toolkit.Uwp.Helpers return SaveFileAsync(Folder, filePath, value); } - private async Task ItemExistsAsync(StorageFolder folder, string itemName) - { - var item = await folder.TryGetItemAsync(itemName); - return item != null; - } - - private Task FileExistsAsync(StorageFolder folder, string fileName, bool isRecursive) - { - return folder.FileExistsAsync(fileName, isRecursive); - } - private async Task ReadFileAsync(StorageFolder folder, string filePath, T @default = default) { string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath); - return (value != null) ? _serializer.Deserialize(value) : @default; + return (value != null) ? Serializer.Deserialize(value) : @default; } - private async Task>> ReadFolderAsync(StorageFolder folder, string folderPath) + private async Task> ReadFolderAsync(StorageFolder folder, string folderPath) { var targetFolder = await folder.GetFolderAsync(folderPath); var items = await targetFolder.GetItemsAsync(); @@ -257,13 +264,13 @@ namespace Microsoft.Toolkit.Uwp.Helpers : item.IsOfType(StorageItemTypes.Folder) ? DirectoryItemType.Folder : DirectoryItemType.None; - return new Tuple(itemType, item.Name); + return new ValueTuple(itemType, item.Name); }).ToList(); } private Task SaveFileAsync(StorageFolder folder, string filePath, T value) { - return StorageFileHelper.WriteTextToFileAsync(folder, _serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); + return StorageFileHelper.WriteTextToFileAsync(folder, Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); } private async Task CreateFolderAsync(StorageFolder folder, string folderPath) diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index eae8a3dfb..2ca3397ee 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -18,13 +18,6 @@ namespace Microsoft.Toolkit.Helpers /// public interface IFileStorageHelper { - /// - /// Determines if a directory item already exists. - /// - /// Key of the file. - /// True if an item exists. - Task ItemExistsAsync(string itemName); - /// /// Retrieves an object from a file. /// @@ -38,8 +31,8 @@ namespace Microsoft.Toolkit.Helpers /// Retrieves the listings for a folder and the item types. /// /// The path to the target folder. - /// A list of file types and names in the target folder. - Task>> ReadFolderAsync(string folderPath); + /// A list of item types and names in the target folder. + Task> ReadFolderAsync(string folderPath); /// /// Saves an object inside a file. diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs index d046ead57..266ec1579 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.Toolkit.Helpers { /// @@ -15,14 +17,16 @@ namespace Microsoft.Toolkit.Helpers /// The type of the object to serialize. /// The object to serialize. /// The serialized object. - object? Serialize(T value); + [return: NotNullIfNotNull("value")] + string? Serialize(T value); /// - /// Deserialize a primitive or string into an object of the given type. + /// Deserialize string into an object of the given type. /// /// The type of the deserialized object. /// The string to deserialize. /// The deserialized object. - T Deserialize(object value); + [return: NotNullIfNotNull("value")] + T Deserialize(string value); } } \ No newline at end of file diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index af9936120..43a03a72a 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -9,71 +9,36 @@ namespace Microsoft.Toolkit.Helpers /// /// Service interface used to store data using key value pairs. /// - public interface ISettingsStorageHelper + /// The type of keys to use for accessing values. + public interface ISettingsStorageHelper + where TKey : notnull { - /// - /// Determines whether a setting already exists. - /// - /// Key of the setting (that contains object). - /// True if a value exists. - bool KeyExists(string key); - - /// - /// Determines whether a setting already exists in composite. - /// - /// Key of the composite (that contains settings). - /// Key of the setting (that contains object). - /// True if a value exists. - bool KeyExists(string compositeKey, string key); - /// /// Retrieves a single item by its key. /// - /// Type of object retrieved. + /// Type of object retrieved. /// Key of the object. /// Default value of the object. - /// The T object - T Read(string key, T? @default = default(T)); - - /// - /// Retrieves a single item by its key in composite. - /// - /// Type of object retrieved. - /// Key of the composite (that contains settings). - /// Key of the object. - /// Default value of the object. - /// The T object. - T Read(string compositeKey, string key, T? @default = default(T)); + /// The TValue object + TValue? Read(TKey key, TValue? @default = default); /// /// Saves a single item by its key. /// - /// Type of object saved. + /// Type of object saved. /// Key of the value saved. /// Object to save. - void Save(string key, T value); - - /// - /// Saves a group of items by its key in a composite. - /// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application - /// and for groups of settings which need to be treated in an atomic way. - /// - /// Type of object saved. - /// Key of the composite (that contains settings). - /// Objects to save. - void Save(string compositeKey, IDictionary values); + void Save(TKey key, TValue value); /// /// Deletes a single item by its key. /// /// Key of the object. - void Delete(string key); + void Delete(TKey key); /// - /// Deletes a single item by its key in composite. + /// Clear all keys and values from the settings store. /// - /// Key of the composite (that contains settings). - /// Key of the object. - void Delete(string compositeKey, string key); + void Clear(); } } diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs index fbf5d9c1d..62d1b681c 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Helpers /// Type to convert value to. /// Value from storage to convert. /// Deserialized value or default value. - public T Deserialize(object value) + public T Deserialize(string value) { var type = typeof(T); var typeInfo = type.GetTypeInfo(); @@ -29,9 +29,7 @@ namespace Microsoft.Toolkit.Helpers return (T)Convert.ChangeType(value, type); } - return ThrowNotSupportedException(); - - static T ThrowNotSupportedException() => throw new NotSupportedException("This serializer can only handle primitive types and strings. Please implement your own IObjectSerializer for more complex scenarios."); + throw new NotSupportedException("This serializer can only handle primitive types and strings. Please implement your own IObjectSerializer for more complex scenarios."); } /// @@ -40,9 +38,9 @@ namespace Microsoft.Toolkit.Helpers /// Type to serialize from. /// Value to serialize. /// String representation of value. - public object? Serialize(T value) + public string? Serialize(T value) { - return value; + return value?.ToString(); } } } \ No newline at end of file diff --git a/Microsoft.Toolkit/Microsoft.Toolkit.csproj b/Microsoft.Toolkit/Microsoft.Toolkit.csproj index 8b428ac26..49f3c5e55 100644 --- a/Microsoft.Toolkit/Microsoft.Toolkit.csproj +++ b/Microsoft.Toolkit/Microsoft.Toolkit.csproj @@ -23,5 +23,8 @@ + + + \ No newline at end of file From 8d18fcba9c756a590f781ae1baafc272167017b9 Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Fri, 9 Apr 2021 09:06:40 +0530 Subject: [PATCH 029/299] Format Build related files Normalize Tabs into Spaces Clean-up trailing white-spaces Fix-up new-lines where necessary --- .gitattributes | 6 +++--- .runsettings | 2 +- azure-pipelines.yml | 12 ++++++------ build/Find-WindowsSDKVersions.ps1 | 12 ++++++------ build/Sign-Package.ps1 | 10 +++++----- build/build.cake | 3 --- nuget.config | 2 +- settings.xamlstyler | 4 ++-- stylecop.json | 14 +++++++------- version.json | 4 ++-- 10 files changed, 33 insertions(+), 36 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1ff0c4230..5896c1692 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -46,9 +46,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain diff --git a/.runsettings b/.runsettings index ff838beea..44f0672a1 100644 --- a/.runsettings +++ b/.runsettings @@ -1,6 +1,6 @@ - 0 + 0 0 ClassLevel diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 77b50e578..bd7f20b94 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,14 +11,14 @@ pr: pool: vmImage: windows-2019 -variables: +variables: BuildConfiguration: Release jobs: ### BUILD ### - job: BuildBits timeoutInMinutes: 60 - + steps: - task: BatchScript@1 inputs: @@ -32,7 +32,7 @@ jobs: inputs: versionSpec: 5.6.0 - - task: DotNetCoreCLI@2 + - task: DotNetCoreCLI@2 inputs: command: custom custom: tool @@ -49,7 +49,7 @@ jobs: displayName: Build ### Unit Tests ### - + - powershell: .\build\build.ps1 -target=Test displayName: Test @@ -105,7 +105,7 @@ jobs: - job: SmokeTests dependsOn: BuildBits timeoutInMinutes: 60 - + steps: - task: DownloadPipelineArtifact@2 displayName: Download NuGet Packages Artifact @@ -113,7 +113,7 @@ jobs: artifact: Packages path: .\bin\nupkg - - task: DotNetCoreCLI@2 + - task: DotNetCoreCLI@2 inputs: command: custom custom: tool diff --git a/build/Find-WindowsSDKVersions.ps1 b/build/Find-WindowsSDKVersions.ps1 index ebb8ab5ee..ab6aa50e9 100644 --- a/build/Find-WindowsSDKVersions.ps1 +++ b/build/Find-WindowsSDKVersions.ps1 @@ -9,13 +9,13 @@ function Get-Nodes { param( [parameter(ValueFromPipeline=$true)] - [xml] $xml, + [xml] $xml, [parameter(Mandatory=$true)] [string] $nodeName) # Try the old style csproj. Also format required for .targets and .props files $n = Select-Xml -Xml $xml.Project -Namespace @{d = $ns } -XPath "//d:$nodeName" - + # Try the SDK-style files if (!$n) { $r = Select-Xml -Xml $xml.Project -XPath "//$nodeName" @@ -28,7 +28,7 @@ function Get-NodeValue { param( [parameter(ValueFromPipeline=$true)] - [xml] $xml, + [xml] $xml, [string] $nodeName) $node = get-nodes $xml $nodeName @@ -45,7 +45,7 @@ function Get-NodeValue function Get-SdkVersion { param( - [Parameter(ValueFromPipeline=$true)] $file) + [Parameter(ValueFromPipeline=$true)] $file) [xml] $xml = Get-Content $file @@ -150,7 +150,7 @@ foreach($version in $versions) { if ($version -match "10\.0\.\d{5}\.0") { $installRequired = Test-InstallWindowsSDK $version Write-Host "Windows SDK '$version' install required: $installRequired" - if ($installRequired) { + if ($installRequired) { # Automatically invoke Install-WindowsSDKIso.ps1 ? $anyInstallRequired = $true } @@ -158,7 +158,7 @@ foreach($version in $versions) { } Write-Host -if ($anyInstallRequired) { +if ($anyInstallRequired) { throw "At least one Windows SDK is missing from this machine" } else { Write-Host "All referenced Windows SDKs are installed!" diff --git a/build/Sign-Package.ps1 b/build/Sign-Package.ps1 index b1f536092..d8def95e1 100644 --- a/build/Sign-Package.ps1 +++ b/build/Sign-Package.ps1 @@ -3,8 +3,8 @@ $currentDirectory = split-path $MyInvocation.MyCommand.Definition # See if we have the ClientSecret available if([string]::IsNullOrEmpty($Env:SignClientSecret)){ - Write-Host "Client Secret not found, not signing packages" - return; + Write-Host "Client Secret not found, not signing packages" + return; } dotnet tool install --tool-path . SignClient @@ -16,13 +16,13 @@ $appSettings = "$currentDirectory\SignClientSettings.json" $nupkgs = gci $Env:ArtifactDirectory\*.nupkg -recurse | Select -ExpandProperty FullName foreach ($nupkg in $nupkgs){ - Write-Host "Submitting $nupkg for signing" + Write-Host "Submitting $nupkg for signing" - .\SignClient 'sign' -c $appSettings -i $nupkg -r $Env:SignClientUser -s $Env:SignClientSecret -n 'Windows Community Toolkit' -d 'Windows Community Toolkit' -u 'https://developer.microsoft.com/en-us/windows/uwp-community-toolkit' + .\SignClient 'sign' -c $appSettings -i $nupkg -r $Env:SignClientUser -s $Env:SignClientSecret -n 'Windows Community Toolkit' -d 'Windows Community Toolkit' -u 'https://developer.microsoft.com/en-us/windows/uwp-community-toolkit' if ($LASTEXITCODE -ne 0) { exit 1 } - Write-Host "Finished signing $nupkg" + Write-Host "Finished signing $nupkg" } Write-Host "Sign-package complete" \ No newline at end of file diff --git a/build/build.cake b/build/build.cake index db5dd4ec8..14c3649c1 100644 --- a/build/build.cake +++ b/build/build.cake @@ -312,7 +312,6 @@ Task("MSTestUITest") DotNetCoreTest(file.FullPath, testSettings); }); - ////////////////////////////////////////////////////////////////////// // TASK TARGETS ////////////////////////////////////////////////////////////////////// @@ -353,8 +352,6 @@ Task("StyleXaml") } }); - - ////////////////////////////////////////////////////////////////////// // EXECUTION ////////////////////////////////////////////////////////////////////// diff --git a/nuget.config b/nuget.config index b96dd699a..1b39cb311 100644 --- a/nuget.config +++ b/nuget.config @@ -8,4 +8,4 @@ - + \ No newline at end of file diff --git a/settings.xamlstyler b/settings.xamlstyler index 34c6ebee2..443f4b5a0 100644 --- a/settings.xamlstyler +++ b/settings.xamlstyler @@ -7,7 +7,7 @@ "SeparateByGroups": false, "AttributeIndentation": 0, "AttributeIndentationStyle": 1, - "RemoveDesignTimeReferences": false, + "RemoveDesignTimeReferences": false, "EnableAttributeReordering": true, "AttributeOrderingRuleGroups": [ "x:Class", @@ -39,4 +39,4 @@ "FormatOnSave": true, "CommentPadding": 2, "IndentSize": 4 -} +} \ No newline at end of file diff --git a/stylecop.json b/stylecop.json index d839cca33..7a611b823 100644 --- a/stylecop.json +++ b/stylecop.json @@ -1,8 +1,8 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "orderingRules": { - "usingDirectivesPlacement": "outsideNamespace" - } +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace" } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/version.json b/version.json index 0dbe7b20f..ca35973bf 100644 --- a/version.json +++ b/version.json @@ -5,7 +5,7 @@ "^refs/heads/dev$", // we release out of dev "^refs/heads/rel/\\d+\\.\\d+\\.\\d+" // we also release branches starting with rel/N.N.N ], - "nugetPackageVersion":{ + "nugetPackageVersion": { "semVer": 2 }, "cloudBuild": { @@ -13,4 +13,4 @@ "enabled": false } } -} +} \ No newline at end of file From b741e3527f0d4ad3faf07b978ca671868954e240 Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Fri, 9 Apr 2021 09:30:22 +0530 Subject: [PATCH 030/299] Upgrade Cake Build Runner - Update Build script - Upgrade Cake Runner - Upgrade modules, addins and tools --- azure-pipelines.yml | 10 +- build/StyleXaml.bat | 2 +- build/UpdateHeaders.bat | 2 +- build/build.cake | 16 +-- build/build.ps1 | 229 ++++++++++++++++++++++-------------- build/tools/packages.config | 2 +- 6 files changed, 158 insertions(+), 103 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bd7f20b94..030173cb6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,12 +45,12 @@ jobs: #- powershell: .\build\Install-WindowsSdkISO.ps1 19041 # displayName: Insider SDK - - powershell: .\build\build.ps1 -target=Build + - powershell: .\build\build.ps1 -Target Build displayName: Build ### Unit Tests ### - - powershell: .\build\build.ps1 -target=Test + - powershell: .\build\build.ps1 -Target Test displayName: Test - task: PublishTestResults@2 @@ -62,7 +62,7 @@ jobs: ### UI Integration Tests ### - - powershell: .\build\build.ps1 -target=UITest + - powershell: .\build\build.ps1 -Target UITest displayName: UI Integration Tests - task: PublishPipelineArtifact@1 @@ -81,7 +81,7 @@ jobs: ### Package ### - - powershell: .\build\build.ps1 -target=Package + - powershell: .\build\build.ps1 -Target Package displayName: Package - task: PowerShell@2 @@ -123,7 +123,7 @@ jobs: - script: nbgv cloud displayName: Set Version - - powershell: .\build\build.ps1 -target=SmokeTest + - powershell: .\build\build.ps1 -Target SmokeTest displayName: SmokeTest - task: CopyFiles@2 diff --git a/build/StyleXaml.bat b/build/StyleXaml.bat index b03dcbd2a..3ebd1ccde 100644 --- a/build/StyleXaml.bat +++ b/build/StyleXaml.bat @@ -1,3 +1,3 @@ @ECHO OFF -PowerShell.exe -file "%~dp0build.ps1" -target=StyleXaml +PowerShell.exe -file "%~dp0build.ps1" -Target StyleXaml PAUSE \ No newline at end of file diff --git a/build/UpdateHeaders.bat b/build/UpdateHeaders.bat index 166ea2121..55872778b 100644 --- a/build/UpdateHeaders.bat +++ b/build/UpdateHeaders.bat @@ -1,3 +1,3 @@ @ECHO OFF -PowerShell.exe -file "%~dp0build.ps1" -target=UpdateHeaders +PowerShell.exe -file "%~dp0build.ps1" -Target UpdateHeaders PAUSE \ No newline at end of file diff --git a/build/build.cake b/build/build.cake index 14c3649c1..82be1b88c 100644 --- a/build/build.cake +++ b/build/build.cake @@ -1,10 +1,10 @@ -#module nuget:?package=Cake.LongPath.Module&version=0.7.0 +#module nuget:?package=Cake.LongPath.Module&version=1.0.1 -#addin nuget:?package=Cake.FileHelpers&version=3.2.1 -#addin nuget:?package=Cake.Powershell&version=0.4.8 -#addin nuget:?package=Cake.GitVersioning&version=3.3.37 +#addin nuget:?package=Cake.FileHelpers&version=4.0.1 +#addin nuget:?package=Cake.Powershell&version=1.0.1 +#addin nuget:?package=Cake.GitVersioning&version=3.4.220 -#tool nuget:?package=MSTest.TestAdapter&version=2.1.0 +#tool nuget:?package=MSTest.TestAdapter&version=2.2.5 #tool nuget:?package=vswhere&version=2.8.4 using System; @@ -94,7 +94,7 @@ void VerifyHeaders(bool Replace) if(!Replace && hasMissing) { - throw new Exception("Please run UpdateHeaders.bat or '.\\build.ps1 -target=UpdateHeaders' and commit the changes."); + throw new Exception("Please run UpdateHeaders.bat or '.\\build.ps1 -Target UpdateHeaders' and commit the changes."); } } @@ -259,7 +259,7 @@ Task("Test") { Configuration = "Release", NoBuild = true, - Logger = "trx;LogFilePrefix=VsTestResults", + Loggers = new[] { "trx;LogFilePrefix=VsTestResults" }, Verbosity = DotNetCoreVerbosity.Normal, ArgumentCustomization = arg => arg.Append($"-s {baseDir}/.runsettings"), }; @@ -306,7 +306,7 @@ Task("MSTestUITest") { Configuration = "Release", NoBuild = true, - Logger = "trx;LogFilePrefix=VsTestResults", + Loggers = new[] { "trx;LogFilePrefix=VsTestResults" }, Verbosity = DotNetCoreVerbosity.Normal }; DotNetCoreTest(file.FullPath, testSettings); diff --git a/build/build.ps1 b/build/build.ps1 index deed10851..18078054a 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -13,19 +13,18 @@ This is a Powershell script to bootstrap a Cake build. This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) and execute your Cake build script with the parameters you provide. +.PARAMETER Script +The build script to execute. .PARAMETER Target The build script target to run. .PARAMETER Configuration The build configuration to use. .PARAMETER Verbosity Specifies the amount of information to be displayed. -.PARAMETER Experimental -Tells Cake to use the latest Roslyn release. -.PARAMETER WhatIf -Performs a dry run of the build script. -No tasks will be executed. -.PARAMETER Mono -Tells Cake to use the Mono scripting engine. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. .PARAMETER SkipToolPackageRestore Skips restoring of packages. .PARAMETER ScriptArgs @@ -38,18 +37,41 @@ https://cakebuild.net [CmdletBinding()] Param( - [string]$Target = "Default", + [string]$Script, + [string]$Target, + [string]$Configuration, [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] - [string]$Verbosity = "Verbose", - [switch]$Experimental, - [Alias("DryRun","Noop")] - [switch]$WhatIf, - [switch]$Mono, + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, [switch]$SkipToolPackageRestore, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [string[]]$ScriptArgs ) +# This is an automatic variable in PowerShell Core, but not in Windows PowerShell 5.x +if (-not (Test-Path variable:global:IsCoreCLR)) { + $IsCoreCLR = $false +} + +# Attempt to set highest encryption available for SecurityProtocol. +# PowerShell will not set this by default (until maybe .NET 4.6.x). This +# will typically produce a message for PowerShell v2 (just an info +# message though) +try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. + if (-not $IsCoreCLR) { + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } + } catch { + Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' + } + [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null function MD5HashFile([string] $filePath) { @@ -72,58 +94,60 @@ function MD5HashFile([string] $filePath) { $file.Dispose() } + + if ($md5 -ne $null) + { + $md5.Dispose() + } } } +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + Write-Host "Preparing to run build script..." if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } +if(!$Script){ + $Script = Join-Path $PSScriptRoot "build.cake" +} $TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" -$MODULES_DIR = Join-Path $PSScriptRoot "tools/modules" +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" -$MODULES_PACKAGES_CONFIG_MD5 = Join-Path $MODULES_DIR "packages.config.md5sum" -# Should we use mono? -$UseMono = ""; -if($Mono.IsPresent) { - Write-Verbose -Message "Using the Mono based scripting engine." - $UseMono = "-mono" -} - -# Should we use the new Roslyn? -$UseExperimental = ""; -if($Experimental.IsPresent -and !($Mono.IsPresent)) { - Write-Verbose -Message "Using experimental version of Roslyn." - $UseExperimental = "-experimental" -} - -# Is this a dry run? -$UseDryRun = ""; -if($WhatIf.IsPresent) { - $UseDryRun = "-dryrun" -} +$env:CAKE_PATHS_TOOLS = $TOOLS_DIR +$env:CAKE_PATHS_ADDINS = $ADDINS_DIR +$env:CAKE_PATHS_MODULES = $MODULES_DIR # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { Write-Verbose -Message "Creating tools directory..." - New-Item -Path $TOOLS_DIR -Type directory | out-null + New-Item -Path $TOOLS_DIR -Type Directory | Out-Null } -# Fix to force PS to use TLS12 -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - # Make sure that packages.config exist. if (!(Test-Path $PACKAGES_CONFIG)) { Write-Verbose -Message "Downloading packages.config..." - try { (New-Object System.Net.WebClient).DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) + } catch { Throw "Could not download packages.config." } } @@ -143,14 +167,26 @@ if (!(Test-Path $NUGET_EXE)) { if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Downloading NuGet.exe..." try { - (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) } catch { Throw "Could not download NuGet.exe." } } +# These are automatic variables in PowerShell Core, but not in Windows PowerShell 5.x +if (-not (Test-Path variable:global:ismacos)) { + $IsLinux = $false + $IsMacOS = $false +} + # Save nuget.exe path to environment to be available to child processed -$ENV:NUGET_EXE = $NUGET_EXE +$env:NUGET_EXE = $NUGET_EXE +$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$NUGET_EXE`"" +} else { + "`"$NUGET_EXE`"" +} # Restore tools from NuGet? if(-Not $SkipToolPackageRestore.IsPresent) { @@ -158,15 +194,17 @@ if(-Not $SkipToolPackageRestore.IsPresent) { Set-Location $TOOLS_DIR # Check for changes in packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { Write-Verbose -Message "Missing or changed package.config hash..." - Remove-Item * -Recurse -Exclude packages.config,nuget.exe + Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | + Remove-Item -Recurse -Force } Write-Verbose -Message "Restoring tools from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet tools." @@ -175,7 +213,42 @@ if(-Not $SkipToolPackageRestore.IsPresent) { { $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" } - Write-Verbose -Message ($NuGetOutput | out-string) + Write-Verbose -Message ($NuGetOutput | Out-String) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | Out-String) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | Out-String) + Pop-Location } @@ -184,49 +257,31 @@ if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe at $CAKE_EXE" } -# Make sure modules folder exists -if ((Test-Path $PSScriptRoot) -and !(Test-Path $MODULES_DIR)) { - Write-Verbose -Message "Creating tools/modules directory..." - New-Item -Path $MODULES_DIR -Type directory | out-null +$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$CAKE_EXE`"" +} else { + "`"$CAKE_EXE`"" } -# Restore modules from NuGet? -if(-Not $SkipToolPackageRestore.IsPresent) { - Push-Location - Set-Location $MODULES_DIR - - # Check for changes in modules packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile($MODULES_PACKAGES_CONFIG) - if((!(Test-Path $MODULES_PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $MODULES_PACKAGES_CONFIG_MD5 ))) { - Write-Verbose -Message "Missing or changed modules package.config hash..." - Remove-Item * -Recurse -Exclude packages.config,packages.config.md5sum,nuget.exe - } - - Write-Verbose -Message "Restoring modules from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet modules." - } - else - { - $md5Hash | Out-File $MODULES_PACKAGES_CONFIG_MD5 -Encoding "ASCII" - } - Write-Verbose -Message ($NuGetOutput | out-string) - Pop-Location -} + # Build an array (not a string) of Cake arguments to be joined later +$cakeArguments = @() +if ($Script) { $cakeArguments += "`"$Script`"" } +if ($Target) { $cakeArguments += "--target=`"$Target`"" } +if ($Configuration) { $cakeArguments += "--configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "--verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "--showdescription" } +if ($DryRun) { $cakeArguments += "--dryrun" } +$cakeArguments += $ScriptArgs # Start Cake -$path = Split-Path -Parent $MyInvocation.MyCommand.Definition -$Script = "$path/build.cake" - -Write-Host "Bootstrapping Cake..." -Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" --bootstrap" -if ($LASTEXITCODE -ne 0) { - throw "An error occurred while bootstrapping Cake." -} - Write-Host "Running build script..." -Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" -exit $LASTEXITCODE \ No newline at end of file +Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" +$cakeExitCode = $LASTEXITCODE + +# Clean up environment variables that were created earlier in this bootstrapper +$env:CAKE_PATHS_TOOLS = $null +$env:CAKE_PATHS_ADDINS = $null +$env:CAKE_PATHS_MODULES = $null + +# Return exit code +exit $cakeExitCode diff --git a/build/tools/packages.config b/build/tools/packages.config index c0ec5b2ff..4dc0269f5 100644 --- a/build/tools/packages.config +++ b/build/tools/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file From 2444b55bf41c1a485379fa2c7e6307340da9de96 Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Fri, 9 Apr 2021 09:43:27 +0530 Subject: [PATCH 031/299] Format PowerShell Scripts Using VSCode's PowerShell extension, format and refactor the scripts! --- build/Find-WindowsSDKVersions.ps1 | 60 +++++------- build/Install-WindowsSDK.ps1 | 12 ++- build/Install-WindowsSdkISO.ps1 | 152 +++++++++++------------------- build/Sign-Package.ps1 | 6 +- build/build.cake | 13 ++- build/build.ps1 | 8 +- 6 files changed, 100 insertions(+), 151 deletions(-) diff --git a/build/Find-WindowsSDKVersions.ps1 b/build/Find-WindowsSDKVersions.ps1 index ab6aa50e9..2649ef7e4 100644 --- a/build/Find-WindowsSDKVersions.ps1 +++ b/build/Find-WindowsSDKVersions.ps1 @@ -5,12 +5,11 @@ $ErrorActionPreference = 'Stop' # Unique set of Windows SDK versions referenced in files $versions = New-Object System.Collections.Generic.HashSet[System.String] -function Get-Nodes -{ +function Get-Nodes { param( - [parameter(ValueFromPipeline=$true)] + [parameter(ValueFromPipeline = $true)] [xml] $xml, - [parameter(Mandatory=$true)] + [parameter(Mandatory = $true)] [string] $nodeName) # Try the old style csproj. Also format required for .targets and .props files @@ -24,10 +23,9 @@ function Get-Nodes return $r } -function Get-NodeValue -{ +function Get-NodeValue { param( - [parameter(ValueFromPipeline=$true)] + [parameter(ValueFromPipeline = $true)] [xml] $xml, [string] $nodeName) @@ -42,10 +40,9 @@ function Get-NodeValue return [string]"" } -function Get-SdkVersion -{ +function Get-SdkVersion { param( - [Parameter(ValueFromPipeline=$true)] $file) + [Parameter(ValueFromPipeline = $true)] $file) [xml] $xml = Get-Content $file @@ -67,26 +64,22 @@ function Get-SdkVersion $versions.Add("10.0." + $version + ".0") | Out-Null } -function Test-RegistryPathAndValue -{ +function Test-RegistryPathAndValue { param ( - [parameter(Mandatory=$true)] + [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $path, - [parameter(Mandatory=$true)] + [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $value) - try - { - if (Test-Path $path) - { + try { + if (Test-Path $path) { Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null return $true } } - catch - { + catch { } return $false @@ -101,24 +94,18 @@ function Test-InstallWindowsSdk([string] $WindowsSDKVersion) { $WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options" - if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) - { + if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) { # A Windows SDK is installed # Is an SDK of our version installed with the options we need? - if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") - { + if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") { # It appears we have what we need. Double check the disk $sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey - if ($sdkRoot) - { - if (Test-Path $sdkRoot) - { + if ($sdkRoot) { + if (Test-Path $sdkRoot) { $refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion" - if (Test-Path $refPath) - { + if (Test-Path $refPath) { $umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion" - if (Test-Path $umdPath) - { + if (Test-Path $umdPath) { # Pretty sure we have what we need $retval = $false } @@ -131,13 +118,13 @@ function Test-InstallWindowsSdk([string] $WindowsSDKVersion) { return $retval } -if(!$PSScriptRoot){ +if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } Write-Host -NoNewline "Locating referenced Windows SDK versions..." -Get-ChildItem *.csproj -Recurse | ForEach-Object { get-sdkversion $_} +Get-ChildItem *.csproj -Recurse | ForEach-Object { get-sdkversion $_ } Get-ChildItem *.targets -Recurse | ForEach-Object { get-sdkversion $_ } Get-ChildItem *.props -Recurse | ForEach-Object { get-sdkversion $_ } @@ -146,7 +133,7 @@ Write-Host $anyInstallRequired = $false; -foreach($version in $versions) { +foreach ($version in $versions) { if ($version -match "10\.0\.\d{5}\.0") { $installRequired = Test-InstallWindowsSDK $version Write-Host "Windows SDK '$version' install required: $installRequired" @@ -160,6 +147,7 @@ foreach($version in $versions) { Write-Host if ($anyInstallRequired) { throw "At least one Windows SDK is missing from this machine" -} else { +} +else { Write-Host "All referenced Windows SDKs are installed!" } \ No newline at end of file diff --git a/build/Install-WindowsSDK.ps1 b/build/Install-WindowsSDK.ps1 index b664dffab..afeb2db26 100644 --- a/build/Install-WindowsSDK.ps1 +++ b/build/Install-WindowsSDK.ps1 @@ -1,6 +1,10 @@ -mkdir c:\winsdktemp -$client = new-object System.Net.WebClient -$client.DownloadFile("https://go.microsoft.com/fwlink/p/?linkid=870807","c:\winsdktemp\winsdksetup.exe") +$WinSdkTempDir = "C:\WinSdkTemp\" +$WinSdkSetupExe = "C:\WinSdkTemp\" + "WinSdkSetup.exe" -Start-Process -Wait "c:\winsdktemp\winsdksetup.exe" "/features OptionId.UWPCpp /q" \ No newline at end of file +mkdir $WinSdkTempDir + +$client = [System.Net.WebClient]::new() +$client.DownloadFile("https://go.microsoft.com/fwlink/p/?linkid=870807", $WinSdkSetupExe) + +Start-Process -Wait $WinSdkSetupExe "/features OptionId.UWPCpp /q" \ No newline at end of file diff --git a/build/Install-WindowsSdkISO.ps1 b/build/Install-WindowsSdkISO.ps1 index ea9cf034d..b83a3de9b 100644 --- a/build/Install-WindowsSdkISO.ps1 +++ b/build/Install-WindowsSdkISO.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] -param([Parameter(Mandatory=$true)] - [string]$buildNumber) +param([Parameter(Mandatory = $true)] + [string]$buildNumber) # Ensure the error action preference is set to the default for PowerShell3, 'Stop' $ErrorActionPreference = 'Stop' @@ -14,11 +14,10 @@ $WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed O $StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification" $PublicKeyTokens = @("31bf3856ad364e35") -function Download-File -{ +function Download-File { param ([string] $outDir, - [string] $downloadUrl, - [string] $downloadName) + [string] $downloadUrl, + [string] $downloadName) $downloadPath = Join-Path $outDir "$downloadName.download" $downloadDest = Join-Path $outDir $downloadName @@ -26,21 +25,17 @@ function Download-File Write-Host -NoNewline "Downloading $downloadName..." - try - { + try { $webclient = new-object System.Net.WebClient $webclient.DownloadFile($downloadUrl, $downloadPath) } - catch [System.Net.WebException] - { + catch [System.Net.WebException] { Write-Host Write-Warning "Failed to fetch updated file from $downloadUrl" - if (!(Test-Path $downloadDest)) - { + if (!(Test-Path $downloadDest)) { throw "$downloadName was not found at $downloadDest" } - else - { + else { Write-Warning "$downloadName may be out of date" } } @@ -50,8 +45,7 @@ function Download-File $downloadDestTemp = $downloadPath; # Delete and rename to final dest - if (Test-Path -PathType Container $downloadDest) - { + if (Test-Path -PathType Container $downloadDest) { [System.IO.Directory]::Delete($downloadDest, $true) } @@ -61,20 +55,16 @@ function Download-File return $downloadDest } -function Get-ISODriveLetter -{ +function Get-ISODriveLetter { param ([string] $isoPath) $diskImage = Get-DiskImage -ImagePath $isoPath - if ($diskImage) - { + if ($diskImage) { $volume = Get-Volume -DiskImage $diskImage - if ($volume) - { + if ($volume) { $driveLetter = $volume.DriveLetter - if ($driveLetter) - { + if ($driveLetter) { $driveLetter += ":" return $driveLetter } @@ -84,15 +74,13 @@ function Get-ISODriveLetter return $null } -function Mount-ISO -{ +function Mount-ISO { param ([string] $isoPath) # Check if image is already mounted $isoDrive = Get-ISODriveLetter $isoPath - if (!$isoDrive) - { + if (!$isoDrive) { Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null } @@ -100,84 +88,68 @@ function Mount-ISO Write-Verbose "$isoPath mounted to ${isoDrive}:" } -function Dismount-ISO -{ +function Dismount-ISO { param ([string] $isoPath) $isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter - if ($isoDrive) - { + if ($isoDrive) { Write-Verbose "$isoPath dismounted" Dismount-DiskImage -ImagePath $isoPath | Out-Null } } -function Disable-StrongName -{ +function Disable-StrongName { param ([string] $publicKeyToken = "*") reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null - if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") - { + if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null } } -function Test-Admin -{ +function Test-Admin { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal $identity $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } -function Test-RegistryPathAndValue -{ +function Test-RegistryPathAndValue { param ( - [parameter(Mandatory=$true)] + [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $path, - [parameter(Mandatory=$true)] + [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $value) - try - { - if (Test-Path $path) - { + try { + if (Test-Path $path) { Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null return $true } } - catch - { + catch { } return $false } -function Test-InstallWindowsSDK -{ +function Test-InstallWindowsSDK { $retval = $true - if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) - { + if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey) { # A Windows SDK is installed # Is an SDK of our version installed with the options we need? - if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") - { + if (Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value "$WindowsSDKOptions") { # It appears we have what we need. Double check the disk $sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey - if ($sdkRoot) - { - if (Test-Path $sdkRoot) - { + if ($sdkRoot) { + if (Test-Path $sdkRoot) { $refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion" - if (Test-Path $refPath) - { + if (Test-Path $refPath) { $umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion" - if (Test-Path $umdPath) - { + if (Test-Path $umdPath) { # Pretty sure we have what we need $retval = $false } @@ -190,13 +162,10 @@ function Test-InstallWindowsSDK return $retval } -function Test-InstallStrongNameHijack -{ - foreach($publicKeyToken in $PublicKeyTokens) - { +function Test-InstallStrongNameHijack { + foreach ($publicKeyToken in $PublicKeyTokens) { $key = "$StrongNameRegPath\*,$publicKeyToken" - if (!(Test-Path $key)) - { + if (!(Test-Path $key)) { return $true } } @@ -206,51 +175,42 @@ function Test-InstallStrongNameHijack Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..." $InstallWindowsSDK = Test-InstallWindowsSDK -if ($InstallWindowsSDK) -{ +if ($InstallWindowsSDK) { Write-Host "Installation required" } -else -{ +else { Write-Host "INSTALLED" } $StrongNameHijack = Test-InstallStrongNameHijack Write-Host -NoNewline "Checking if StrongName bypass required..." -if ($StrongNameHijack) -{ +if ($StrongNameHijack) { Write-Host "REQUIRED" } -else -{ +else { Write-Host "Done" } -if ($StrongNameHijack -or $InstallWindowsSDK) -{ - if (!(Test-Admin)) - { +if ($StrongNameHijack -or $InstallWindowsSDK) { + if (!(Test-Admin)) { Write-Host throw "ERROR: Elevation required" } } -if ($InstallWindowsSDK) -{ +if ($InstallWindowsSDK) { # Static(ish) link for Windows SDK # Note: there is a delay from Windows SDK announcements to availability via the static link $uri = "https://software-download.microsoft.com/download/sg/Windows_InsiderPreview_SDK_en-us_$($buildNumber)_1.iso"; - if ($env:TEMP -eq $null) - { + if ($null -eq $env:TEMP) { $env:TEMP = Join-Path $env:SystemDrive 'temp' } $winsdkTempDir = Join-Path $env:TEMP "WindowsSDK" - if (![System.IO.Directory]::Exists($winsdkTempDir)) - { + if (![System.IO.Directory]::Exists($winsdkTempDir)) { [void][System.IO.Directory]::CreateDirectory($winsdkTempDir) } @@ -260,41 +220,35 @@ if ($InstallWindowsSDK) $downloadFile = Download-File $winsdkTempDir $uri $file # TODO Check if zip, exe, iso, etc. - try - { + try { Write-Host -NoNewline "Mounting ISO $file..." Mount-ISO $downloadFile Write-Host "Done" $isoDrive = Get-ISODriveLetter $downloadFile - if (Test-Path $isoDrive) - { + if (Test-Path $isoDrive) { Write-Host -NoNewLine "Installing WinSDK..." $setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe" Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q" Write-Host "Done" } - else - { + else { throw "Could not find mounted ISO at ${isoDrive}" } } - finally - { + finally { Write-Host -NoNewline "Dismounting ISO $file..." - #Dismount-ISO $downloadFile + # Dismount-ISO $downloadFile Write-Host "Done" } } -if ($StrongNameHijack) -{ +if ($StrongNameHijack) { Write-Host -NoNewline "Disabling StrongName for Windows SDK..." - foreach($key in $PublicKeyTokens) - { + foreach ($key in $PublicKeyTokens) { Disable-StrongName $key } diff --git a/build/Sign-Package.ps1 b/build/Sign-Package.ps1 index d8def95e1..8509c018b 100644 --- a/build/Sign-Package.ps1 +++ b/build/Sign-Package.ps1 @@ -2,7 +2,7 @@ $currentDirectory = split-path $MyInvocation.MyCommand.Definition # See if we have the ClientSecret available -if([string]::IsNullOrEmpty($Env:SignClientSecret)){ +if ([string]::IsNullOrEmpty($Env:SignClientSecret)) { Write-Host "Client Secret not found, not signing packages" return; } @@ -13,9 +13,9 @@ dotnet tool install --tool-path . SignClient $appSettings = "$currentDirectory\SignClientSettings.json" -$nupkgs = gci $Env:ArtifactDirectory\*.nupkg -recurse | Select -ExpandProperty FullName +$nupkgs = Get-ChildItem $Env:ArtifactDirectory\*.nupkg -recurse | Select-Object -ExpandProperty FullName -foreach ($nupkg in $nupkgs){ +foreach ($nupkg in $nupkgs) { Write-Host "Submitting $nupkg for signing" .\SignClient 'sign' -c $appSettings -i $nupkg -r $Env:SignClientUser -s $Env:SignClientSecret -n 'Windows Community Toolkit' -d 'Windows Community Toolkit' -u 'https://developer.microsoft.com/en-us/windows/uwp-community-toolkit' diff --git a/build/build.cake b/build/build.cake index 82be1b88c..9bfd2c780 100644 --- a/build/build.cake +++ b/build/build.cake @@ -176,13 +176,14 @@ Task("InheritDoc") .Does(() => { Information("\nDownloading InheritDoc..."); - var installSettings = new NuGetInstallSettings { + var installSettings = new NuGetInstallSettings + { ExcludeVersion = true, Version = inheritDocVersion, OutputDirectory = toolsDir }; - NuGetInstall(new []{"InheritDoc"}, installSettings); + NuGetInstall(new[] {"InheritDoc"}, installSettings); var args = new ProcessArgumentBuilder() .AppendSwitchQuoted("-b", baseDir) @@ -210,7 +211,8 @@ Task("Package") .Does(() => { // Invoke the pack target in the end - var buildSettings = new MSBuildSettings { + var buildSettings = new MSBuildSettings + { MaxCpuCount = 0 } .SetConfiguration("Release") @@ -334,12 +336,13 @@ Task("StyleXaml") .Does(() => { Information("\nDownloading XamlStyler..."); - var installSettings = new NuGetInstallSettings { + var installSettings = new NuGetInstallSettings + { ExcludeVersion = true, OutputDirectory = toolsDir }; - NuGetInstall(new []{"xamlstyler.console"}, installSettings); + NuGetInstall(new[] {"xamlstyler.console"}, installSettings); Func exclude_objDir = fileSystemInfo => !fileSystemInfo.Path.Segments.Contains("obj"); diff --git a/build/build.ps1 b/build/build.ps1 index 18078054a..63a20a1f9 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -90,12 +90,12 @@ function MD5HashFile([string] $filePath) } finally { - if ($file -ne $null) + if ($null -ne $file) { $file.Dispose() } - if ($md5 -ne $null) + if ($null -ne $md5) { $md5.Dispose() } @@ -157,7 +157,7 @@ if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Trying to find nuget.exe in PATH..." $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 - if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + if ($null -ne $NUGET_EXE_IN_PATH -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName } @@ -175,7 +175,7 @@ if (!(Test-Path $NUGET_EXE)) { } # These are automatic variables in PowerShell Core, but not in Windows PowerShell 5.x -if (-not (Test-Path variable:global:ismacos)) { +if (-not (Test-Path variable:global:IsMacOS)) { $IsLinux = $false $IsMacOS = $false } From e921d894821d7735fd9cac66bcf7c6087299583c Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 22 Jul 2021 12:15:58 -0700 Subject: [PATCH 032/299] Updated ISettingsStorageHelper references --- .../Controls/SampleAppMarkdownRenderer.cs | 2 +- Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs | 2 +- Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs | 2 +- .../SamplePages/Object Storage/ObjectStoragePage.xaml.cs | 2 +- Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs | 2 +- UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs | 6 +++--- UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs index 4085cd815..e30989e9b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs @@ -418,7 +418,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls /// /// The local app data storage helper for storing settings. /// - private ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private readonly ApplicationDataStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(); /// /// DocFX note types and styling info, keyed by identifier. diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs index ce625ec03..6e512819c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -46,7 +46,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp public static async void EnsureCacheLatest() { - ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + var settingsStorage = ApplicationDataStorageHelper.GetCurrent(); var onlineDocsSHA = await GetDocsSHA(); var cacheSHA = settingsStorage.Read(_cacheSHAKey); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs index c43cf3aeb..a05c2c19f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Samples.cs @@ -22,7 +22,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp private static SemaphoreSlim _semaphore = new SemaphoreSlim(1); private static LinkedList _recentSamples; - private static ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private static ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(); public static async Task GetCategoryBySample(Sample sample) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs index 96079fb89..9bc0b00df 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Object Storage/ObjectStoragePage.xaml.cs @@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { public sealed partial class ObjectStoragePage { - private readonly ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private readonly ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(); public ObjectStoragePage() { diff --git a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs index 81f7a6cac..d55ccddcc 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/SystemInformation.cs @@ -26,7 +26,7 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// The instance used to save and retrieve application settings. /// - private readonly ISettingsStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Toolkit.Helpers.SystemSerializer()); + private readonly ApplicationDataStorageHelper _settingsStorage = ApplicationDataStorageHelper.GetCurrent(); /// /// The starting time of the current application session (since app launch or last move to foreground). diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index 0b27db815..e5922304f 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -15,9 +15,9 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { - private ISettingsStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); - private ISettingsStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); - private ISettingsStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); + private readonly ISettingsStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(); + private readonly ISettingsStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); + private readonly ISettingsStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); /// /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs index 7132f500a..466398d68 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_SystemInformation.cs @@ -58,7 +58,7 @@ namespace UnitTests.Helpers // Simulate a first app startup _ = (SystemInformation)Activator.CreateInstance(typeof(SystemInformation), nonPublic: true); - ISettingsStorageHelper settingsStorage = ApplicationDataStorageHelper.GetCurrent(new Microsoft.Toolkit.Helpers.SystemSerializer()); + var settingsStorage = ApplicationDataStorageHelper.GetCurrent(); PackageVersion previousVersion = new() { Build = 42, Major = 1111, Minor = 2222, Revision = 12345 }; settingsStorage.Save("currentVersion", previousVersion.ToFormattedString()); From e61c0cdd044916ad9eb1e89972c874e8c523c732 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 22 Jul 2021 12:26:08 -0700 Subject: [PATCH 033/299] Update Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs Co-authored-by: Sergio Pedri --- Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index 2ca3397ee..df3b1600c 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -25,7 +25,7 @@ namespace Microsoft.Toolkit.Helpers /// Path to the file that contains the object. /// Default value of the object. /// Waiting task until completion with the object in the file. - Task ReadFileAsync(string filePath, T? @default = default); + Task ReadFileAsync(string filePath, T? @default = default); /// /// Retrieves the listings for a folder and the item types. From 0e414ec54d98059deb50fd2c72ede34c23502f2b Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 22 Jul 2021 12:57:41 -0700 Subject: [PATCH 034/299] Removed default param from ISettingsStorageHelper.Read method and updated exception handling in ApplicationDataStorageHelper for missing keys --- .../ApplicationDataStorageHelper.cs | 38 ++++++++++++++++--- .../ObjectStorage/ISettingsStorageHelper.cs | 5 ++- .../Helpers/Test_StorageHelper.cs | 6 +-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 4f1ddbb41..75c8d2d7e 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -81,7 +81,13 @@ namespace Microsoft.Toolkit.Uwp.Helpers return Settings.Values.ContainsKey(key); } - /// + /// + /// Retrieves a single item by its key. + /// + /// Type of object retrieved. + /// Key of the object. + /// Default value of the object. + /// The TValue object public T Read(string key, T @default = default) { if (!Settings.Values.TryGetValue(key, out var valueObj) || valueObj == null) @@ -92,6 +98,17 @@ namespace Microsoft.Toolkit.Uwp.Helpers return Serializer.Deserialize(valueObj as string); } + /// + public T Read(string key) + { + if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj != null) + { + return Serializer.Deserialize(valueObj as string); + } + + throw new KeyNotFoundException(key); + } + /// public void Save(string key, T value) { @@ -101,7 +118,11 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// public void Delete(string key) { - Settings.Values.Remove(key); + var removed = Settings.Values.Remove(key); + if (!removed) + { + throw new KeyNotFoundException(key); + } } /// @@ -196,12 +217,19 @@ namespace Microsoft.Toolkit.Uwp.Helpers /// /// Key of the composite (that contains settings). /// Key of the object. + /// Throws when the specified composite/settings key is not found. public void Delete(string compositeKey, string key) { - if (KeyExists(compositeKey)) + if (!KeyExists(compositeKey)) { - ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; - composite.Remove(key); + throw new KeyNotFoundException($"Composite key not found: {compositeKey}"); + } + + ApplicationDataCompositeValue composite = (ApplicationDataCompositeValue)Settings.Values[compositeKey]; + + if (!composite.Remove(key)) + { + throw new KeyNotFoundException($"Settings key not found: {key}"); } } diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index 43a03a72a..f1ea88276 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -18,9 +18,9 @@ namespace Microsoft.Toolkit.Helpers /// /// Type of object retrieved. /// Key of the object. - /// Default value of the object. + /// Throws when the specified key is not found. /// The TValue object - TValue? Read(TKey key, TValue? @default = default); + TValue? Read(TKey key); /// /// Saves a single item by its key. @@ -34,6 +34,7 @@ namespace Microsoft.Toolkit.Helpers /// Deletes a single item by its key. /// /// Key of the object. + /// Throws when the specified key is not found. void Delete(TKey key); /// diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index e5922304f..be0b192e6 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -15,9 +15,9 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { - private readonly ISettingsStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(); - private readonly ISettingsStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); - private readonly ISettingsStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); + private readonly ApplicationDataStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(); + private readonly ApplicationDataStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); + private readonly ApplicationDataStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); /// /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. From 51d58cd014ec1f1b04f9b56d3d6ba68b4a3832da Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 22 Jul 2021 13:23:51 -0700 Subject: [PATCH 035/299] Updated JsonObjectSerializer and SystemTextJsonSerializer in Uwp test app --- UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs | 6 +++--- UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index c0d492467..160c52aec 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -14,7 +14,7 @@ namespace UnitTests.UWP.Helpers /// internal class JsonObjectSerializer : IObjectSerializer { - public T Deserialize(object value) + public T Deserialize(string value) { var type = typeof(T); var typeInfo = type.GetTypeInfo(); @@ -29,7 +29,7 @@ namespace UnitTests.UWP.Helpers return JsonConvert.DeserializeObject((string)value); } - public object Serialize(T value) + public string Serialize(T value) { var type = typeof(T); var typeInfo = type.GetTypeInfo(); @@ -38,7 +38,7 @@ namespace UnitTests.UWP.Helpers // This if/return combo is to maintain compatibility with 6.1.1 if (typeInfo.IsPrimitive || type == typeof(string)) { - return value; + return value.ToString(); } return JsonConvert.SerializeObject(value); diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs index 7871089f0..6204426a1 100644 --- a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs @@ -13,8 +13,8 @@ namespace UnitTests.UWP.Helpers /// internal class SystemTextJsonSerializer : IObjectSerializer { - public T Deserialize(object value) => JsonSerializer.Deserialize(value as string); + public T Deserialize(string value) => JsonSerializer.Deserialize(value); - public object Serialize(T value) => JsonSerializer.Serialize(value); + public string Serialize(T value) => JsonSerializer.Serialize(value); } } \ No newline at end of file From fd6c39ec5ebdc63351056dc5216394cfe5298fee Mon Sep 17 00:00:00 2001 From: Arcadio Garcia Salvadores Date: Wed, 14 Jul 2021 22:04:11 -0700 Subject: [PATCH 036/299] Shorter expression strings --- .../ExpressionNodes/ExpressionNode.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs index 51e6fbd3b..da200bcf3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs @@ -283,9 +283,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions // Create a map to store the generated paramNames for each CompObj _compObjToParamNameMap = new Dictionary(); + var paramCount = 0; foreach (var compObj in compObjects) { - string paramName = Guid.NewGuid().ToUppercaseAsciiLetters(); + string paramName = UniqueParamNameFromIndex(paramCount++); // Guid.NewGuid().ToUppercaseAsciiLetters(); _compObjToParamNameMap.Add(compObj, paramName); } @@ -312,6 +313,22 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions refNode.ParamName = paramName; } } + + //Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, AB... + string UniqueParamNameFromIndex(int i) + { + var alphabetLength = 'Z' - 'A' + 1; + var paramName = ((char)('A' + (i % alphabetLength))).ToString(); + + while (i / alphabetLength > 0) + { + i = (i / alphabetLength) - 1; + var nextCharacter = (char)('A' + (i % alphabetLength)); + paramName = nextCharacter + paramName; + } + + return paramName; + } } /// From 94337814c3c38312c73b92e1be5de4411c43360c Mon Sep 17 00:00:00 2001 From: Arcadio Garcia Salvadores <9416356+arcadiogarcia@users.noreply.github.com> Date: Tue, 20 Jul 2021 15:11:09 -0700 Subject: [PATCH 037/299] Update Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs Co-authored-by: Rosario Pulella --- .../Expressions/ExpressionNodes/ExpressionNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs index da200bcf3..0b2bcb016 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs @@ -314,7 +314,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions } } - //Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, AB... + // Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, AB... string UniqueParamNameFromIndex(int i) { var alphabetLength = 'Z' - 'A' + 1; @@ -687,4 +687,4 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions /// The subchannels. protected internal string[] Subchannels { get; set; } } -} \ No newline at end of file +} From 50008c0db1c7ba09a3f2ec5d69ec2b9420f5291c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 23 Jul 2021 00:36:44 +0200 Subject: [PATCH 038/299] Optimized CreateUniqueParamNameFromIndex method --- .../ExpressionNodes/ExpressionNode.cs | 38 +++++++++++++------ ...Microsoft.Toolkit.Uwp.UI.Animations.csproj | 1 + .../Properties/SkipLocalsInitAttribute.cs | 24 ++++++++++++ 3 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Properties/SkipLocalsInitAttribute.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs index 0b2bcb016..e02d45002 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Numerics; +using System.Runtime.CompilerServices; using Windows.UI; using Windows.UI.Composition; @@ -283,10 +284,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions // Create a map to store the generated paramNames for each CompObj _compObjToParamNameMap = new Dictionary(); - var paramCount = 0; + var paramCount = 0u; foreach (var compObj in compObjects) { - string paramName = UniqueParamNameFromIndex(paramCount++); // Guid.NewGuid().ToUppercaseAsciiLetters(); + string paramName = CreateUniqueParamNameFromIndex(paramCount++); _compObjToParamNameMap.Add(compObj, paramName); } @@ -314,20 +315,35 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions } } - // Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, AB... - string UniqueParamNameFromIndex(int i) + // Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, BA... + // This implementation aggregates characters in reverse order to avoid having to + // precompute the exact number of characters in the resulting string. This is not + // important in this context as the only critical property to maintain is to have + // a unique mapping to each input value to the resulting sequence of letters. + [SkipLocalsInit] + static unsafe string CreateUniqueParamNameFromIndex(uint i) { - var alphabetLength = 'Z' - 'A' + 1; - var paramName = ((char)('A' + (i % alphabetLength))).ToString(); + const int alphabetLength = 'Z' - 'A' + 1; - while (i / alphabetLength > 0) + // The total length of the resulting sequence is guaranteed to always + // be less than 8, given that log26(4294967295) ≈ 6.8. In this case we + // are just allocating the immediate next power of two following that. + // Note: this is using a char* buffer instead of Span as the latter + // is not referenced here, and we don't want to pull in an extra package. + char* characters = stackalloc char[8]; + + characters[0] = (char)('A' + (i % alphabetLength)); + + int totalCharacters = 1; + + while ((i /= alphabetLength) > 0) { - i = (i / alphabetLength) - 1; - var nextCharacter = (char)('A' + (i % alphabetLength)); - paramName = nextCharacter + paramName; + i--; + + characters[totalCharacters++] = (char)('A' + (i % alphabetLength)); } - return paramName; + return new string(characters, 0, totalCharacters); } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj index c51450237..130fdd806 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj @@ -2,6 +2,7 @@ uap10.0.17763 + true diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Properties/SkipLocalsInitAttribute.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Properties/SkipLocalsInitAttribute.cs new file mode 100644 index 000000000..4f0d98a5f --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Properties/SkipLocalsInitAttribute.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + /// + /// Used to indicate to the compiler that the .locals init flag should not be set in method headers. + /// + /// Internal copy from the BCL attribute. + [AttributeUsage( + AttributeTargets.Module | + AttributeTargets.Class | + AttributeTargets.Struct | + AttributeTargets.Interface | + AttributeTargets.Constructor | + AttributeTargets.Method | + AttributeTargets.Property | + AttributeTargets.Event, + Inherited = false)] + internal sealed class SkipLocalsInitAttribute : Attribute + { + } +} \ No newline at end of file From 27cae2eec777fcee25dfde1523d1013e1f0a06e8 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Thu, 22 Jul 2021 16:13:07 -0700 Subject: [PATCH 039/299] Added extension methods for ISettingsStorageHelper --- .../ISettingsStorageHelperExtensions.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs diff --git a/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs b/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs new file mode 100644 index 000000000..fa0664a1b --- /dev/null +++ b/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Toolkit.Helpers; + +namespace Microsoft.Toolkit.Extensions +{ + /// + /// Helpers methods for working with implementations. + /// + public static class ISettingsStorageHelperExtensions + { + /// + /// Attempts to read the provided key and return the value. + /// If the key is not found, the fallback value will be used instead. + /// + /// The type of key used to lookup the object. + /// The type of object value expected. + /// The storage helper instance fo read from. + /// The key of the target object. + /// An alternative value returned if the read fails. + /// The value of the target object, or the fallback value. + public static TValue? GetValueOrDefault(this ISettingsStorageHelper storageHelper, TKey key, TValue? fallback = default) + where TKey : notnull + { + try + { + return storageHelper.Read(key); + } + catch (KeyNotFoundException) + { + return fallback; + } + } + + /// + /// Attempts to perform read the provided key and returns a boolean indicator of success. + /// If the key is not found, the fallback value will be used instead. + /// + /// The type of key used to lookup the object. + /// The type of object value expected. + /// The storage helper instance to read from. + /// The key of the target object. + /// The value of the target object, or the fallback value. + /// An alternate value returned if the read fails. + /// A boolean indicator of success. + public static bool TryRead(this ISettingsStorageHelper storageHelper, TKey key, out TValue? value, TValue? fallback = default) + where TKey : notnull + { + try + { + value = storageHelper.Read(key); + return true; + } + catch (KeyNotFoundException) + { + value = fallback; + return false; + } + } + + /// + /// Attempts to remove an object by key and returns a boolean indicator of success. + /// If the key is not found, the method will return false. + /// + /// The type of key used to lookup the object. + /// The storage helper instance to delete from. + /// The key of the target object. + /// A boolean indicator of success. + public static bool TryDelete(this ISettingsStorageHelper storageHelper, TKey key) + where TKey : notnull + { + try + { + storageHelper.Delete(key); + return true; + } + catch (KeyNotFoundException) + { + return false; + } + } + } +} From 58dda08a842b9c286f8c9f940c37b85cb6086e12 Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Fri, 23 Jul 2021 13:35:02 -0500 Subject: [PATCH 040/299] Switched to WinUI NavView, template broken --- .../TabbedCommandBar/TabbedCommandBar.cs | 3 +- .../TabbedCommandBar/TabbedCommandBar.xaml | 390 +++++++++--------- 2 files changed, 190 insertions(+), 203 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs index d569f476c..3a79a7db2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs @@ -6,8 +6,9 @@ using System.Linq; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Animation; +using NavigationView = Microsoft.UI.Xaml.Controls.NavigationView; +using NavigationViewSelectionChangedEventArgs = Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs; namespace Microsoft.Toolkit.Uwp.UI.Controls { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml index 2bbfdf236..b7f468427 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml @@ -1,7 +1,8 @@ + xmlns:muxc="using:Microsoft.UI.Xaml.Controls" + xmlns:contract4Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,4)"> @@ -23,188 +24,22 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - - - - - - - - - - - + + + + @@ -213,42 +48,193 @@ - - - - - - - - - - - - - - - - - + - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 23 Jul 2021 16:12:57 -0700 Subject: [PATCH 041/299] Update TabbedCommandBar DefaultStyleResourceUri to point to Toolkit vs. WinUI See https://github.com/microsoft/microsoft-ui-xaml/issues/3502 Also update TabbedCommandBarItem DataTemplate to use MUX version of NavigationViewItem --- .../TabbedCommandBar/TabbedCommandBar.cs | 1 + .../TabbedCommandBarItem.xaml | 481 +++++++++--------- 2 files changed, 251 insertions(+), 231 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs index 3a79a7db2..83f78c972 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs @@ -37,6 +37,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls public TabbedCommandBar() { DefaultStyleKey = typeof(TabbedCommandBar); + DefaultStyleResourceUri = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml"); SelectionChanged += SelectedItemChanged; } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml index 52e480179..4ddf46036 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml @@ -1,15 +1,22 @@ + xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" + xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> - - - + + + - - - + - - - - - - - + + + + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Background="{TemplateBinding Background}" /> + Grid.Column="2" + Background="{TemplateBinding Background}" /> + CornerRadius="{TemplateBinding CornerRadius}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c43aaeeaf4003fe50f6493c43f2ca44567507b83 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 23 Jul 2021 16:48:00 -0700 Subject: [PATCH 042/299] Move SelectedItem setting for TabbedCommandBar to mitigate binding timing issue --- .../TabbedCommandBar/TabbedCommandBar.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs index 83f78c972..beb71b3a6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs @@ -40,6 +40,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls DefaultStyleResourceUri = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml"); SelectionChanged += SelectedItemChanged; + Loaded += TabbedCommandBar_Loaded; } /// @@ -57,6 +58,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls _tabbedCommandBarContentBorder = GetTemplateChild("PART_TabbedCommandBarContentBorder") as Border; _tabChangedStoryboard = GetTemplateChild("TabChangedStoryboard") as Storyboard; + // TODO: We could maybe optimize and use a lower-level Loaded event for what's hosting the MenuItems + // to set SelectedItem, but then we may have to pull in another template part, so think we're OK + // to do the Loaded event at the top level. + } + + private void TabbedCommandBar_Loaded(object sender, RoutedEventArgs e) + { + // We need to select the item after the template is realized, otherwise the SelectedItem's + // DataTemplate bindings don't properly navigate the visual tree. SelectedItem = MenuItems.FirstOrDefault(); } From 4590172e32d91414ced541eed20b84a03f56f934 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Mon, 26 Jul 2021 02:15:43 +0530 Subject: [PATCH 043/299] Changed SmoothScrollIntoView method to truly asynchronous --- .../ListViewExtensionsPage.xaml.cs | 4 +- ...ListViewExtensions.SmoothScrollIntoView.cs | 61 ++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs index b79849ef1..cb23e89d7 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs @@ -37,7 +37,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages private void Load() { - SampleController.Current.RegisterNewCommand("Start Smooth Scroll", (sender, args) => + SampleController.Current.RegisterNewCommand("Start Smooth Scroll", async (sender, args) => { var index = int.TryParse(IndexInput.Text, out var i) ? i : 0; var itemPlacement = ItemPlacementInput.SelectedItem switch @@ -55,7 +55,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages var scrollIfVisibile = ScrollIfVisibileInput.IsChecked ?? true; var additionalHorizontalOffset = int.TryParse(AdditionalHorizontalOffsetInput.Text, out var ho) ? ho : 0; var additionalVerticalOffset = int.TryParse(AdditionalVerticalOffsetInput.Text, out var vo) ? vo : 0; - sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset); + await sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset); }); if (sampleListView != null) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index 419471d95..6fba693a3 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -26,7 +26,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// Set false to disable scrolling when the corresponding item is in view /// Adds additional horizontal offset /// Adds additional vertical offset - /// Note: Even though this return , it will not wait until the scrolling completes + /// Returns that completes after scrolling public static async Task SmoothScrollIntoViewWithIndexAsync(this ListViewBase listViewBase, int index, ScrollItemPlacement itemPlacement = ScrollItemPlacement.Default, bool disableAnimation = false, bool scrollIfVisible = true, int additionalHorizontalOffset = 0, int additionalVerticalOffset = 0) { if (index > (listViewBase.Items.Count - 1)) @@ -56,15 +56,15 @@ namespace Microsoft.Toolkit.Uwp.UI previousXOffset = scrollViewer.HorizontalOffset; previousYOffset = scrollViewer.VerticalOffset; - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); - void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: null); + void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: default); try { scrollViewer.ViewChanged += ViewChanged; listViewBase.ScrollIntoView(listViewBase.Items[index], ScrollIntoViewAlignment.Leading); - await tcs.Task; + await tcs.Task.ConfigureAwait(true); } finally { @@ -80,20 +80,7 @@ namespace Microsoft.Toolkit.Uwp.UI // Scrolling back to previous position if (isVirtualizing) { - var tcs = new TaskCompletionSource(); - - void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: null); - - try - { - scrollViewer.ViewChanged += ViewChanged; - scrollViewer.ChangeView(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true); - await tcs.Task; - } - finally - { - scrollViewer.ViewChanged -= ViewChanged; - } + await scrollViewer.ChangeViewAsync(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true).ConfigureAwait(true); } var listViewBaseWidth = listViewBase.ActualWidth; @@ -185,7 +172,7 @@ namespace Microsoft.Toolkit.Uwp.UI } } - scrollViewer.ChangeView(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation); + await scrollViewer.ChangeViewAsync(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation).ConfigureAwait(true); } /// @@ -198,10 +185,42 @@ namespace Microsoft.Toolkit.Uwp.UI /// Set true to disable scrolling when the corresponding item is in view /// Adds additional horizontal offset /// Adds additional vertical offset - /// Note: Even though this return , it will not wait until the scrolling completes + /// Returns that completes after scrolling public static async Task SmoothScrollIntoViewWithItemAsync(this ListViewBase listViewBase, object item, ScrollItemPlacement itemPlacement = ScrollItemPlacement.Default, bool disableAnimation = false, bool scrollIfVisibile = true, int additionalHorizontalOffset = 0, int additionalVerticalOffset = 0) { - await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset); + await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset).ConfigureAwait(true); } + + /// + /// Changes the view of asynchronous. + /// + /// The scroll viewer. + /// The horizontal offset. + /// The vertical offset. + /// The zoom factor. + /// if set to true disable animation. + private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) + { + var tcs = new TaskCompletionSource(); + + void ViewChanged(object _, ScrollViewerViewChangedEventArgs __) => tcs.TrySetResult(result: default); + + try + { + scrollViewer.ViewChanged += ViewChanged; + scrollViewer.ChangeView(horizontalOffset, verticalOffset, zoomFactor, disableAnimation); + await tcs.Task.ConfigureAwait(true); + } + finally + { + scrollViewer.ViewChanged -= ViewChanged; + } + } + + /// + /// Used as a placeholder TResult to indicate that a ]]> has a void TResult + /// + /// + private struct VoidResult { } } } From 831aef6bce0bf212a154a66af436d3fb8a5bd818 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Mon, 26 Jul 2021 09:45:45 -0700 Subject: [PATCH 044/299] Update string reliance on WinUI NavigationView --- .../ListDetailsView/ListDetailsView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs index 710d5dc5e..c7ba7a161 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -199,7 +199,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls _frame.Navigating -= OnFrameNavigating; } - _navigationView = this.FindAscendants().FirstOrDefault(p => p.GetType().FullName == "Microsoft.UI.Xaml.Controls.NavigationView"); + _navigationView = this.FindAscendant(); _frame = this.FindAscendant(); if (_frame != null) { From 6f1869e70dce6ab3a9b10b38be2140772d27ab27 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Mon, 26 Jul 2021 22:38:25 +0530 Subject: [PATCH 045/299] Fixed SA1502 in VoidResult struct Co-authored-by: Rosario Pulella --- .../ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index 6fba693a3..0efda60a5 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -221,6 +221,8 @@ namespace Microsoft.Toolkit.Uwp.UI /// Used as a placeholder TResult to indicate that a ]]> has a void TResult /// /// - private struct VoidResult { } + private struct VoidResult + { + } } } From 13c3e2f4918b81124d80254de3ec8a1e5c8f3375 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 26 Jul 2021 11:50:08 -0700 Subject: [PATCH 046/299] Update Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs Co-authored-by: Sergio Pedri --- .../Helpers/ObjectStorage/ISettingsStorageHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index f1ea88276..6bc4063d2 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Helpers /// Service interface used to store data using key value pairs. /// /// The type of keys to use for accessing values. - public interface ISettingsStorageHelper + public interface ISettingsStorageHelper where TKey : notnull { /// From b472cc849679d5d811725851399cad10eb3b7799 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 26 Jul 2021 12:15:46 -0700 Subject: [PATCH 047/299] PR updates --- Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs | 4 ---- .../Helpers/ObjectStorage/ISettingsStorageHelper.cs | 2 +- Microsoft.Toolkit/Microsoft.Toolkit.csproj | 4 +--- UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs index 266ec1579..5a8177b4f 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Toolkit.Helpers { /// @@ -17,7 +15,6 @@ namespace Microsoft.Toolkit.Helpers /// The type of the object to serialize. /// The object to serialize. /// The serialized object. - [return: NotNullIfNotNull("value")] string? Serialize(T value); /// @@ -26,7 +23,6 @@ namespace Microsoft.Toolkit.Helpers /// The type of the deserialized object. /// The string to deserialize. /// The deserialized object. - [return: NotNullIfNotNull("value")] T Deserialize(string value); } } \ No newline at end of file diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index f1ea88276..d8dba333c 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.Helpers /// Type of object retrieved. /// Key of the object. /// Throws when the specified key is not found. - /// The TValue object + /// The object for key. TValue? Read(TKey key); /// diff --git a/Microsoft.Toolkit/Microsoft.Toolkit.csproj b/Microsoft.Toolkit/Microsoft.Toolkit.csproj index 31afd868c..6efb4c972 100644 --- a/Microsoft.Toolkit/Microsoft.Toolkit.csproj +++ b/Microsoft.Toolkit/Microsoft.Toolkit.csproj @@ -21,11 +21,9 @@ NETSTANDARD2_1_OR_GREATER - + - - diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index 160c52aec..c9b4aeaa3 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -26,7 +26,7 @@ namespace UnitTests.UWP.Helpers return (T)Convert.ChangeType(value, type); } - return JsonConvert.DeserializeObject((string)value); + return JsonConvert.DeserializeObject(value); } public string Serialize(T value) From 39459998b494e991722fe13406c666e33257d8df Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 01:36:50 +0530 Subject: [PATCH 048/299] Fixed issue where SmoothScrollIntoView doesn't await until the animation completes --- .../ListViewExtensions.SmoothScrollIntoView.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index 6fba693a3..a71126bee 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -203,7 +203,15 @@ namespace Microsoft.Toolkit.Uwp.UI { var tcs = new TaskCompletionSource(); - void ViewChanged(object _, ScrollViewerViewChangedEventArgs __) => tcs.TrySetResult(result: default); + void ViewChanged(object _, ScrollViewerViewChangedEventArgs e) + { + if (e.IsIntermediate) + { + return; + } + + tcs.TrySetResult(result: default); + } try { From 82c2ba31d3fcc803587e98707e0f32ab9355f96a Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 26 Jul 2021 15:09:18 -0700 Subject: [PATCH 049/299] PR updates --- .../ApplicationDataStorageHelper.cs | 16 +- .../ISettingsStorageHelperExtensions.cs | 71 ++++--- .../ObjectStorage/ISettingsStorageHelper.cs | 10 +- .../Helpers/JsonObjectSerializer.cs | 13 +- .../Helpers/JsonObjectSerializer2.cs | 48 +++++ .../Helpers/SystemTextJsonSerializer.cs | 9 +- .../Helpers/SystemTextJsonSerializer2.cs | 20 ++ .../Test_ApplicationDataStorageHelper.cs | 177 ++++++++++++++++++ .../Helpers/Test_StorageHelper.cs | 43 +++-- UnitTests/UnitTests.UWP/UnitTests.UWP.csproj | 5 +- 10 files changed, 326 insertions(+), 86 deletions(-) create mode 100644 UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer2.cs create mode 100644 UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer2.cs create mode 100644 UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 75c8d2d7e..723125460 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -99,14 +99,16 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public T Read(string key) + public bool TryRead(string key, out T value) { if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj != null) { - return Serializer.Deserialize(valueObj as string); + value = Serializer.Deserialize(valueObj as string); + return true; } - throw new KeyNotFoundException(key); + value = default; + return false; } /// @@ -116,13 +118,9 @@ namespace Microsoft.Toolkit.Uwp.Helpers } /// - public void Delete(string key) + public bool TryDelete(string key) { - var removed = Settings.Values.Remove(key); - if (!removed) - { - throw new KeyNotFoundException(key); - } + return Settings.Values.Remove(key); } /// diff --git a/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs b/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs index fa0664a1b..b8b11a7b9 100644 --- a/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs +++ b/Microsoft.Toolkit/Extensions/ISettingsStorageHelperExtensions.cs @@ -22,41 +22,18 @@ namespace Microsoft.Toolkit.Extensions /// The type of object value expected. /// The storage helper instance fo read from. /// The key of the target object. - /// An alternative value returned if the read fails. - /// The value of the target object, or the fallback value. - public static TValue? GetValueOrDefault(this ISettingsStorageHelper storageHelper, TKey key, TValue? fallback = default) - where TKey : notnull - { - try - { - return storageHelper.Read(key); - } - catch (KeyNotFoundException) - { - return fallback; - } - } - - /// - /// Attempts to perform read the provided key and returns a boolean indicator of success. - /// If the key is not found, the fallback value will be used instead. - /// - /// The type of key used to lookup the object. - /// The type of object value expected. - /// The storage helper instance to read from. - /// The key of the target object. /// The value of the target object, or the fallback value. - /// An alternate value returned if the read fails. + /// An alternative value returned if the read fails. /// A boolean indicator of success. - public static bool TryRead(this ISettingsStorageHelper storageHelper, TKey key, out TValue? value, TValue? fallback = default) + public static bool TryGetValueOrDefault(this ISettingsStorageHelper storageHelper, TKey key, out TValue? value, TValue? fallback = default) where TKey : notnull { - try + if (storageHelper.TryRead(key, out TValue? storedValue)) { - value = storageHelper.Read(key); + value = storedValue; return true; } - catch (KeyNotFoundException) + else { value = fallback; return false; @@ -64,24 +41,40 @@ namespace Microsoft.Toolkit.Extensions } /// - /// Attempts to remove an object by key and returns a boolean indicator of success. - /// If the key is not found, the method will return false. + /// Read the key in the storage helper instance and get the value. + /// + /// The type of key used to lookup the object. + /// The type of object value expected. + /// The storage helper instance fo read from. + /// The key of the target object. + /// The value of the target object + /// Throws when the key is not found in storage. + public static TValue? Read(this ISettingsStorageHelper storageHelper, TKey key) + where TKey : notnull + { + if (storageHelper.TryRead(key, out TValue? value)) + { + return value; + } + else + { + throw new KeyNotFoundException(); + } + } + + /// + /// Deletes a key from storage. /// /// The type of key used to lookup the object. /// The storage helper instance to delete from. /// The key of the target object. - /// A boolean indicator of success. - public static bool TryDelete(this ISettingsStorageHelper storageHelper, TKey key) + /// Throws when the key is not found in storage. + public static void Delete(this ISettingsStorageHelper storageHelper, TKey key) where TKey : notnull { - try + if (!storageHelper.TryDelete(key)) { - storageHelper.Delete(key); - return true; - } - catch (KeyNotFoundException) - { - return false; + throw new KeyNotFoundException(); } } } diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs index 90068d1ce..c8f9713b5 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs @@ -18,9 +18,9 @@ namespace Microsoft.Toolkit.Helpers /// /// Type of object retrieved. /// Key of the object. - /// Throws when the specified key is not found. - /// The object for key. - TValue? Read(TKey key); + /// The object for key. + /// A boolean indicator of success. + bool TryRead(TKey key, out TValue? value); /// /// Saves a single item by its key. @@ -34,8 +34,8 @@ namespace Microsoft.Toolkit.Helpers /// Deletes a single item by its key. /// /// Key of the object. - /// Throws when the specified key is not found. - void Delete(TKey key); + /// A boolean indicator of success. + bool TryDelete(TKey key); /// /// Clear all keys and values from the settings store. diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index c9b4aeaa3..e14a612cf 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -4,17 +4,18 @@ using System; using System.Reflection; -using Microsoft.Toolkit.Helpers; +using Microsoft.Toolkit.Uwp.Helpers; using Newtonsoft.Json; -namespace UnitTests.UWP.Helpers +namespace UnitTests.Helpers { /// /// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json. + /// Based on . /// internal class JsonObjectSerializer : IObjectSerializer { - public T Deserialize(string value) + public T Deserialize(object value) { var type = typeof(T); var typeInfo = type.GetTypeInfo(); @@ -26,10 +27,10 @@ namespace UnitTests.UWP.Helpers return (T)Convert.ChangeType(value, type); } - return JsonConvert.DeserializeObject(value); + return JsonConvert.DeserializeObject((string)value); } - public string Serialize(T value) + public object Serialize(T value) { var type = typeof(T); var typeInfo = type.GetTypeInfo(); @@ -38,7 +39,7 @@ namespace UnitTests.UWP.Helpers // This if/return combo is to maintain compatibility with 6.1.1 if (typeInfo.IsPrimitive || type == typeof(string)) { - return value.ToString(); + return value; } return JsonConvert.SerializeObject(value); diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer2.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer2.cs new file mode 100644 index 000000000..8721acc69 --- /dev/null +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer2.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using Microsoft.Toolkit.Helpers; +using Newtonsoft.Json; + +namespace UnitTests.Helpers +{ + /// + /// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json. + /// Based on . + /// + internal class JsonObjectSerializer2 : IObjectSerializer + { + public T Deserialize(string value) + { + var type = typeof(T); + var typeInfo = type.GetTypeInfo(); + + // Note: If you're creating a new app, you could just use the serializer directly. + // This if/return combo is to maintain compatibility with 6.1.1 + if (typeInfo.IsPrimitive || type == typeof(string)) + { + return (T)Convert.ChangeType(value, type); + } + + return JsonConvert.DeserializeObject(value); + } + + public string Serialize(T value) + { + var type = typeof(T); + var typeInfo = type.GetTypeInfo(); + + // Note: If you're creating a new app, you could just use the serializer directly. + // This if/return combo is to maintain compatibility with 6.1.1 + if (typeInfo.IsPrimitive || type == typeof(string)) + { + return value.ToString(); + } + + return JsonConvert.SerializeObject(value); + } + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs index 6204426a1..3598d2deb 100644 --- a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs @@ -4,17 +4,18 @@ using System; using System.Text.Json; -using Microsoft.Toolkit.Helpers; +using Microsoft.Toolkit.Uwp.Helpers; -namespace UnitTests.UWP.Helpers +namespace UnitTests.Helpers { /// /// Example class of writing a new that uses System.Text.Json. + /// Based on . /// internal class SystemTextJsonSerializer : IObjectSerializer { - public T Deserialize(string value) => JsonSerializer.Deserialize(value); + public T Deserialize(object value) => JsonSerializer.Deserialize(value as string); - public string Serialize(T value) => JsonSerializer.Serialize(value); + public object Serialize(T value) => JsonSerializer.Serialize(value); } } \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer2.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer2.cs new file mode 100644 index 000000000..caae64f28 --- /dev/null +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer2.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; +using Microsoft.Toolkit.Helpers; + +namespace UnitTests.Helpers +{ + /// + /// Example class of writing a new that uses System.Text.Json. + /// Based on . + /// + internal class SystemTextJsonSerializer2 : IObjectSerializer + { + public T Deserialize(string value) => JsonSerializer.Deserialize(value); + + public string Serialize(T value) => JsonSerializer.Serialize(value); + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs new file mode 100644 index 000000000..f6c5e184a --- /dev/null +++ b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs @@ -0,0 +1,177 @@ +using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnitTests.Helpers +{ + [TestClass] + public class Test_ApplicationDataStorageHelper + { + private readonly ApplicationDataStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(); + private readonly ApplicationDataStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer2()); + private readonly ApplicationDataStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer2()); + + /// + /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. + /// + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_CheckNewtonsoftVersion() + { + var version = typeof(Newtonsoft.Json.JsonSerializer).Assembly.GetName().Version; + Assert.AreEqual(10, version.Major); + Assert.AreEqual(0, version.Minor); + Assert.AreEqual(0, version.Revision); // Apparently the file revision was not updated for the updated package + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_LegacyIntTest() + { + string key = "LifeUniverseAndEverything"; + + int input = 42; + + // Use our previous Json layer to store value + _settingsStorage_JsonCompat.Save(key, input); + + // But try and read from our new system to see if it works + int output = _settingsStorage_System.Read(key, 0); + + Assert.AreEqual(input, output); + } + + /// + /// If we try and deserialize a complex type with the , we do a check ourselves and will throw our own exception. + /// + [TestCategory("Helpers")] + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void Test_StorageHelper_LegacyDateTestFailure() + { + string key = "ChristmasDay"; + + DateTime input = new DateTime(2017, 12, 25); + + _settingsStorage_JsonCompat.Save(key, input); + + // now read it as int to valid that the change works + _ = _settingsStorage_System.Read(key, DateTime.Today); + } + + /// + /// The doesn't support complex types, since it just passes through directly. + /// We'll get the argument exception from the API. + /// + [TestCategory("Helpers")] + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void Test_StorageHelper_DateTestFailure() + { + string key = "Today"; + + _settingsStorage_System.Save(key, DateTime.Today); + _settingsStorage_System.TryRead(key, out _); + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_LegacyInternalClassTest() + { + string key = "Contact"; + + UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; + + // simulate previous version by generating json and manually inserting it as string + _settingsStorage_JsonCompat.Save(key, input); + + // now read it as int to valid that the change works + UI.Person output = _settingsStorage_JsonCompat.Read(key, null); + + Assert.IsNotNull(output); + Assert.AreEqual(input.Name, output.Name); + Assert.AreEqual(input.Age, output.Age); + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_LegacyPublicClassTest() + { + string key = "Contact"; + + // Here's we're serializing a different class which has the same properties as our other class below. + UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; + + // simulate previous version by generating json and manually inserting it as string + _settingsStorage_JsonCompat.Save(key, input); + + // now read it as int to valid that the change works + Person output = _settingsStorage_JsonCompat.Read(key, null); + + Assert.IsNotNull(output); + Assert.AreEqual(input.Name, output.Name); + Assert.AreEqual(input.Age, output.Age); + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_IntTest() + { + string key = "NewLifeUniverseAndEverything"; + + int input = 42; + + _settingsStorage_System.Save(key, input); + + // now read it as int to valid that the change works + int output = _settingsStorage_System.Read(key, 0); + + Assert.AreEqual(input, output); + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_NewDateTest() + { + string key = "NewChristmasDay"; + + DateTime input = new DateTime(2017, 12, 25); + + _settingsStorage_JsonNew.Save(key, input); + + // now read it as int to valid that the change works + DateTime output = _settingsStorage_JsonNew.Read(key, DateTime.Today); + + Assert.AreEqual(input, output); + } + + [TestCategory("Helpers")] + [TestMethod] + public void Test_StorageHelper_NewPersonTest() + { + string key = "Contact"; + + Person input = new Person() { Name = "Joe Bloggs", Age = 42 }; + + _settingsStorage_JsonNew.Save(key, input); + + // now read it as int to valid that the change works + Person output = _settingsStorage_JsonNew.Read(key, null); + + Assert.IsNotNull(output); + Assert.AreEqual(input.Name, output.Name); + Assert.AreEqual(input.Age, output.Age); + } + + public class Person + { + public string Name { get; set; } + + public int Age { get; set; } + } + } +} diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index be0b192e6..a70abd9b5 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Uwp.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; -using UnitTests.UWP.Helpers; +using UnitTests.Helpers; using Windows.Storage; namespace UnitTests.Helpers @@ -15,9 +14,9 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { - private readonly ApplicationDataStorageHelper _settingsStorage_System = ApplicationDataStorageHelper.GetCurrent(); - private readonly ApplicationDataStorageHelper _settingsStorage_JsonCompat = ApplicationDataStorageHelper.GetCurrent(new JsonObjectSerializer()); - private readonly ApplicationDataStorageHelper _settingsStorage_JsonNew = ApplicationDataStorageHelper.GetCurrent(new SystemTextJsonSerializer()); + private readonly LocalObjectStorageHelper _localStorageHelperSystem = new LocalObjectStorageHelper(new SystemSerializer()); + private readonly LocalObjectStorageHelper _localStorageHelperJsonCompat = new LocalObjectStorageHelper(new JsonObjectSerializer()); + private readonly LocalObjectStorageHelper _localStorageHelperJsonNew = new LocalObjectStorageHelper(new SystemTextJsonSerializer()); /// /// Checks that we're running 10.0.3 version of Newtonsoft.Json package which we used in 6.1.1. @@ -41,16 +40,16 @@ namespace UnitTests.Helpers int input = 42; // Use our previous Json layer to store value - _settingsStorage_JsonCompat.Save(key, input); + _localStorageHelperJsonCompat.Save(key, input); // But try and read from our new system to see if it works - int output = _settingsStorage_System.Read(key, 0); + int output = _localStorageHelperSystem.Read(key, 0); Assert.AreEqual(input, output); } /// - /// If we try and deserialize a complex type with the , we do a check ourselves and will throw our own exception. + /// If we try and deserialize a complex type with the , we do a check ourselves and will throw our own exception. /// [TestCategory("Helpers")] [TestMethod] @@ -61,14 +60,14 @@ namespace UnitTests.Helpers DateTime input = new DateTime(2017, 12, 25); - _settingsStorage_JsonCompat.Save(key, input); + _localStorageHelperJsonCompat.Save(key, input); // now read it as int to valid that the change works - DateTime output = _settingsStorage_System.Read(key, DateTime.Today); + DateTime output = _localStorageHelperSystem.Read(key, DateTime.Today); } /// - /// The doesn't support complex types, since it just passes through directly. + /// The doesn't support complex types, since it just passes through directly. /// We'll get the argument exception from the API. /// [TestCategory("Helpers")] @@ -81,7 +80,7 @@ namespace UnitTests.Helpers // as local and online platforms seem to throw different exception types :( try { - _settingsStorage_System.Save("Today", DateTime.Today); + _localStorageHelperSystem.Save("Today", DateTime.Today); } catch (Exception exception) { @@ -100,10 +99,10 @@ namespace UnitTests.Helpers UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; // simulate previous version by generating json and manually inserting it as string - _settingsStorage_JsonCompat.Save(key, input); + _localStorageHelperJsonCompat.Save(key, input); // now read it as int to valid that the change works - UI.Person output = _settingsStorage_JsonCompat.Read(key, null); + UI.Person output = _localStorageHelperJsonCompat.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); @@ -120,10 +119,10 @@ namespace UnitTests.Helpers UI.Person input = new UI.Person() { Name = "Joe Bloggs", Age = 42 }; // simulate previous version by generating json and manually inserting it as string - _settingsStorage_JsonCompat.Save(key, input); + _localStorageHelperJsonCompat.Save(key, input); // now read it as int to valid that the change works - Person output = _settingsStorage_JsonCompat.Read(key, null); + Person output = _localStorageHelperJsonCompat.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); @@ -138,10 +137,10 @@ namespace UnitTests.Helpers int input = 42; - _settingsStorage_System.Save(key, input); + _localStorageHelperSystem.Save(key, input); // now read it as int to valid that the change works - int output = _settingsStorage_System.Read(key, 0); + int output = _localStorageHelperSystem.Read(key, 0); Assert.AreEqual(input, output); } @@ -154,10 +153,10 @@ namespace UnitTests.Helpers DateTime input = new DateTime(2017, 12, 25); - _settingsStorage_JsonNew.Save(key, input); + _localStorageHelperJsonNew.Save(key, input); // now read it as int to valid that the change works - DateTime output = _settingsStorage_JsonNew.Read(key, DateTime.Today); + DateTime output = _localStorageHelperJsonNew.Read(key, DateTime.Today); Assert.AreEqual(input, output); } @@ -170,10 +169,10 @@ namespace UnitTests.Helpers Person input = new Person() { Name = "Joe Bloggs", Age = 42 }; - _settingsStorage_JsonNew.Save(key, input); + _localStorageHelperJsonNew.Save(key, input); // now read it as int to valid that the change works - Person output = _settingsStorage_JsonNew.Read(key, null); + Person output = _localStorageHelperJsonNew.Read(key, null); Assert.IsNotNull(output); Assert.AreEqual(input.Name, output.Name); diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index 0323d553a..215c4582b 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -1,4 +1,4 @@ - + Debug @@ -175,10 +175,13 @@ + + + From dc218aa0bb7930f4583f5dcb11e1925a7d5ae2b6 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 26 Jul 2021 15:23:33 -0700 Subject: [PATCH 050/299] The ever important file header! --- .../Helpers/Test_ApplicationDataStorageHelper.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs index f6c5e184a..94d44b770 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using Microsoft.Toolkit.Uwp.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; From bd0b15bb50b1dbef9015024839f4ffa7ea90e2d6 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Mon, 26 Jul 2021 17:09:01 -0700 Subject: [PATCH 051/299] Introduce & utilize WinUI NavigationView --- .../ListDetailsView/ListDetailsView.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs index c7ba7a161..0b77d673b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -47,7 +47,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private ContentPresenter _detailsPresenter; private VisualStateGroup _selectionStateGroup; private Button _inlineBackButton; - private object _navigationView; + private Microsoft.UI.Xaml.Controls.NavigationView _navigationView; private Frame _frame; /// @@ -448,20 +448,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls return; } - var navType = _navigationView.GetType(); - var visibleProperty = navType.GetProperty("IsBackButtonVisible"); - if (visibleProperty != null) - { - _previousNavigationViewBackVisibilty = (int)visibleProperty.GetValue(_navigationView); - visibleProperty.SetValue(_navigationView, visible); - } + _previousNavigationViewBackVisibilty = (int)_navigationView.IsBackButtonVisible; + _navigationView.IsBackButtonVisible = (Microsoft.UI.Xaml.Controls.NavigationViewBackButtonVisible)visible; - var enabledProperty = navType.GetProperty("IsBackEnabled"); - if (enabledProperty != null) - { - _previousNavigationViewBackEnabled = (bool)enabledProperty.GetValue(_navigationView); - enabledProperty.SetValue(_navigationView, enabled); - } + _previousNavigationViewBackEnabled = _navigationView.IsBackEnabled; + _navigationView.IsBackEnabled = enabled; } private void SetDetailsContent() From f4207eaa02904598a61feac813f72cdb6baff9e4 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 26 Jul 2021 17:18:20 -0700 Subject: [PATCH 052/299] Added Obsolete attributes to old storage helper tests --- .../UnitTests.UWP/Helpers/JsonObjectSerializer.cs | 1 + .../UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs | 1 + UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index e14a612cf..27b775155 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -13,6 +13,7 @@ namespace UnitTests.Helpers /// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json. /// Based on . /// + [Obsolete] internal class JsonObjectSerializer : IObjectSerializer { public T Deserialize(object value) diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs index 3598d2deb..9bdb208f0 100644 --- a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs @@ -12,6 +12,7 @@ namespace UnitTests.Helpers /// Example class of writing a new that uses System.Text.Json. /// Based on . /// + [Obsolete] internal class SystemTextJsonSerializer : IObjectSerializer { public T Deserialize(object value) => JsonSerializer.Deserialize(value as string); diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs index a70abd9b5..c7d66e3dd 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_StorageHelper.cs @@ -14,8 +14,11 @@ namespace UnitTests.Helpers [TestClass] public class Test_StorageHelper { + [Obsolete] private readonly LocalObjectStorageHelper _localStorageHelperSystem = new LocalObjectStorageHelper(new SystemSerializer()); + [Obsolete] private readonly LocalObjectStorageHelper _localStorageHelperJsonCompat = new LocalObjectStorageHelper(new JsonObjectSerializer()); + [Obsolete] private readonly LocalObjectStorageHelper _localStorageHelperJsonNew = new LocalObjectStorageHelper(new SystemTextJsonSerializer()); /// @@ -33,6 +36,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_LegacyIntTest() { string key = "LifeUniverseAndEverything"; @@ -53,6 +57,7 @@ namespace UnitTests.Helpers /// [TestCategory("Helpers")] [TestMethod] + [Obsolete] [ExpectedException(typeof(NotSupportedException))] public void Test_StorageHelper_LegacyDateTestFailure() { @@ -72,6 +77,7 @@ namespace UnitTests.Helpers /// [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_DateTestFailure() { Exception expectedException = null; @@ -92,6 +98,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_LegacyInternalClassTest() { string key = "Contact"; @@ -111,6 +118,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_LegacyPublicClassTest() { string key = "Contact"; @@ -131,6 +139,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_IntTest() { string key = "NewLifeUniverseAndEverything"; @@ -147,6 +156,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_NewDateTest() { string key = "NewChristmasDay"; @@ -163,6 +173,7 @@ namespace UnitTests.Helpers [TestCategory("Helpers")] [TestMethod] + [Obsolete] public void Test_StorageHelper_NewPersonTest() { string key = "Contact"; From 525bc259134ad1da0598c90513fcb9240fa0e022 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 10:49:19 +0530 Subject: [PATCH 053/299] Fixed review comments. 1) Remove ConfigureAwait(true), 2) Remove VoidResult --- ...ListViewExtensions.SmoothScrollIntoView.cs | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index d92740b35..d00bf20c2 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -56,15 +56,15 @@ namespace Microsoft.Toolkit.Uwp.UI previousXOffset = scrollViewer.HorizontalOffset; previousYOffset = scrollViewer.VerticalOffset; - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); - void ViewChanged(object obj, ScrollViewerViewChangedEventArgs args) => tcs.TrySetResult(result: default); + void ViewChanged(object _, ScrollViewerViewChangedEventArgs __) => tcs.TrySetResult(result: default); try { scrollViewer.ViewChanged += ViewChanged; listViewBase.ScrollIntoView(listViewBase.Items[index], ScrollIntoViewAlignment.Leading); - await tcs.Task.ConfigureAwait(true); + await tcs.Task; } finally { @@ -80,7 +80,7 @@ namespace Microsoft.Toolkit.Uwp.UI // Scrolling back to previous position if (isVirtualizing) { - await scrollViewer.ChangeViewAsync(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true).ConfigureAwait(true); + await scrollViewer.ChangeViewAsync(previousXOffset, previousYOffset, zoomFactor: null, disableAnimation: true); } var listViewBaseWidth = listViewBase.ActualWidth; @@ -172,7 +172,7 @@ namespace Microsoft.Toolkit.Uwp.UI } } - await scrollViewer.ChangeViewAsync(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation).ConfigureAwait(true); + await scrollViewer.ChangeViewAsync(finalXPosition, finalYPosition, zoomFactor: null, disableAnimation); } /// @@ -188,7 +188,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// Returns that completes after scrolling public static async Task SmoothScrollIntoViewWithItemAsync(this ListViewBase listViewBase, object item, ScrollItemPlacement itemPlacement = ScrollItemPlacement.Default, bool disableAnimation = false, bool scrollIfVisibile = true, int additionalHorizontalOffset = 0, int additionalVerticalOffset = 0) { - await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset).ConfigureAwait(true); + await SmoothScrollIntoViewWithIndexAsync(listViewBase, listViewBase.Items.IndexOf(item), itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset); } /// @@ -201,7 +201,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); void ViewChanged(object _, ScrollViewerViewChangedEventArgs e) { @@ -217,20 +217,12 @@ namespace Microsoft.Toolkit.Uwp.UI { scrollViewer.ViewChanged += ViewChanged; scrollViewer.ChangeView(horizontalOffset, verticalOffset, zoomFactor, disableAnimation); - await tcs.Task.ConfigureAwait(true); + await tcs.Task; } finally { scrollViewer.ViewChanged -= ViewChanged; } } - - /// - /// Used as a placeholder TResult to indicate that a ]]> has a void TResult - /// - /// - private struct VoidResult - { - } } } From b1033fb2115229a1f6db465df916fcc7c2e7e4db Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 15:11:11 +0530 Subject: [PATCH 054/299] Added Scroll indicator --- .../ListViewExtensionsPage.xaml | 21 +++++++++++++------ .../ListViewExtensionsPage.xaml.cs | 18 ++++++++++++++++ ...ListViewExtensions.SmoothScrollIntoView.cs | 6 ++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml index 625285f44..a39bc773a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml @@ -35,14 +35,16 @@ - + + Text="100" + Margin="0,10,0,0" /> + SelectedIndex="0" + Margin="0,10,0,0" > Default Left Top @@ -52,18 +54,25 @@ + IsChecked="False" + Margin="0,10,0,0" /> + Text="0" + Margin="0,10,0,0" /> + Text="0" + Margin="0,10,0,0" /> + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs index cb23e89d7..134be3719 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs @@ -6,9 +6,11 @@ using System; using System.Collections.ObjectModel; using System.Windows.Input; using Microsoft.Toolkit.Uwp.UI; +using Windows.UI; using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { @@ -55,7 +57,9 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages var scrollIfVisibile = ScrollIfVisibileInput.IsChecked ?? true; var additionalHorizontalOffset = int.TryParse(AdditionalHorizontalOffsetInput.Text, out var ho) ? ho : 0; var additionalVerticalOffset = int.TryParse(AdditionalVerticalOffsetInput.Text, out var vo) ? vo : 0; + UpdateScrollIndicator(true); await sampleListView.SmoothScrollIntoViewWithIndexAsync(index, itemPlacement, disableAnimation, scrollIfVisibile, additionalHorizontalOffset, additionalVerticalOffset); + UpdateScrollIndicator(false); }); if (sampleListView != null) @@ -64,6 +68,20 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages } } + private void UpdateScrollIndicator(bool isScrolling) + { + if (isScrolling) + { + ScrollIndicatorTest.Text = "Scroll started"; + ScrollIndicator.Fill = new SolidColorBrush(Colors.Green); + } + else + { + ScrollIndicator.Fill = new SolidColorBrush(Colors.Red); + ScrollIndicatorTest.Text = "Scroll completed"; + } + } + private ObservableCollection GetOddEvenSource(int count) { var oddEvenSource = new ObservableCollection(); diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index d00bf20c2..f51cc87e2 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -201,6 +201,12 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { + // MUST check this an return immediately, otherwise this async task will never completes because ViewChanged event won't triggered + if (horizontalOffset == scrollViewer.HorizontalOffset && verticalOffset == scrollViewer.VerticalOffset) + { + return; + } + var tcs = new TaskCompletionSource(); void ViewChanged(object _, ScrollViewerViewChangedEventArgs e) From 8a5774e6a556ba0e04222d9ec225dd340224fca3 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 15:15:40 +0530 Subject: [PATCH 055/299] Fixed typos --- .../ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index f51cc87e2..74b8b699b 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -201,7 +201,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { - // MUST check this an return immediately, otherwise this async task will never completes because ViewChanged event won't triggered + // MUST check this and return immediately, otherwise this async task will never completes because ViewChanged event won't triggered if (horizontalOffset == scrollViewer.HorizontalOffset && verticalOffset == scrollViewer.VerticalOffset) { return; From 4ee302dc411c9d8a4d39a78478c0920c0ea5ca60 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 15:16:10 +0530 Subject: [PATCH 056/299] Fixed typo --- .../ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index 74b8b699b..4833e5440 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -201,7 +201,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { - // MUST check this and return immediately, otherwise this async task will never completes because ViewChanged event won't triggered + // MUST check this and return immediately, otherwise this async task will never complete because ViewChanged event won't get triggered if (horizontalOffset == scrollViewer.HorizontalOffset && verticalOffset == scrollViewer.VerticalOffset) { return; From 625716b23f7ad915ad93dd1fde4909b40b82b148 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 20:29:25 +0530 Subject: [PATCH 057/299] Scroll indicators test change in code behind Co-authored-by: Rosario Pulella --- .../ListViewExtensions/ListViewExtensionsPage.xaml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs index 134be3719..cd547d2bd 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs @@ -72,13 +72,13 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { if (isScrolling) { - ScrollIndicatorTest.Text = "Scroll started"; + ScrollIndicatorTest.Text = "Scrolling"; ScrollIndicator.Fill = new SolidColorBrush(Colors.Green); } else { ScrollIndicator.Fill = new SolidColorBrush(Colors.Red); - ScrollIndicatorTest.Text = "Scroll completed"; + ScrollIndicatorTest.Text = "Not Scolling"; } } @@ -121,4 +121,4 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages await new MessageDialog($"You clicked {item} via the 'ListViewExtensions.Command' binding", "Item Clicked").ShowAsync(); } } -} \ No newline at end of file +} From a09e203b5cd480b32a162f803d92c54fa00ab92d Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 20:29:38 +0530 Subject: [PATCH 058/299] Scroll indicators test change in xaml Co-authored-by: Rosario Pulella --- .../ListViewExtensions/ListViewExtensionsPage.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml index a39bc773a..bb50bbcf3 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml @@ -71,8 +71,8 @@ Margin="0,10,0,0" /> - + - \ No newline at end of file + From 4b70980f2d12e2e9a58a5ade8857f8114f76ff87 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 22:36:40 +0530 Subject: [PATCH 059/299] Fixed Task not completing issue --- .../ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index 4833e5440..e0edbaed0 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -201,6 +201,9 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { + horizontalOffset = horizontalOffset > scrollViewer.ScrollableWidth ? scrollViewer.ScrollableWidth : horizontalOffset; + verticalOffset = verticalOffset > scrollViewer.ScrollableHeight ? scrollViewer.ScrollableHeight : verticalOffset; + // MUST check this and return immediately, otherwise this async task will never complete because ViewChanged event won't get triggered if (horizontalOffset == scrollViewer.HorizontalOffset && verticalOffset == scrollViewer.VerticalOffset) { From 331f7ddc3d617b6c3b960167c779a125df7a187e Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 27 Jul 2021 10:18:31 -0700 Subject: [PATCH 060/299] Comment cref updates in test app --- UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs | 2 +- UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs | 2 +- .../UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs index 27b775155..0cfcb64d0 100644 --- a/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/JsonObjectSerializer.cs @@ -11,7 +11,7 @@ namespace UnitTests.Helpers { /// /// This is a Serializer which should mimic the previous functionality of 6.1.1 release of the Toolkit with Newtonsoft.Json. - /// Based on . + /// Based on . /// [Obsolete] internal class JsonObjectSerializer : IObjectSerializer diff --git a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs index 9bdb208f0..b4b47f73f 100644 --- a/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs +++ b/UnitTests/UnitTests.UWP/Helpers/SystemTextJsonSerializer.cs @@ -10,7 +10,7 @@ namespace UnitTests.Helpers { /// /// Example class of writing a new that uses System.Text.Json. - /// Based on . + /// Based on . /// [Obsolete] internal class SystemTextJsonSerializer : IObjectSerializer diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs index 94d44b770..3181736f8 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs @@ -69,7 +69,7 @@ namespace UnitTests.Helpers /// /// The doesn't support complex types, since it just passes through directly. - /// We'll get the argument exception from the API. + /// We'll get the argument exception from the API. /// [TestCategory("Helpers")] [TestMethod] From 70d7b0e498f5d261fc7b5d3340d8537e8d5e4027 Mon Sep 17 00:00:00 2001 From: Vijay Nirmal Date: Tue, 27 Jul 2021 23:46:14 +0530 Subject: [PATCH 061/299] Fixed Task not completing issue when offset is negative --- ...ListViewExtensions.SmoothScrollIntoView.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs index e0edbaed0..c32bf5c4d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.SmoothScrollIntoView.cs @@ -201,8 +201,23 @@ namespace Microsoft.Toolkit.Uwp.UI /// if set to true disable animation. private static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, float? zoomFactor, bool disableAnimation) { - horizontalOffset = horizontalOffset > scrollViewer.ScrollableWidth ? scrollViewer.ScrollableWidth : horizontalOffset; - verticalOffset = verticalOffset > scrollViewer.ScrollableHeight ? scrollViewer.ScrollableHeight : verticalOffset; + if (horizontalOffset > scrollViewer.ScrollableWidth) + { + horizontalOffset = scrollViewer.ScrollableWidth; + } + else if (horizontalOffset < 0) + { + horizontalOffset = 0; + } + + if (verticalOffset > scrollViewer.ScrollableHeight) + { + verticalOffset = scrollViewer.ScrollableHeight; + } + else if (verticalOffset < 0) + { + verticalOffset = 0; + } // MUST check this and return immediately, otherwise this async task will never complete because ViewChanged event won't get triggered if (horizontalOffset == scrollViewer.HorizontalOffset && verticalOffset == scrollViewer.VerticalOffset) From b9e792301a9a30d8c1a3d632961c73927b267176 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Tue, 27 Jul 2021 11:24:38 -0700 Subject: [PATCH 062/299] Strongly type Nav View to enum --- .../ListDetailsView/ListDetailsView.bind | 17 +++++++++-------- .../ListDetailsView/ListDetailsView.cs | 19 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListDetailsView/ListDetailsView.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListDetailsView/ListDetailsView.bind index af80e92f2..b0a200550 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListDetailsView/ListDetailsView.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListDetailsView/ListDetailsView.bind @@ -74,19 +74,20 @@ + - - - - - - - - + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs index 0b77d673b..7047aa4a9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -10,6 +10,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Navigation; +using NavigationView = Microsoft.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { @@ -42,12 +43,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private AppViewBackButtonVisibility? _previousSystemBackButtonVisibility; private bool _previousNavigationViewBackEnabled; - // Int used because the underlying type is an enum, but we don't have access to the enum - private int _previousNavigationViewBackVisibilty; + private NavigationView.NavigationViewBackButtonVisible _previousNavigationViewBackVisibilty; + private NavigationView.NavigationView _navigationView; + private ContentPresenter _detailsPresenter; private VisualStateGroup _selectionStateGroup; private Button _inlineBackButton; - private Microsoft.UI.Xaml.Controls.NavigationView _navigationView; private Frame _frame; /// @@ -199,7 +200,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls _frame.Navigating -= OnFrameNavigating; } - _navigationView = this.FindAscendant(); + _navigationView = this.FindAscendant(); _frame = this.FindAscendant(); if (_frame != null) { @@ -328,8 +329,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) { - const int backButtonVisible = 1; - if (DesignMode.DesignModeEnabled) { return; @@ -358,7 +357,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls } else { - SetNavigationViewBackButtonState(backButtonVisible, true); + SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible.Visible, true); } } else if (BackButtonBehavior != BackButtonBehavior.Manual) @@ -441,15 +440,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls VisualStateManager.GoToState(this, SelectedItem == null ? noSelectionState : hasSelectionState, animate); } - private void SetNavigationViewBackButtonState(int visible, bool enabled) + private void SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible visibility, bool enabled) { if (_navigationView == null) { return; } - _previousNavigationViewBackVisibilty = (int)_navigationView.IsBackButtonVisible; - _navigationView.IsBackButtonVisible = (Microsoft.UI.Xaml.Controls.NavigationViewBackButtonVisible)visible; + _previousNavigationViewBackVisibilty = _navigationView.IsBackButtonVisible; + _navigationView.IsBackButtonVisible = visibility; _previousNavigationViewBackEnabled = _navigationView.IsBackEnabled; _navigationView.IsBackEnabled = enabled; From 7bcf51f2140c33ae9527b902f734422e8d80613b Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:12:34 -0700 Subject: [PATCH 063/299] Apply XAML Styler --- .../TabbedCommandBar/TabbedCommandBar.xaml | 217 ++++++++---------- .../TabbedCommandBarItem.xaml | 126 +++++----- settings.xamlstyler | 2 +- 3 files changed, 166 insertions(+), 179 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml index b7f468427..dc171dfcc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml @@ -1,8 +1,8 @@ + xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> @@ -24,8 +24,8 @@ - - + + @@ -53,105 +53,99 @@ - + - + - - + + - - + + + Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPaneVisibility}" /> - + BorderBrush="{ThemeResource NavigationViewItemSeparatorForeground}" + Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.TopPaneVisibility}"> - - - - - - - - - + + + + + + + + + - + - + - + - + - - + + - + - + + - - - - - - - - - - + + - - - - - - + - - + + + - + - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + AutomationProperties.LandmarkType="Navigation" + AutomationProperties.Name="{TemplateBinding AutomationProperties.Name}"> + + - - - + + + + + + + + + + + + + + + + - + + Date: Wed, 28 Jul 2021 13:55:35 -0700 Subject: [PATCH 073/299] Fix VisualState that was crashing, had a reference to unused element --- .../TabbedCommandBar/TabbedCommandBar.xaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml index 27017a1e2..f3c2e59ee 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml @@ -30,6 +30,14 @@ + + + + + + + + From 609442bc6bc121ae15e7d70e60726aca3ab755c9 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:55:51 -0700 Subject: [PATCH 074/299] Update CommandBar to use WinUI 2.6 style over reveal, looks nice! --- .../TabbedCommandBar/TabbedCommandBarItem.xaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml index 484d45d09..62684b8fd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml @@ -27,7 +27,7 @@ TargetType="controls:TabbedCommandBarItem" /> From a66b6d0ece2503b2cd03ca379322f0da48796cc9 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:59:15 -0700 Subject: [PATCH 075/299] Remove old TODO --- .../TabbedCommandBar/TabbedCommandBarItem.xaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml index 62684b8fd..6a43a028c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml @@ -29,7 +29,6 @@ + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs new file mode 100644 index 000000000..6ba680e6a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Toolkit.Uwp.Deferred; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public class SuggestionChosenEventArgs : DeferredEventArgs + { + public string Query { get; internal set; } + + public string Prefix { get; internal set; } + + public string Text { get; set; } + + public object SelectedItem { get; internal set; } + + public Guid Id { get; internal set; } + + public SuggestionTokenFormat Format { get; internal set; } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs new file mode 100644 index 000000000..9893cde2a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel; +using Windows.UI.Text; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public class SuggestionInfo : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public RichSuggestBox Owner { get; } + + public Guid Id { get; } + + public string DisplayText { get; } + + public object Item { get; set; } + + public int RangeStart { get; private set; } + + public int RangeEnd { get; private set; } + + public int Position => _range?.GetIndex(TextRangeUnit.Character) - 1 ?? 0; + + internal bool Active { get; set; } + + private ITextRange _range; + + public SuggestionInfo(Guid id, string displayText, RichSuggestBox owner) + { + Id = id; + DisplayText = displayText; + Owner = owner; + } + + internal void UpdateTextRange(ITextRange range) + { + if (_range == null) + { + _range = range; + RangeStart = _range.StartPosition; + RangeEnd = _range.EndPosition; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty)); + } + else if (RangeStart != range.StartPosition || RangeEnd != range.EndPosition) + { + _range.SetRange(range.StartPosition, range.EndPosition); + RangeStart = _range.StartPosition; + RangeEnd = _range.EndPosition; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty)); + } + } + + public override string ToString() + { + return $"HYPERLINK \"{Id}\"{DisplayText}"; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs new file mode 100644 index 000000000..61d7ad167 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs @@ -0,0 +1,18 @@ +using Windows.UI; +using Windows.UI.Text; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public class SuggestionTokenFormat + { + public Color Foreground { get; set; } + + public Color Background { get; set; } + + public FormatEffect Italic { get; set; } + + public FormatEffect Bold { get; set; } + + public UnderlineType Underline { get; set; } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs new file mode 100644 index 000000000..def23b9ec --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs @@ -0,0 +1,11 @@ +using Microsoft.Toolkit.Uwp.Deferred; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public class SuggestionsRequestedEventArgs : DeferredCancelEventArgs + { + public string Prefix { get; set; } + + public string Query { get; set; } + } +} From 4ee560666cb49cc186cd21c5149d2e5d9c89c0ba Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Thu, 14 Jan 2021 23:06:35 -0800 Subject: [PATCH 169/299] update headers --- .../RichSuggestBox/PlainTextCommandBarFlyout.cs | 6 +++++- .../RichSuggestBox/RichSuggestBox.Events.cs | 6 +++++- .../RichSuggestBox/RichSuggestBox.Properties.cs | 6 +++++- .../RichSuggestBox/RichSuggestBox.cs | 6 +++++- .../RichSuggestBox/SuggestionChosenEventArgs.cs | 6 +++++- .../RichSuggestBox/SuggestionInfo.cs | 6 +++++- .../RichSuggestBox/SuggestionTokenFormat.cs | 6 +++++- .../RichSuggestBox/SuggestionsRequestedEventArgs.cs | 6 +++++- 8 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs index 7adbc988c..342514f52 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs @@ -1,4 +1,8 @@ -using Windows.UI.Xaml.Controls; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs index 73f6229aa..8b735c393 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs @@ -1,4 +1,8 @@ -using Windows.Foundation; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs index b5083430d..882ac0138 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs @@ -1,4 +1,8 @@ -using System.Collections.ObjectModel; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.ObjectModel; using Windows.UI.Text; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs index bd00ece61..dd87d6883 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs index 6ba680e6a..1b10a12a9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using Microsoft.Toolkit.Uwp.Deferred; namespace Microsoft.Toolkit.Uwp.UI.Controls diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs index 9893cde2a..f0332dc71 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.ComponentModel; using Windows.UI.Text; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs index 61d7ad167..ebb1b48f9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs @@ -1,4 +1,8 @@ -using Windows.UI; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI; using Windows.UI.Text; namespace Microsoft.Toolkit.Uwp.UI.Controls diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs index def23b9ec..c9f7a71d4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs @@ -1,4 +1,8 @@ -using Microsoft.Toolkit.Uwp.Deferred; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Toolkit.Uwp.Deferred; namespace Microsoft.Toolkit.Uwp.UI.Controls { From 583d5ae2e44932636bbbd92176b9413e5b0c2c60 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Tue, 19 Jan 2021 22:02:13 -0800 Subject: [PATCH 170/299] add comments --- .../RichSuggestBox/RichSuggestBox.Events.cs | 13 ++++ .../RichSuggestBox.Properties.cs | 63 +++++++++++++++++++ .../RichSuggestBox/RichSuggestBox.cs | 8 +++ .../SuggestionChosenEventArgs.cs | 21 +++++++ .../RichSuggestBox/SuggestionInfo.cs | 32 ++++++++++ .../RichSuggestBox/SuggestionTokenFormat.cs | 18 ++++++ .../SuggestionsRequestedEventArgs.cs | 9 +++ 7 files changed, 164 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs index 8b735c393..3351c3d81 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs @@ -8,12 +8,25 @@ using Windows.UI.Xaml.Controls; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// The RichSuggestBox control extends control that suggests and embeds custom data in a rich document. + /// public partial class RichSuggestBox { + /// + /// Event raised when the control needs to show suggestions. + /// public event TypedEventHandler SuggestionsRequested; + /// + /// Event raised when user click on a suggestion. + /// This event lets you customize the token appearance in the document. + /// public event TypedEventHandler SuggestionChosen; + /// + /// Event raised when text is changed, either by user or by internal formatting. + /// public event TypedEventHandler TextChanged; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs index 882ac0138..59b499e9e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs @@ -10,8 +10,14 @@ using Windows.UI.Xaml.Media; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// The RichSuggestBox control extends control that suggests and embeds custom data in a rich document. + /// public partial class RichSuggestBox { + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register( nameof(PlaceholderText), @@ -19,6 +25,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(string.Empty)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty RichEditBoxStyleProperty = DependencyProperty.Register( nameof(RichEditBoxStyle), @@ -26,6 +35,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( nameof(Header), @@ -33,6 +45,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( nameof(Description), @@ -40,6 +55,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register( nameof(TextWrapping), @@ -47,6 +65,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(TextWrapping.NoWrap)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty ClipboardCopyFormatProperty = DependencyProperty.Register( nameof(ClipboardCopyFormat), @@ -54,6 +75,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(RichEditClipboardFormat.PlainText)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty SuggestionBackgroundProperty = DependencyProperty.Register( nameof(SuggestionBackground), @@ -61,6 +85,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty SuggestionForegroundProperty = DependencyProperty.Register( nameof(SuggestionForeground), @@ -68,6 +95,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty PrefixesProperty = DependencyProperty.Register( nameof(Prefixes), @@ -75,54 +105,81 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls typeof(RichSuggestBox), new PropertyMetadata("@", OnPrefixesChanged)); + /// + /// Gets or sets the text that is displayed in the control until the value is changed by a user action or some other operation. + /// public string PlaceholderText { get => (string)GetValue(PlaceholderTextProperty); set => SetValue(PlaceholderTextProperty, value); } + /// + /// Gets or sets the style of the underlying . + /// public Style RichEditBoxStyle { get => (Style)GetValue(RichEditBoxStyleProperty); set => SetValue(RichEditBoxStyleProperty, value); } + /// + /// Gets or sets the content for the control's header. + /// public object Header { get => GetValue(HeaderProperty); set => SetValue(HeaderProperty, value); } + /// + /// Gets or sets content that is shown below the control. The content should provide guidance about the input expected by the control. + /// public object Description { get => GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); } + /// + /// Gets or sets a value that indicates how text wrapping occurs if a line of text extends beyond the available width of the control. + /// public TextWrapping TextWrapping { get => (TextWrapping)GetValue(TextWrappingProperty); set => SetValue(TextWrappingProperty, value); } + /// + /// Gets or sets a value that specifies whether text is copied with all formats, or as plain text only. + /// public RichEditClipboardFormat ClipboardCopyFormat { get => (RichEditClipboardFormat)GetValue(ClipboardCopyFormatProperty); set => SetValue(ClipboardCopyFormatProperty, value); } + /// + /// Gets or sets the default brush used to color the suggestion token background. + /// public SolidColorBrush SuggestionBackground { get => (SolidColorBrush)GetValue(SuggestionBackgroundProperty); set => SetValue(SuggestionBackgroundProperty, value); } + /// + /// Gets or sets the default brush used to color the suggestion token foreground. + /// public SolidColorBrush SuggestionForeground { get => (SolidColorBrush)GetValue(SuggestionForegroundProperty); set => SetValue(SuggestionForegroundProperty, value); } + /// + /// Gets or sets prefix characters to start a query. + /// public string Prefixes { get => (string)GetValue(PrefixesProperty); @@ -134,8 +191,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// protected object LockObj { get; } + /// + /// Gets an object that enables access to the text object model for the text contained in a . + /// public RichEditTextDocument TextDocument => _richEditBox?.TextDocument; + /// + /// Gets a collection of suggestion tokens that are present in the document. + /// public ReadOnlyObservableCollection Tokens { get; } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs index dd87d6883..3d311912f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -19,6 +19,9 @@ using Windows.UI.Xaml.Input; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// The RichSuggestBox control extends control that suggests and embeds custom data in a rich document. + /// [TemplatePart(Name = PartRichEditBox, Type = typeof(RichEditBox))] [TemplatePart(Name = PartSuggestionsPopup, Type = typeof(Popup))] [TemplatePart(Name = PartSuggestionsList, Type = typeof(ListViewBase))] @@ -56,6 +59,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls RegisterPropertyChangedCallback(ItemsSourceProperty, ItemsSource_PropertyChanged); } + /// + /// Clear unused tokens and undo/redo history. saves all of previously committed tokens + /// even when they are removed from the text. They have to be manually removed using this method. + /// public void ClearUndoRedoSuggestionHistory() { TextDocument.ClearUndoRedoHistory(); @@ -71,6 +78,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls } } + /// protected override void OnApplyTemplate() { base.OnApplyTemplate(); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs index 1b10a12a9..3ab5f5a80 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs @@ -7,18 +7,39 @@ using Microsoft.Toolkit.Uwp.Deferred; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// Provides data for the event. + /// public class SuggestionChosenEventArgs : DeferredEventArgs { + /// + /// Gets the query used for this token. + /// public string Query { get; internal set; } + /// + /// Gets the prefix character used for this token. + /// public string Prefix { get; internal set; } + /// + /// Gets or sets the display text. + /// public string Text { get; set; } + /// + /// Gets the suggestion item associated with this token. + /// public object SelectedItem { get; internal set; } + /// + /// Gets token ID. + /// public Guid Id { get; internal set; } + /// + /// Gets the formatting construct to override this token formatting. + /// public SuggestionTokenFormat Format { get; internal set; } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs index f0332dc71..823c8def1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs @@ -8,28 +8,59 @@ using Windows.UI.Text; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// SuggestionInfo describes a suggestion token in the document. + /// public class SuggestionInfo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// Gets the instance that owns this token. + /// public RichSuggestBox Owner { get; } + /// + /// Gets the token ID. + /// public Guid Id { get; } + /// + /// Gets the text displayed in the document. + /// public string DisplayText { get; } + /// + /// Gets or sets the suggested item associated with this token. + /// public object Item { get; set; } + /// + /// Gets the start position of the text range. + /// public int RangeStart { get; private set; } + /// + /// Gets the end position of the text range. + /// public int RangeEnd { get; private set; } + /// + /// Gets the start position of the token in number of characters. + /// public int Position => _range?.GetIndex(TextRangeUnit.Character) - 1 ?? 0; internal bool Active { get; set; } private ITextRange _range; + /// + /// Initializes a new instance of the class. + /// + /// Token ID + /// Text in the document + /// instance that owns this token public SuggestionInfo(Guid id, string displayText, RichSuggestBox owner) { Id = id; @@ -55,6 +86,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls } } + /// public override string ToString() { return $"HYPERLINK \"{Id}\"{DisplayText}"; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs index ebb1b48f9..588076f56 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs @@ -7,16 +7,34 @@ using Windows.UI.Text; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// SuggestionTokenFormat describes how a token should be formatted. + /// public class SuggestionTokenFormat { + /// + /// Gets or sets token foreground color. + /// public Color Foreground { get; set; } + /// + /// Gets or sets token background color. + /// public Color Background { get; set; } + /// + /// Gets or sets token italic style. + /// public FormatEffect Italic { get; set; } + /// + /// Gets or sets token bold style. + /// public FormatEffect Bold { get; set; } + /// + /// Gets or sets token underline style. + /// public UnderlineType Underline { get; set; } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs index c9f7a71d4..60e358b2f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs @@ -6,10 +6,19 @@ using Microsoft.Toolkit.Uwp.Deferred; namespace Microsoft.Toolkit.Uwp.UI.Controls { + /// + /// Provide data for event. + /// public class SuggestionsRequestedEventArgs : DeferredCancelEventArgs { + /// + /// Gets or sets the prefix character used for the query. + /// public string Prefix { get; set; } + /// + /// Gets or sets the query for suggestions. + /// public string Query { get; set; } } } From 60169ae95883f724413c6a5f1ed5a380437c6844 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Tue, 19 Jan 2021 22:37:03 -0800 Subject: [PATCH 171/299] fix indentation issues --- .../RichSuggestBox/RichSuggestBox.cs | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs index 3d311912f..ca8b9738e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -199,37 +199,31 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { case VirtualKey.Up when itemsList.Count == 1: case VirtualKey.Down when itemsList.Count == 1: - { - _suggestionsList.SelectedItem = itemsList[0]; - break; - } + _suggestionsList.SelectedItem = itemsList[0]; + break; + case VirtualKey.Up: - { - _suggestionChoice = _suggestionChoice <= 0 ? itemsList.Count : _suggestionChoice - 1; - _suggestionsList.SelectedItem = _suggestionChoice == 0 ? null : itemsList[_suggestionChoice - 1]; - args.Handled = true; - break; - } + _suggestionChoice = _suggestionChoice <= 0 ? itemsList.Count : _suggestionChoice - 1; + _suggestionsList.SelectedItem = _suggestionChoice == 0 ? null : itemsList[_suggestionChoice - 1]; + args.Handled = true; + break; + case VirtualKey.Down: - { - _suggestionChoice = _suggestionChoice >= itemsList.Count ? 0 : _suggestionChoice + 1; - _suggestionsList.SelectedItem = _suggestionChoice == 0 ? null : itemsList[_suggestionChoice - 1]; - args.Handled = true; - break; - } + _suggestionChoice = _suggestionChoice >= itemsList.Count ? 0 : _suggestionChoice + 1; + _suggestionsList.SelectedItem = _suggestionChoice == 0 ? null : itemsList[_suggestionChoice - 1]; + args.Handled = true; + break; + case VirtualKey.Enter when _suggestionsList.SelectedItem != null: - { - ShowSuggestionsPopup(false); - _ = OnSuggestionSelectedAsync(_suggestionsList.SelectedItem); - args.Handled = true; - break; - } + ShowSuggestionsPopup(false); + _ = OnSuggestionSelectedAsync(_suggestionsList.SelectedItem); + args.Handled = true; + break; + case VirtualKey.Escape: - { - ShowSuggestionsPopup(false); - args.Handled = true; - break; - } + ShowSuggestionsPopup(false); + args.Handled = true; + break; } } From 74c32190f31e8c404081baa10106a8df32568779 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Tue, 19 Jan 2021 23:30:24 -0800 Subject: [PATCH 172/299] more fixes --- .../RichSuggestBox/RichSuggestBox.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs index ca8b9738e..55d8a2850 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -31,6 +31,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private const string PartSuggestionsPopup = "SuggestionsPopup"; private const string PartSuggestionsList = "SuggestionsList"; + private readonly Dictionary _tokens; + private readonly ObservableCollection _visibleTokens; + private Popup _suggestionPopup; private RichEditBox _richEditBox; private ListViewBase _suggestionsList; @@ -41,8 +44,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private bool _ignoreChange; private ITextRange _currentRange; private CancellationTokenSource _suggestionRequestedTokenSource; - private readonly Dictionary _tokens; - private readonly ObservableCollection _visibleTokens; /// /// Initializes a new instance of the class. @@ -416,12 +417,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { range.Link = string.Empty; } + return false; } if (token.ToString() != range.Text) { - //range.Link = string.Empty; + range.Link = string.Empty; range.CharacterFormat = TextDocument.GetDefaultCharacterFormat(); token.Active = false; return false; From 08122c3bed92d664402b125188ac718179a411b8 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Thu, 21 Jan 2021 03:19:05 -0800 Subject: [PATCH 173/299] xaml formatting --- .../RichSuggestBox/RichSuggestBox.xaml | 108 ++++++++++-------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml index 5f3cb590d..44065225f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml @@ -1,34 +1,40 @@ - + - - + + - - + + - - + + - - - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelPage.xaml.cs deleted file mode 100644 index 0c6c06a95..000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelPage.xaml.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Windows.UI.Xaml.Controls; - -namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages -{ - /// - /// A page that shows how to use the DropShadowPanel control. - /// - public sealed partial class DropShadowPanelPage : Page - { - /// - /// Initializes a new instance of the class. - /// - public DropShadowPanelPage() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanel.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/DropShadowPanel.png similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanel.png rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/DropShadowPanel.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/DropShadowPanelXaml.bind similarity index 96% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelXaml.bind rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/DropShadowPanelXaml.bind index 175499746..678a9eaf1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/DropShadowPanelXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/DropShadowPanelXaml.bind @@ -88,7 +88,7 @@ VerticalAlignment="Center" HorizontalAlignment="Center" IsMasked="@[Is Masked]"> - + - + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/Trex.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/Trex.png similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/Trex.png rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/Trex.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/Unicorn.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/Unicorn.png similarity index 100% rename from Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DropShadowPanel/Unicorn.png rename to Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/Unicorn.png diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 780f14479..5eddad6e0 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -49,6 +49,7 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 2447ae36d..4cbc804ec 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -185,13 +185,11 @@ }, { "Name": "DropShadowPanel", - "Type": "DropShadowPanelPage", "Subcategory": "Media", "About": "DropShadowPanel contol allows the creation of a DropShadow for any Xaml FrameworkElement in markup.", "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel", - "XamlCodeFile": "DropShadowPanelXaml.bind", - "Icon": "/SamplePages/DropShadowPanel/DropShadowPanel.png", - "BadgeUpdateVersionRequired": "Anniversary Update required", + "XamlCodeFile": "/SamplePages/Shadows/DropShadowPanelXaml.bind", + "Icon": "/SamplePages/Shadows/DropShadowPanel.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md" }, { From 863e75c3cc963c0a013d190d28f608afe831f85f Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 11 Aug 2021 15:27:45 -0700 Subject: [PATCH 276/299] Clean-up out-dated min version warnings on samples as the Toolkit min itself is 1809+ now. Add Deprecation message we missed to RadialProgressBar control. --- .../SamplePages/samples.json | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 4cbc804ec..426164933 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -126,6 +126,8 @@ { "Name": "RadialProgressBar", "Type": "RadialProgressBarPage", + "BadgeUpdateVersionRequired": "DEPRECATED", + "DeprecatedWarning": "Please migrate to the ProgressRing control from WinUI with IsIndeterminate set to false, this control will be removed in a future release. https://aka.ms/winui", "Subcategory": "Status and Info", "About": "The radial progress bar displays progress as a circle getting filled.", "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Controls.Core/RadialProgressBar", @@ -357,7 +359,6 @@ "XamlCodeFile": "InfiniteCanvas.bind", "Icon": "/SamplePages/InfiniteCanvas/InfiniteCanvas.png", "ApiCheck": "Windows.UI.Xaml.Controls.ColorPicker", - "BadgeUpdateVersionRequired": "Fall Creators Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/InfiniteCanvas.md" }, { @@ -557,7 +558,6 @@ "CodeFile": "/SamplePages/Animations/Behaviors/BlurBehaviorCode.bind", "XamlCodeFile": "/SamplePages/Animations/Behaviors/BlurBehaviorXaml.bind", "Icon": "/SamplePages/Animations/Behaviors/BlurBehavior.png", - "BadgeUpdateVersionRequired": "Anniversary Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/brushes/PipelineVisualFactory.md" }, { @@ -568,7 +568,6 @@ "CodeFile": "/SamplePages/Animations/Behaviors/SaturationBehaviorCode.bind", "XamlCodeFile": "/SamplePages/Animations/Behaviors/SaturationBehaviorXaml.bind", "Icon": "/SamplePages/Animations/Behaviors/SaturationBehavior.png", - "BadgeUpdateVersionRequired": "Anniversary Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/brushes/PipelineVisualFactory.md" }, { @@ -598,7 +597,6 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Animations", "XamlCodeFile": "ItemsReorderAnimation.bind", "Icon": "/SamplePages/ItemsReorderAnimation/ItemsReorderAnimation.png", - "BadgeUpdateVersionRequired": "Anniversary Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ItemsReorderAnimation.md" }, { @@ -618,7 +616,6 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Animations", "Icon": "/SamplePages/Implicit Animations/ImplicitAnimations.png", "XamlCodeFile": "ImplicitAnimationsCode.bind", - "BadgeUpdateVersionRequired": "Creators Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ImplicitAnimationSet.md" }, { @@ -630,7 +627,6 @@ "XamlCodeFile": "ConnectedAnimationsCode.bind", "DisableXamlEditorRendering": true, "Icon": "/SamplePages/Connected Animations/ConnectedAnimations.png", - "BadgeUpdateVersionRequired": "Creators Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/animations/ConnectedAnimations.md" } ] @@ -713,8 +709,7 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.Connectivity/BluetoothLEHelper", "CodeFile": "BluetoothLEHelperCode.bind", "Icon": "/SamplePages/BluetoothLEHelper/BluetoothLEHelper.png", - "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/BluetoothLEHelper.md", - "BadgeUpdateVersionRequired": "Creators Update required" + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/BluetoothLEHelper.md" }, { "Name": "SystemInformation", @@ -1168,7 +1163,6 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/TextBox/TextBoxExtensions.SurfaceDial.cs", "XamlCodeFile": "/SamplePages/SurfaceDialTextbox/SurfaceDialTextboxCode.bind", "Icon": "/SamplePages/SurfaceDialTextbox/SurfaceDialTextbox.png", - "BadgeUpdateVersionRequired": "Anniversary Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/SurfaceDialTextboxHelper.md" }, { @@ -1178,7 +1172,6 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/VisualExtensions.cs", "XamlCodeFile": "VisualExtensionsCode.bind", "Icon": "/SamplePages/Visual Extensions/VisualExtensions.png", - "BadgeUpdateVersionRequired": "Creators Update required", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/extensions/VisualExtensions.md" }, { From e3c6fe28a0b8e08cd5eda15f730a54a482b7a1a8 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 11 Aug 2021 15:58:23 -0700 Subject: [PATCH 277/299] Move common Shadow base helpers to a folder in Uwp.UI --- .../{ => Shadows}/AttachedShadowBase.cs | 5 +++-- .../AttachedShadowElementContext.cs | 14 +++++++------- .../{Helpers => Shadows}/IAlphaMaskProvider.cs | 0 .../{ => Shadows}/IAttachedShadow.cs | 16 +++++++++++++--- 4 files changed, 23 insertions(+), 12 deletions(-) rename Microsoft.Toolkit.Uwp.UI/{ => Shadows}/AttachedShadowBase.cs (98%) rename Microsoft.Toolkit.Uwp.UI/{ => Shadows}/AttachedShadowElementContext.cs (93%) rename Microsoft.Toolkit.Uwp.UI/{Helpers => Shadows}/IAlphaMaskProvider.cs (100%) rename Microsoft.Toolkit.Uwp.UI/{ => Shadows}/IAttachedShadow.cs (79%) diff --git a/Microsoft.Toolkit.Uwp.UI/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs similarity index 98% rename from Microsoft.Toolkit.Uwp.UI/AttachedShadowBase.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 60efaed47..f398a422b 100644 --- a/Microsoft.Toolkit.Uwp.UI/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -1,6 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. - -using System; using System.Numerics; using System.Runtime.CompilerServices; using Windows.Foundation; diff --git a/Microsoft.Toolkit.Uwp.UI/AttachedShadowElementContext.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs similarity index 93% rename from Microsoft.Toolkit.Uwp.UI/AttachedShadowElementContext.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs index 92950a933..c9687d041 100644 --- a/Microsoft.Toolkit.Uwp.UI/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -1,6 +1,6 @@ -// ------------------------------------------------------ -// Copyright (C) Microsoft. All rights reserved. -// ------------------------------------------------------ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; @@ -18,7 +18,7 @@ namespace Microsoft.Toolkit.Uwp.UI private Dictionary _resources; /// - /// Gets whether or not this has been initialized. + /// Gets a value indicating whether or not this has been initialized. /// public bool IsInitialized { get; private set; } @@ -28,7 +28,7 @@ namespace Microsoft.Toolkit.Uwp.UI public AttachedShadowBase Parent { get; private set; } /// - /// Get the this instance is attached to + /// Gets the this instance is attached to /// public FrameworkElement Element { get; private set; } @@ -43,12 +43,12 @@ namespace Microsoft.Toolkit.Uwp.UI public Compositor Compositor { get; private set; } /// - /// Get the that contains the shadow for this instance + /// Gets the that contains the shadow for this instance /// public SpriteVisual SpriteVisual { get; private set; } /// - /// Get the that is rendered on this instance's + /// Gets the that is rendered on this instance's /// public DropShadow Shadow { get; private set; } diff --git a/Microsoft.Toolkit.Uwp.UI/Helpers/IAlphaMaskProvider.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs similarity index 100% rename from Microsoft.Toolkit.Uwp.UI/Helpers/IAlphaMaskProvider.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs diff --git a/Microsoft.Toolkit.Uwp.UI/IAttachedShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs similarity index 79% rename from Microsoft.Toolkit.Uwp.UI/IAttachedShadow.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs index f3adc9884..3ea72a1f3 100644 --- a/Microsoft.Toolkit.Uwp.UI/IAttachedShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs @@ -1,6 +1,6 @@ -// ------------------------------------------------------ -// Copyright (C) Microsoft. All rights reserved. -// ------------------------------------------------------ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System.Numerics; using Windows.Foundation; @@ -12,15 +12,25 @@ namespace Microsoft.Toolkit.Uwp.UI public interface IAttachedShadow { double BlurRadius { get; set; } + double Opacity { get; set; } + Vector3 Offset { get; set; } + Color Color { get; set; } + bool SupportsOnSizeChangedEvent { get; } + void ConnectElement(FrameworkElement element); + void DisconnectElement(FrameworkElement element); + void OnElementContextInitialized(AttachedShadowElementContext context); + void OnElementContextUninitialized(AttachedShadowElementContext context); + void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize); + AttachedShadowElementContext GetElementContext(FrameworkElement element); } } \ No newline at end of file From f2a82541f0b09622094a813dc79f44886dcf01a3 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 11 Aug 2021 15:58:51 -0700 Subject: [PATCH 278/299] Move AttachedShadow to root namespace for Uwp.UI.Media --- .../Shadows/AttachedCardShadow.cs | 10 +++++----- .../Shadows/AttachedShadowBaseWithOpacityMask.cs | 13 ++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs index 5deb64afc..aceb04473 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -1,16 +1,16 @@ -// ------------------------------------------------------ -// Copyright (C) Microsoft. All rights reserved. -// ------------------------------------------------------ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. -using Microsoft.Graphics.Canvas.Geometry; using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; using Windows.Foundation; using Windows.Foundation.Metadata; using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; -namespace Microsoft.Toolkit.Uwp.UI.Media.Shadows +namespace Microsoft.Toolkit.Uwp.UI.Media { public class AttachedCardShadow : AttachedShadowBase { diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs index 746cc7f84..71d097349 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs @@ -1,14 +1,13 @@ -using Microsoft.Graphics.Canvas.Effects; -using System; -using System.Collections.Generic; -using System.Linq; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Graphics.Canvas.Effects; using Windows.UI.Composition; using Windows.UI.Xaml.Hosting; -namespace Microsoft.Toolkit.Uwp.UI.Media.Shadows +namespace Microsoft.Toolkit.Uwp.UI.Media { /// /// A base class for attached shadows that use an opacity mask to clip content from the shadow. From 72d524cd7a63d989f847c9e2a5b0746a469dfac5 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 11 Aug 2021 17:44:57 -0700 Subject: [PATCH 279/299] Add Vector3 support for Sample Bindings Add additional shortcuts/fallbacks for parsing other Vector formats to different types --- .../Common/Vector3Converter.cs | 57 +++++++++++++++++++ .../Controls/PropertyControl.xaml.cs | 8 +++ .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 2 +- .../Models/PropertyDescriptor/PropertyKind.cs | 3 +- .../ThicknessPropertyOptions.cs | 10 ---- .../Models/Sample.cs | 27 +++++++-- .../Extensions/StringExtensions.cs | 12 ++++ 7 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/ThicknessPropertyOptions.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs b/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs new file mode 100644 index 000000000..2c4dd68e6 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Numerics; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace Microsoft.Toolkit.Uwp.SampleApp.Common +{ + public class Vector3Converter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is string) + { + return value; + } + + var thickness = (Vector3)value; + + return thickness.ToString().TrimStart('<').Replace(" ", string.Empty).TrimEnd('>'); + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + if (value is string vectorString) + { + var vectorTokens = vectorString.Split(',') + .Where(tkn => !string.IsNullOrWhiteSpace(tkn)) + .ToArray(); + switch (vectorTokens.Length) + { + case 1: + var vectorValue = float.Parse(vectorString); + return new Vector3(vectorValue, vectorValue, vectorValue); + case 2: + var xValue = float.Parse(vectorTokens[0]); + var yValue = float.Parse(vectorTokens[1]); + + return new Vector3(xValue, yValue, 0); + case 3: + return new Vector3( + float.Parse(vectorTokens[0]), + float.Parse(vectorTokens[1]), + float.Parse(vectorTokens[2])); + default: + return default(Vector3); + } + } + + return value.ToString(); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs index 51526c861..e992f5616 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs @@ -179,6 +179,14 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls converter = new ThicknessConverter(); break; + case PropertyKind.Vector3: + var vectorTextBox = new TextBox { Text = (propertyDict[option.Name] as ValueHolder).Value.ToString() }; + + controlToAdd = vectorTextBox; + dependencyProperty = TextBox.TextProperty; + converter = new Vector3Converter(); + break; + default: var textBox = new TextBox { Text = (propertyDict[option.Name] as ValueHolder).Value.ToString() }; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 0180bf898..37e339ff9 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -488,6 +488,7 @@ + AutoFocusBehaviorPage.xaml @@ -663,7 +664,6 @@ UniformGridPage.xaml - SampleController.xaml diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs index fcb070359..f45876a6a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs @@ -13,6 +13,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Models Bool, Brush, TimeSpan, - Thickness + Thickness, + Vector3, } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/ThicknessPropertyOptions.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/ThicknessPropertyOptions.cs deleted file mode 100644 index 11b5325b1..000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/ThicknessPropertyOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Uwp.SampleApp.Models -{ - public class ThicknessPropertyOptions : PropertyOptions - { - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs index 6e512819c..e26a50768 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -418,8 +418,12 @@ namespace Microsoft.Toolkit.Uwp.SampleApp { if (proxy[option.Name] is ValueHolder value) { - var newString = value.Value is Windows.UI.Xaml.Media.SolidColorBrush brush ? - brush.Color.ToString() : value.Value.ToString(); + var newString = value.Value switch + { + Windows.UI.Xaml.Media.SolidColorBrush brush => brush.Color.ToString(), + System.Numerics.Vector3 vector => vector.ToString().TrimStart('<').Replace(" ", string.Empty).TrimEnd('>'), + _ => value.Value.ToString() + }; result = result.Replace(option.OriginalString, newString); result = result.Replace("@[" + option.Label + "]@", newString); @@ -630,12 +634,27 @@ namespace Microsoft.Toolkit.Uwp.SampleApp case PropertyKind.Thickness: try { - var thicknessOptions = new ThicknessPropertyOptions { DefaultValue = value }; + var thicknessOptions = new PropertyOptions { DefaultValue = value }; options = thicknessOptions; } catch (Exception ex) { - Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); + Debug.WriteLine($"Unable to extract thickness info from {value}({ex.Message})"); + TrackingManager.TrackException(ex); + continue; + } + + break; + + case PropertyKind.Vector3: + try + { + var vector3Options = new PropertyOptions { DefaultValue = value }; + options = vector3Options; + } + catch (Exception ex) + { + Debug.WriteLine($"Unable to extract vector3 info from {value}({ex.Message})"); TrackingManager.TrackException(ex); continue; } diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs index 7f4982ff9..0aeadc70d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs @@ -95,6 +95,10 @@ namespace Microsoft.Toolkit.Uwp.UI return new(x, y, z); } } + else if (values.Length == 2) + { + return new(text.ToVector2(), 0); + } } } @@ -140,6 +144,14 @@ namespace Microsoft.Toolkit.Uwp.UI return new(x, y, z, w); } } + else if (values.Length == 3) + { + return new(text.ToVector3(), 0); + } + else if (values.Length == 2) + { + return new(text.ToVector2(), 0, 0); + } } } From ed42e4118140dc6590112d738b55bdb67d79c985 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 11 Aug 2021 17:48:30 -0700 Subject: [PATCH 280/299] Add AttachedShadow Sample Had to create static Attached Property location Had to convert Vector3 to string for XAML parsing Made empty strings return zerod --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 3 +- .../Shadows/AttachedShadowWin2DXaml.bind | 24 ++++++++ .../SamplePages/XamlOnlyPage.xaml | 4 +- .../SamplePages/samples.json | 11 +++- .../Extensions/StringExtensions.cs | 24 ++++++-- .../Shadows/AttachedShadowBase.cs | 60 +++---------------- Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs | 59 ++++++++++++++++++ 7 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind create mode 100644 Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 37e339ff9..adcd8ef2a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -627,6 +627,7 @@ Designer + @@ -1534,4 +1535,4 @@ - \ No newline at end of file + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind new file mode 100644 index 000000000..4e8ceb2cc --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 5eddad6e0..7aac97257 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -49,7 +49,9 @@ - + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 426164933..4756dff85 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -170,7 +170,7 @@ "Type": "ScrollHeaderPage", "Subcategory": "Layout", "About": "A UI control that works as a ListView or GridView header control with quick return, sticky and fade behavior.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Behaviors/Headers", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Behaviors/Headers", "XamlCodeFile": "ScrollHeaderCode.bind", "Icon": "/SamplePages/ScrollHeader/ScrollHeader.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/HeaderBehaviors.md" @@ -185,6 +185,15 @@ "Icon": "/SamplePages/GridSplitter/GridSplitter.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/GridSplitter.md" }, + { + "Name": "AttachedDropShadow (Win2D)", + "Subcategory": "Media", + "About": "An AttachedDropShadow allows the creation of a DropShadow for any Xaml FrameworkElement in markup.", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Shadows", + "XamlCodeFile": "/SamplePages/Shadows/AttachedShadowWin2DXaml.bind", + "Icon": "/SamplePages/Shadows/DropShadowPanel.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md" + }, { "Name": "DropShadowPanel", "Subcategory": "Media", diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs index 0aeadc70d..68c5a6cfb 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs @@ -26,7 +26,11 @@ namespace Microsoft.Toolkit.Uwp.UI [Pure] public static Vector2 ToVector2(this string text) { - if (text.Length > 0) + if (text.Length == 0) + { + return Vector2.Zero; + } + else { // The format or is supported text = Unbracket(text); @@ -71,7 +75,11 @@ namespace Microsoft.Toolkit.Uwp.UI [Pure] public static Vector3 ToVector3(this string text) { - if (text.Length > 0) + if (text.Length == 0) + { + return Vector3.Zero; + } + else { text = Unbracket(text); @@ -119,7 +127,11 @@ namespace Microsoft.Toolkit.Uwp.UI [Pure] public static Vector4 ToVector4(this string text) { - if (text.Length > 0) + if (text.Length == 0) + { + return Vector4.Zero; + } + else { text = Unbracket(text); @@ -171,7 +183,11 @@ namespace Microsoft.Toolkit.Uwp.UI [Pure] public static Quaternion ToQuaternion(this string text) { - if (text.Length > 0) + if (text.Length == 0) + { + return new(); + } + else { text = Unbracket(text); diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index f398a422b..2d8e2ccd6 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -17,50 +17,6 @@ namespace Microsoft.Toolkit.Uwp.UI /// public abstract class AttachedShadowBase : DependencyObject { - /// - /// Gets the shadow attached to a by getting the value of the property. - /// - /// The the is attached to. - /// The that is attached to the FrameworkElement. - public static AttachedShadowBase GetShadow(FrameworkElement obj) - { - return (AttachedShadowBase)obj.GetValue(ShadowProperty); - } - - /// - /// Attaches a shadow to an element by setting the property. - /// - /// The to attach the shadow to. - /// The that will be attached to the element - public static void SetShadow(FrameworkElement obj, AttachedShadowBase value) - { - obj.SetValue(ShadowProperty, value); - } - - /// - /// Backing dependency property used to attach shadows to UI elements. - /// - public static readonly DependencyProperty ShadowProperty = - DependencyProperty.RegisterAttached("Shadow", typeof(AttachedShadowBase), typeof(AttachedShadowBase), new PropertyMetadata(null, OnShadowChanged)); - - private static void OnShadowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (!(d is FrameworkElement element)) - { - return; - } - - if (e.OldValue is AttachedShadowBase oldShadow) - { - oldShadow.DisconnectElement(element); - } - - if (e.NewValue is AttachedShadowBase newShadow) - { - newShadow.ConnectElement(element); - } - } - /// /// The for . /// @@ -79,9 +35,9 @@ namespace Microsoft.Toolkit.Uwp.UI public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register( nameof(Offset), - typeof(Vector3), + typeof(string), // Needs to be string as we can't convert in XAML natively from Vector3, see https://github.com/microsoft/microsoft-ui-xaml/issues/3896 typeof(AttachedShadowBase), - new PropertyMetadata(Vector3.Zero, OnDependencyPropertyChanged)); + new PropertyMetadata(string.Empty, OnDependencyPropertyChanged)); /// /// The for @@ -118,11 +74,11 @@ namespace Microsoft.Toolkit.Uwp.UI } /// - /// Gets or sets the offset of the shadow. + /// Gets or sets the offset of the shadow as a string representation of a . /// - public Vector3 Offset + public string Offset { - get => (Vector3)GetValue(OffsetProperty); + get => (string)GetValue(OffsetProperty); set => SetValue(OffsetProperty, value); } @@ -140,7 +96,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// protected internal abstract bool SupportsOnSizeChangedEvent { get; } - private void ConnectElement(FrameworkElement element) + internal void ConnectElement(FrameworkElement element) { if (!IsSupported) { @@ -158,7 +114,7 @@ namespace Microsoft.Toolkit.Uwp.UI ShadowElementContextTable.Add(element, context); } - private void DisconnectElement(FrameworkElement element) + internal void DisconnectElement(FrameworkElement element) { if (ShadowElementContextTable == null) { @@ -308,7 +264,7 @@ namespace Microsoft.Toolkit.Uwp.UI } else if (property == OffsetProperty) { - context.Shadow.Offset = (Vector3)newValue; + context.Shadow.Offset = (Vector3)(newValue as string)?.ToVector3(); } } diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs new file mode 100644 index 000000000..2755dbb79 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI +{ + /// + /// Helper class for attaching shadows to s. + /// + public static class Shadows + { + /// + /// Gets the shadow attached to a by getting the value of the property. + /// + /// The the is attached to. + /// The that is attached to the FrameworkElement. + public static AttachedShadowBase GetAttached(FrameworkElement obj) + { + return (AttachedShadowBase)obj.GetValue(AttachedProperty); + } + + /// + /// Attaches a shadow to an element by setting the property. + /// + /// The to attach the shadow to. + /// The that will be attached to the element + public static void SetAttached(FrameworkElement obj, AttachedShadowBase value) + { + obj.SetValue(AttachedProperty, value); + } + + /// + /// Attached for setting an to a . + /// + public static readonly DependencyProperty AttachedProperty = + DependencyProperty.RegisterAttached("Attached", typeof(AttachedShadowBase), typeof(Shadows), new PropertyMetadata(null, OnShadowChanged)); + + private static void OnShadowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (!(d is FrameworkElement element)) + { + return; + } + + if (e.OldValue is AttachedShadowBase oldShadow) + { + oldShadow.DisconnectElement(element); + } + + if (e.NewValue is AttachedShadowBase newShadow) + { + newShadow.ConnectElement(element); + } + } + + } +} From f48df97bc15019f8624eb35eaa4074fcd8a17918 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 12 Aug 2021 17:04:31 -0700 Subject: [PATCH 281/299] Clean-up some documentation and info about min version to sample app --- .../SamplePages/samples.json | 4 +++- .../Shadows/AttachedCardShadow.cs | 14 ++++++++++---- .../Shadows/AttachedShadowBase.cs | 7 ++++--- .../Shadows/AttachedShadowElementContext.cs | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 4756dff85..968914112 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -192,7 +192,9 @@ "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Shadows", "XamlCodeFile": "/SamplePages/Shadows/AttachedShadowWin2DXaml.bind", "Icon": "/SamplePages/Shadows/DropShadowPanel.png", - "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md" + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/DropShadowPanel.md", + "BadgeUpdateVersionRequired": "May 2019 update required", + "ApiCheck": "Windows.UI.Composition.CompositionVisualSurface" }, { "Name": "DropShadowPanel", diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs index aceb04473..8c57b8a9c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -17,7 +17,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media private const float MaxBlurRadius = 72; private static readonly TypedResourceKey ClipResourceKey = "Clip"; private static readonly bool SupportsCompositionVisualSurface; - private static readonly bool SupportsCompositionGeometricClip; private static readonly TypedResourceKey PathGeometryResourceKey = "PathGeometry"; private static readonly TypedResourceKey RoundedRectangleGeometryResourceKey = "RoundedGeometry"; @@ -34,12 +33,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Media nameof(CornerRadius), typeof(double), typeof(AttachedCardShadow), - new PropertyMetadata(8d, OnDependencyPropertyChanged)); + new PropertyMetadata(4d, OnDependencyPropertyChanged)); // Default WinUI ControlCornerRadius is 4 static AttachedCardShadow() { - SupportsCompositionGeometricClip = ApiInformation.IsTypePresent(typeof(CompositionGeometricClip).FullName); - SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); ; + SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min } /// @@ -51,15 +49,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Media set => SetValue(CornerRadiusProperty, value); } + /// public override bool IsSupported => SupportsCompositionVisualSurface; + + /// protected override bool SupportsOnSizeChangedEvent => true; + /// protected override void OnElementContextUninitialized(AttachedShadowElementContext context) { context.ClearAndDisposeResources(); base.OnElementContextUninitialized(context); } + /// protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue) { if (property == CornerRadiusProperty) @@ -78,6 +81,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media } } + /// protected override CompositionBrush GetShadowMask(AttachedShadowElementContext context) { if (!SupportsCompositionVisualSurface) @@ -115,6 +119,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media return surfaceBrush; } + /// protected override CompositionClip GetShadowClip(AttachedShadowElementContext context) { var pathGeom = context.GetResource(PathGeometryResourceKey) ?? @@ -141,6 +146,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media return clip; } + /// protected override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize) { var sizeAsVec2 = newSize.ToVector2(); diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 2d8e2ccd6..685fca8ac 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -46,7 +46,7 @@ namespace Microsoft.Toolkit.Uwp.UI DependencyProperty.Register(nameof(Opacity), typeof(double), typeof(AttachedShadowBase), new PropertyMetadata(1d, OnDependencyPropertyChanged)); /// - /// Returns whether or not this implementation is supported on the current platform. + /// Gets a value indicating whether or not this implementation is supported on the current platform. /// public abstract bool IsSupported { get; } @@ -56,7 +56,7 @@ namespace Microsoft.Toolkit.Uwp.UI private ConditionalWeakTable ShadowElementContextTable { get; set; } /// - /// Gets or set the blur radius of the shadow. + /// Gets or sets the blur radius of the shadow. /// public double BlurRadius { @@ -92,7 +92,7 @@ namespace Microsoft.Toolkit.Uwp.UI } /// - /// Returns whether or not OnSizeChanged should be called when is fired. + /// Gets a value indicating whether or not OnSizeChanged should be called when is fired. /// protected internal abstract bool SupportsOnSizeChangedEvent { get; } @@ -155,6 +155,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Get the associated for the specified . /// + /// The for the element. public AttachedShadowElementContext GetElementContext(FrameworkElement element) { if (ShadowElementContextTable != null && ShadowElementContextTable.TryGetValue(element, out var context)) diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs index c9687d041..ae17bdf17 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -279,6 +279,7 @@ namespace Microsoft.Toolkit.Uwp.UI { (kvp.Value as IDisposable)?.Dispose(); } + _resources.Clear(); } } From 75e53c9116e7ff001dc25e663b7abed396bf65fc Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 12 Aug 2021 17:06:01 -0700 Subject: [PATCH 282/299] Rename Shadows.Attached to Effects.Shadow --- .../Shadows/AttachedShadowWin2DXaml.bind | 4 ++-- .../SamplePages/XamlOnlyPage.xaml | 2 +- .../Shadows/{Shadows.cs => Effects.cs} | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) rename Microsoft.Toolkit.Uwp.UI/Shadows/{Shadows.cs => Effects.cs} (71%) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind index 4e8ceb2cc..4f3679a3e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind @@ -12,13 +12,13 @@ \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 7aac97257..0741b8a8f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -51,7 +51,7 @@ + ui:Effects.Shadow="{StaticResource AttachedShadow}" /> diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs similarity index 71% rename from Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs index 2755dbb79..d7af17ae7 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/Shadows.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,33 +9,33 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Helper class for attaching shadows to s. /// - public static class Shadows + public static class Effects { /// - /// Gets the shadow attached to a by getting the value of the property. + /// Gets the shadow attached to a by getting the value of the property. /// /// The the is attached to. /// The that is attached to the FrameworkElement. - public static AttachedShadowBase GetAttached(FrameworkElement obj) + public static AttachedShadowBase GetShadow(FrameworkElement obj) { - return (AttachedShadowBase)obj.GetValue(AttachedProperty); + return (AttachedShadowBase)obj.GetValue(ShadowProperty); } /// - /// Attaches a shadow to an element by setting the property. + /// Attaches a shadow to an element by setting the property. /// /// The to attach the shadow to. /// The that will be attached to the element - public static void SetAttached(FrameworkElement obj, AttachedShadowBase value) + public static void SetShadow(FrameworkElement obj, AttachedShadowBase value) { - obj.SetValue(AttachedProperty, value); + obj.SetValue(ShadowProperty, value); } /// /// Attached for setting an to a . /// - public static readonly DependencyProperty AttachedProperty = - DependencyProperty.RegisterAttached("Attached", typeof(AttachedShadowBase), typeof(Shadows), new PropertyMetadata(null, OnShadowChanged)); + public static readonly DependencyProperty ShadowProperty = + DependencyProperty.RegisterAttached("Shadow", typeof(AttachedShadowBase), typeof(Effects), new PropertyMetadata(null, OnShadowChanged)); private static void OnShadowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { From 4a651dd3d7c895d1d5e41b98f9b6711163542ea5 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 12 Aug 2021 17:33:30 -0700 Subject: [PATCH 283/299] Add more samples (though one doesn't work for some reason with raw Border) --- .../Shadows/AttachedShadowWin2DXaml.bind | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind index 4f3679a3e..04e4a8ffd 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind @@ -6,19 +6,69 @@ xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media" mc:Ignorable="d"> - - - - - - \ No newline at end of file + + + + + + + + + + + protected object Image { get; private set; } + /// + public bool WaitUntilLoaded => true; + /// /// Initializes a new instance of the class. /// diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs index 8c57b8a9c..2a937a58c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -16,7 +16,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media { private const float MaxBlurRadius = 72; private static readonly TypedResourceKey ClipResourceKey = "Clip"; - private static readonly bool SupportsCompositionVisualSurface; private static readonly TypedResourceKey PathGeometryResourceKey = "PathGeometry"; private static readonly TypedResourceKey RoundedRectangleGeometryResourceKey = "RoundedGeometry"; @@ -35,11 +34,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media typeof(AttachedCardShadow), new PropertyMetadata(4d, OnDependencyPropertyChanged)); // Default WinUI ControlCornerRadius is 4 - static AttachedCardShadow() - { - SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min - } - /// /// Gets or sets the roundness of the shadow's corners. /// @@ -55,13 +49,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Media /// protected override bool SupportsOnSizeChangedEvent => true; - /// - protected override void OnElementContextUninitialized(AttachedShadowElementContext context) - { - context.ClearAndDisposeResources(); - base.OnElementContextUninitialized(context); - } - /// protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue) { diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs new file mode 100644 index 000000000..235e60cbf --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Shapes; + +namespace Microsoft.Toolkit.Uwp.UI +{ + /// + /// A helper to add a composition based drop shadow to a . + /// + public class AttachedDropShadow : AttachedShadowBase + { + private const float MaxBlurRadius = 72; + + /// + public override bool IsSupported => true; + + /// + protected internal override bool SupportsOnSizeChangedEvent => true; + + private static readonly TypedResourceKey ShadowContainerResourceKey = "ShadowContainer"; + + private static readonly TypedResourceKey PathGeometryResourceKey = "PathGeometry"; + private static readonly TypedResourceKey RectangleGeometryResourceKey = "RectGeometry"; + private static readonly TypedResourceKey ClipResourceKey = "Clip"; + + private static readonly TypedResourceKey RoundedRectangleGeometryResourceKey = "RoundedGeometry"; + private static readonly TypedResourceKey ShapeResourceKey = "Shape"; + private static readonly TypedResourceKey ShapeVisualResourceKey = "ShapeVisual"; + private static readonly TypedResourceKey SurfaceBrushResourceKey = "SurfaceBrush"; + private static readonly TypedResourceKey VisualSurfaceResourceKey = "VisualSurface"; + + /// + /// Gets or sets a value indicating whether the panel uses an alpha mask to create a more precise shadow vs. a quicker rectangle shape. + /// + /// + /// Turn this off to lose fidelity and gain performance of the panel. + /// + public bool IsMasked + { + get { return (bool)GetValue(IsMaskedProperty); } + set { SetValue(IsMaskedProperty, value); } + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty IsMaskedProperty = + DependencyProperty.Register(nameof(IsMasked), typeof(bool), typeof(AttachedDropShadow), new PropertyMetadata(true, OnDependencyPropertyChanged)); + + /// + /// Gets or sets the roundness of the shadow's corners. + /// + public double CornerRadius + { + get => (double)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + + /// + /// The for + /// + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register( + nameof(CornerRadius), + typeof(double), + typeof(AttachedDropShadow), + new PropertyMetadata(4d, OnDependencyPropertyChanged)); // Default WinUI ControlCornerRadius is 4 + + /// + /// Gets or sets the to be used as a backdrop to cast shadows on. + /// + public FrameworkElement CastTo + { + get { return (FrameworkElement)GetValue(CastToProperty); } + set { SetValue(CastToProperty, value); } + } + + /// + /// The for + /// + public static readonly DependencyProperty CastToProperty = + DependencyProperty.Register(nameof(CastTo), typeof(FrameworkElement), typeof(AttachedDropShadow), new PropertyMetadata(null, OnCastToPropertyChanged)); // TODO: Property Change + + private ContainerVisual _container; + + private static void OnCastToPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is AttachedDropShadow shadow) + { + if (e.OldValue is FrameworkElement element) + { + ElementCompositionPreview.SetElementChildVisual(element, null); + element.SizeChanged -= shadow.CastToElement_SizeChanged; + } + + if (e.NewValue is FrameworkElement elementNew) + { + var prevContainer = shadow._container; + + var child = ElementCompositionPreview.GetElementChildVisual(elementNew); + if (child is ContainerVisual visual) + { + shadow._container = visual; + } + else + { + var compositor = ElementCompositionPreview.GetElementVisual(shadow.CastTo).Compositor; + shadow._container = compositor.CreateContainerVisual(); + + ElementCompositionPreview.SetElementChildVisual(elementNew, shadow._container); + } + + // Need to remove all old children from previous container if it's changed + if (prevContainer != null && prevContainer != shadow._container) + { + foreach (var context in shadow.ShadowElementContextTable) + { + if (context.Value.IsInitialized && + prevContainer.Children.Contains(context.Value.SpriteVisual)) + { + prevContainer.Children.Remove(context.Value.SpriteVisual); + } + } + } + + // Make sure all child shadows are hooked into container + foreach (var context in shadow.ShadowElementContextTable) + { + if (context.Value.IsInitialized) + { + shadow.SetElementChildVisual(context.Value); + } + } + + elementNew.SizeChanged += shadow.CastToElement_SizeChanged; + + // Re-trigger updates to all shadow locations for new parent + shadow.CastToElement_SizeChanged(null, null); + } + } + } + + private void CastToElement_SizeChanged(object sender, SizeChangedEventArgs e) + { + // Don't use sender or 'e' here as related to container element not + // element for shadow, grab values off context. (Also may be null from internal call.) + foreach (var context in ShadowElementContextTable) + { + if (context.Value.IsInitialized) + { + // TODO: Should we use ActualWidth/Height instead of RenderSize? + OnSizeChanged(context.Value, context.Value.Element.RenderSize, context.Value.Element.RenderSize); + } + } + } + + /// + protected internal override void OnElementContextUninitialized(AttachedShadowElementContext context) + { + if (_container != null && _container.Children.Contains(context.SpriteVisual)) + { + _container.Children.Remove(context.SpriteVisual); + } + + base.OnElementContextUninitialized(context); + } + + /// + protected override void SetElementChildVisual(AttachedShadowElementContext context) + { + if (_container != null && !_container.Children.Contains(context.SpriteVisual)) + { + _container.Children.InsertAtTop(context.SpriteVisual); + } + } + + /// + protected override CompositionBrush GetShadowMask(AttachedShadowElementContext context) + { + CompositionBrush mask = null; + + if (DesignTimeHelpers.IsRunningInLegacyDesignerMode) + { + return null; + } + + if (context.Element != null) + { + if (IsMasked) + { + // We check for IAlphaMaskProvider first, to ensure that we use the custom + // alpha mask even if Content happens to extend any of the other classes + if (context.Element is IAlphaMaskProvider maskedControl) + { + if (maskedControl.WaitUntilLoaded && !context.Element.IsLoaded) + { + context.Element.Loaded += CustomMaskedElement_Loaded; + } + else + { + mask = maskedControl.GetAlphaMask(); + } + } + else if (context.Element is Image) + { + mask = ((Image)context.Element).GetAlphaMask(); + } + else if (context.Element is Shape) + { + mask = ((Shape)context.Element).GetAlphaMask(); + } + else if (context.Element is TextBlock) + { + mask = ((TextBlock)context.Element).GetAlphaMask(); + } + } + + // If we don't have a mask and have specified rounded corners, we'll generate a simple quick mask. + // This is the same code from link:AttachedCardShadow.cs:GetShadowMask + if (mask == null && SupportsCompositionVisualSurface && CornerRadius > 0) + { + // Create rounded rectangle geometry and add it to a shape + var geometry = context.GetResource(RoundedRectangleGeometryResourceKey) ?? context.AddResource( + RoundedRectangleGeometryResourceKey, + context.Compositor.CreateRoundedRectangleGeometry()); + geometry.CornerRadius = new Vector2((float)CornerRadius); + + var shape = context.GetResource(ShapeResourceKey) ?? context.AddResource(ShapeResourceKey, context.Compositor.CreateSpriteShape(geometry)); + shape.FillBrush = context.Compositor.CreateColorBrush(Colors.Black); + + // Create a ShapeVisual so that our geometry can be rendered to a visual + var shapeVisual = context.GetResource(ShapeVisualResourceKey) ?? + context.AddResource(ShapeVisualResourceKey, context.Compositor.CreateShapeVisual()); + shapeVisual.Shapes.Add(shape); + + // Create a CompositionVisualSurface, which renders our ShapeVisual to a texture + var visualSurface = context.GetResource(VisualSurfaceResourceKey) ?? + context.AddResource(VisualSurfaceResourceKey, context.Compositor.CreateVisualSurface()); + visualSurface.SourceVisual = shapeVisual; + + // Create a CompositionSurfaceBrush to render our CompositionVisualSurface to a brush. + // Now we have a rounded rectangle brush that can be used on as the mask for our shadow. + var surfaceBrush = context.GetResource(SurfaceBrushResourceKey) ?? context.AddResource( + SurfaceBrushResourceKey, + context.Compositor.CreateSurfaceBrush(visualSurface)); + + geometry.Size = visualSurface.SourceSize = shapeVisual.Size = context.Element.RenderSize.ToVector2(); + + mask = surfaceBrush; + } + } + + // Position our shadow in the correct spot to match the corresponding element. + context.SpriteVisual.Size = context.Element.RenderSize.ToVector2(); + context.SpriteVisual.Offset = context.Element.CoordinatesFrom(CastTo).ToVector3(); + + return mask; + } + + private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e) + { + var context = GetElementContext(sender as FrameworkElement); + + context.Element.Loaded -= CustomMaskedElement_Loaded; + + UpdateShadowClip(context); + UpdateShadowMask(context); + } + + /// + /*protected override CompositionClip GetShadowClip(AttachedShadowElementContext context) + { + var rectGeom = context.GetResource(RectangleGeometryResourceKey) ?? + context.AddResource(RectangleGeometryResourceKey, context.Compositor.CreateRoundedRectangleGeometry()); + + rectGeom.Offset = Offset.ToVector2(); + rectGeom.Size = new Vector2((float)context.Element.ActualWidth, (float)context.Element.ActualHeight); + rectGeom.CornerRadius = new Vector2((float)CornerRadius, (float)CornerRadius); + + var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectGeom)); + + return clip;*/ + + /*var pathGeom = context.GetResource(PathGeometryResourceKey) ?? + context.AddResource(PathGeometryResourceKey, context.Compositor.CreatePathGeometry()); + + // Create rounded rectangle geometry at a larger size that compensates for the size of the stroke, + // as we want the inside edge of the stroke to match the edges of the element. + // Additionally, the inside edge of the stroke will have a smaller radius than the radius we specified. + // Using "(StrokeThickness / 2) + Radius" as our rectangle's radius will give us an inside stroke radius that matches the radius we want. + var radius = (MaxBlurRadius / 2) + (float)CornerRadius; + var canvasRectangle = context.Compositor.CreateRoundedRectangleGeometry(); + canvasRectangle.Offset = new Vector2(-MaxBlurRadius / 2, -MaxBlurRadius / 2); + canvasRectangle.Size = new Vector2((float)context.Element.ActualWidth + MaxBlurRadius, (float)context.Element.ActualHeight + MaxBlurRadius); + canvasRectangle.CornerRadius = new Vector2(radius, radius); + + var rectangleShape = context.Compositor.CreateSpriteShape(canvasRectangle); + rectangleShape.StrokeThickness = MaxBlurRadius; + + var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectangleShape.Geometry)); + + return clip;*/ + +//// return null; + ////} + + /// + protected internal override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize) + { + var sizeAsVec2 = newSize.ToVector2(); + + context.SpriteVisual.Size = sizeAsVec2; + context.SpriteVisual.Offset = context.Element.CoordinatesFrom(CastTo).ToVector3(); + + UpdateShadowClip(context); + + base.OnSizeChanged(context, newSize, previousSize); + } + + /// + protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue) + { + if (property == IsMaskedProperty) + { + UpdateShadowMask(context); + } + else if (property == CornerRadiusProperty) + { + //var geometry = context.GetResource(RectangleGeometryResourceKey); + //if (geometry != null) + //{ + // geometry.CornerRadius = new Vector2((float)(double)newValue); + //} + + UpdateShadowClip(context); + } + else + { + base.OnPropertyChanged(context, property, oldValue, newValue); + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 685fca8ac..70d7db724 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using Windows.Foundation; +using Windows.Foundation.Metadata; using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; @@ -17,6 +18,16 @@ namespace Microsoft.Toolkit.Uwp.UI /// public abstract class AttachedShadowBase : DependencyObject { + /// + /// Gets a value indicating whether or not Composition's VisualSurface is supported. + /// + protected static readonly bool SupportsCompositionVisualSurface; + + static AttachedShadowBase() + { + SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min + } + /// /// The for . /// @@ -53,7 +64,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Gets or sets the collection of for each element this is connected to. /// - private ConditionalWeakTable ShadowElementContextTable { get; set; } + protected ConditionalWeakTable ShadowElementContextTable { get; set; } /// /// Gets or sets the blur radius of the shadow. @@ -149,6 +160,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// The that is being uninitialized. protected internal virtual void OnElementContextUninitialized(AttachedShadowElementContext context) { + context.ClearAndDisposeResources(); ElementCompositionPreview.SetElementChildVisual(context.Element, null); } diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs index ae17bdf17..c9cd0d02e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -275,12 +275,15 @@ namespace Microsoft.Toolkit.Uwp.UI /// public void ClearAndDisposeResources() { - foreach (var kvp in _resources) + if (_resources != null) { - (kvp.Value as IDisposable)?.Dispose(); - } + foreach (var kvp in _resources) + { + (kvp.Value as IDisposable)?.Dispose(); + } - _resources.Clear(); + _resources.Clear(); + } } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs index dbd6be7e8..011560e82 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/IAlphaMaskProvider.cs @@ -11,6 +11,11 @@ namespace Microsoft.Toolkit.Uwp.UI /// public interface IAlphaMaskProvider { + /// + /// Gets a value indicating whether the AlphaMask needs to be retrieved after the element has loaded. + /// + bool WaitUntilLoaded { get; } + /// /// This method should return the appropiate alpha mask to be used in the shadow of this control /// diff --git a/Microsoft.Toolkit.Uwp/Extensions/PointExtensions.cs b/Microsoft.Toolkit.Uwp/Extensions/PointExtensions.cs index af0a413ad..e8c393bc7 100644 --- a/Microsoft.Toolkit.Uwp/Extensions/PointExtensions.cs +++ b/Microsoft.Toolkit.Uwp/Extensions/PointExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Contracts; +using System.Numerics; using System.Runtime.CompilerServices; using Point = Windows.Foundation.Point; using Rect = Windows.Foundation.Rect; @@ -54,5 +55,17 @@ namespace Microsoft.Toolkit.Uwp { return new Rect(point, size); } + + /// + /// Creates a new of the specified point with 0 for the coordinate. + /// + /// to transform to a . + /// New representing the X,Y position of the . + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 ToVector3(this Point point) + { + return new Vector3(point.ToVector2(), 0f); + } } } \ No newline at end of file From 864791961c5884cd02bc04017dba8a81013a7607 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Mon, 23 Aug 2021 10:12:42 -0700 Subject: [PATCH 286/299] Add initial basic animation support for Shadows --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 1 + .../Animations/Effects/FadeBehaviorXaml.bind | 1 - .../Shadows/AnimatedCardShadowXaml.bind | 42 +++++++++ .../SamplePages/XamlOnlyPage.xaml | 1 + .../SamplePages/samples.json | 9 ++ ...adowAnimation{TShadow,TValue,TKeyFrame}.cs | 91 +++++++++++++++++++ .../Xaml/Shadows/OffsetDropShadowAnimation.cs | 25 +++++ 7 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Shadows/AnimatedCardShadowXaml.bind create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 05b7f2d9e..9ce613af5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -632,6 +632,7 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Effects/FadeBehaviorXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Effects/FadeBehaviorXaml.bind index 1a0f0eb90..faa191462 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Effects/FadeBehaviorXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Animations/Effects/FadeBehaviorXaml.bind @@ -7,7 +7,6 @@ xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations" xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors" - xmlns:core="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Properties/AssemblyInfo.cs index b9d3571a0..94d361ddf 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Properties/AssemblyInfo.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Properties/AssemblyInfo.cs @@ -4,4 +4,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Media")] \ No newline at end of file +[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Behaviors")] +[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Media")] diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs index c9a678455..f0c8420c9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs @@ -21,8 +21,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// properties. This can differ from to facilitate XAML parsing. /// /// The actual type of keyframe values in use. - public abstract class ShadowAnimation : Animation - where TShadow : FrameworkElement + public abstract class ShadowAnimation : Animation, IAttachedTimeline + where TShadow : AttachedShadowBase where TKeyFrame : unmanaged { /// @@ -46,21 +46,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint) { - if (Target is not TShadow target) - { - static AnimationBuilder ThrowTargetNullException() => throw new ArgumentNullException("The target element is null, make sure to set the Target property"); - - return ThrowTargetNullException(); - } - - var shadowBase = Effects.GetShadow(Target); - if (shadowBase == null) - { - static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target's shadow is null, make sure to set the Target property to an element with a Shadow"); - - return ThrowArgumentNullException(); - } + throw new NotSupportedException(); + } + /// + public AnimationBuilder AppendToBuilder(AnimationBuilder builder, UIElement parent, TimeSpan? delayHint = null, TimeSpan? durationHint = null, EasingType? easingTypeHint = null, EasingMode? easingModeHint = null) + { if (ExplicitTarget is not string explicitTarget) { static AnimationBuilder ThrowArgumentNullException() @@ -72,20 +63,52 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations return ThrowArgumentNullException(); } - var shadow = shadowBase.GetElementContext(Target).Shadow; + if (Target is TShadow allShadows) + { + // in this case we'll animate all the shadows being used. + foreach (var context in allShadows.GetElementContextEnumerable()) //// TODO: Find better way!!! + { + NormalizedKeyFrameAnimationBuilder.Composition keyFrameBuilder = new( + explicitTarget, + Delay ?? delayHint ?? DefaultDelay, + Duration ?? durationHint ?? DefaultDuration, + Repeat, + DelayBehavior); - NormalizedKeyFrameAnimationBuilder.Composition keyFrameBuilder = new( - explicitTarget, - Delay ?? delayHint ?? DefaultDelay, - Duration ?? durationHint ?? DefaultDuration, - Repeat, - DelayBehavior); + AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint); - AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint); + CompositionAnimation animation = keyFrameBuilder.GetAnimation(context.Shadow, out _); - CompositionAnimation animation = keyFrameBuilder.GetAnimation(shadow, out _); + builder.ExternalAnimation(context.Shadow, animation); + } - return builder.ExternalAnimation(shadow, animation); + return builder; + } + else + { + var shadowBase = Effects.GetShadow(parent as FrameworkElement); + if (shadowBase == null) + { + static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target's shadow is null, make sure to set the Target property to an element with a Shadow"); + + return ThrowArgumentNullException(); + } + + var shadow = shadowBase.GetElementContext((FrameworkElement)parent).Shadow; + + NormalizedKeyFrameAnimationBuilder.Composition keyFrameBuilder = new( + explicitTarget, + Delay ?? delayHint ?? DefaultDelay, + Duration ?? durationHint ?? DefaultDuration, + Repeat, + DelayBehavior); + + AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint); + + CompositionAnimation animation = keyFrameBuilder.GetAnimation(shadow, out _); + + return builder.ExternalAnimation(shadow, animation); + } } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs index c3631c9d9..39472358c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs @@ -119,7 +119,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations { foreach (object node in this) { - if (node is ITimeline timeline) + if (node is IAttachedTimeline attachedTimeline) + { + var builder = AnimationBuilder.Create(); + + attachedTimeline.AppendToBuilder(builder, element); + + await builder.StartAsync(element, token); + } + else if (node is ITimeline timeline) { var builder = AnimationBuilder.Create(); @@ -166,6 +174,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations { switch (node) { + case IAttachedTimeline attachedTimeline: + builder = attachedTimeline.AppendToBuilder(builder, element); + break; case ITimeline timeline: builder = timeline.AppendToBuilder(builder); break; diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IAttachedTimeline.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IAttachedTimeline.cs new file mode 100644 index 000000000..ea4339a2c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IAttachedTimeline.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media.Animation; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// An interface representing a XAML model for a custom animation that requires a specific parent context. + /// + public interface IAttachedTimeline + { + /// + /// Appends the current animation to a target instance. + /// This method is used when the current instance is explicitly triggered. + /// + /// The target instance to schedule the animation on. + /// The parent this animation will be started on. + /// A hint for the animation delay, if present. + /// A hint for the animation duration, if present. + /// A hint for the easing type, if present. + /// A hint for the easing mode, if present. + /// The same instance as . + AnimationBuilder AppendToBuilder( + AnimationBuilder builder, + UIElement parent, + TimeSpan? delayHint = null, + TimeSpan? durationHint = null, + EasingType? easingTypeHint = null, + EasingMode? easingModeHint = null); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs index 092ff4b91..12544e0af 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs @@ -13,7 +13,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public interface ITimeline { /// - /// Appens the current animation to a target instance. + /// Appends the current animation to a target instance. /// This method is used when the current instance is explicitly triggered. /// /// The target instance to schedule the animation on. diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs index 9c37718e9..4fcd561cc 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs @@ -11,7 +11,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// /// An offset animation working on the composition layer. /// - public sealed class OffsetDropShadowAnimation : ShadowAnimation + public sealed class OffsetDropShadowAnimation : ShadowAnimation { /// protected override string ExplicitTarget => nameof(DropShadow.Offset); diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs index 0ed6bbb2d..827dc5556 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs @@ -58,13 +58,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors ThrowArgumentNullException(); } + UIElement parent = null; + if (TargetObject is not null) { Animation.Start(TargetObject); } + else if (Animation.ParentReference?.TryGetTarget(out parent) == true) //// TODO: Tidy... apply same pattern to Activities? + { + Animation.Start(parent); + } else { - Animation.Start(); + Animation.Start(sender as UIElement); } return null!; diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs index b85bfc941..b4a7e0f5e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs @@ -58,13 +58,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors ThrowArgumentNullException(); } + UIElement parent = null; + if (TargetObject is not null) { Animation.Stop(TargetObject); } + else if (Animation.ParentReference?.TryGetTarget(out parent) == true) //// TODO: Tidy... + { + Animation.Stop(parent); + } else { - Animation.Stop(); + Animation.Stop(sender as UIElement); } return null!; diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs index 235e60cbf..6df8015e3 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs @@ -123,22 +123,22 @@ namespace Microsoft.Toolkit.Uwp.UI // Need to remove all old children from previous container if it's changed if (prevContainer != null && prevContainer != shadow._container) { - foreach (var context in shadow.ShadowElementContextTable) + foreach (var context in shadow.GetElementContextEnumerable()) { - if (context.Value.IsInitialized && - prevContainer.Children.Contains(context.Value.SpriteVisual)) + if (context.IsInitialized && + prevContainer.Children.Contains(context.SpriteVisual)) { - prevContainer.Children.Remove(context.Value.SpriteVisual); + prevContainer.Children.Remove(context.SpriteVisual); } } } // Make sure all child shadows are hooked into container - foreach (var context in shadow.ShadowElementContextTable) + foreach (var context in shadow.GetElementContextEnumerable()) { - if (context.Value.IsInitialized) + if (context.IsInitialized) { - shadow.SetElementChildVisual(context.Value); + shadow.SetElementChildVisual(context); } } @@ -154,12 +154,12 @@ namespace Microsoft.Toolkit.Uwp.UI { // Don't use sender or 'e' here as related to container element not // element for shadow, grab values off context. (Also may be null from internal call.) - foreach (var context in ShadowElementContextTable) + foreach (var context in GetElementContextEnumerable()) { - if (context.Value.IsInitialized) + if (context.IsInitialized) { // TODO: Should we use ActualWidth/Height instead of RenderSize? - OnSizeChanged(context.Value, context.Value.Element.RenderSize, context.Value.Element.RenderSize); + OnSizeChanged(context, context.Element.RenderSize, context.Element.RenderSize); } } } diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 70d7db724..11e981f5a 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using Windows.Foundation; @@ -64,7 +65,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Gets or sets the collection of for each element this is connected to. /// - protected ConditionalWeakTable ShadowElementContextTable { get; set; } + private ConditionalWeakTable ShadowElementContextTable { get; set; } /// /// Gets or sets the blur radius of the shadow. @@ -178,6 +179,18 @@ namespace Microsoft.Toolkit.Uwp.UI return null; } + /// + /// Gets an enumerator over the current list of of elements using this shared shadow definition. + /// + /// Enumeration of objects. + public IEnumerable GetElementContextEnumerable() + { + foreach (var kvp in ShadowElementContextTable) + { + yield return kvp.Value; + } + } + /// /// Sets as a child visual on /// From 08ffdcf84ea4e21a35679e0e2dd646d9f381ce8c Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 25 Aug 2021 10:23:24 -0700 Subject: [PATCH 288/299] Fix issues with XML documentation for new shadows feature --- .../Shadows/AttachedCardShadow.cs | 13 ++- .../AttachedShadowBaseWithOpacityMask.cs | 110 ------------------ .../Shadows/AttachedDropShadow.cs | 59 ++-------- .../Shadows/AttachedShadowBase.cs | 46 +++----- .../Shadows/AttachedShadowElementContext.cs | 37 +++++- Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs | 3 +- .../Shadows/IAttachedShadow.cs | 42 ++++--- Microsoft.Toolkit/TypedResourceKey.cs | 16 ++- 8 files changed, 113 insertions(+), 213 deletions(-) delete mode 100644 Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs index 2a937a58c..e0643ae08 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -5,13 +5,18 @@ using System.Numerics; using Microsoft.Graphics.Canvas.Geometry; using Windows.Foundation; -using Windows.Foundation.Metadata; using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Media { + /// + /// A performant rectangular which can be attached to any . It uses Win2D to create a clipped area of the outline of the element such that transparent elements don't see the shadow below them, and the shadow can be attached without having to project to another surface. It is animatable, can be shared via a resource, and used in a . + /// + /// + /// This shadow will not work on which is directly clipping to its bounds (e.g. a using a ). An extra can instead be applied around the clipped border with the Shadow to create the desired effect. Most existing controls due to how they're templated will not encounter this behavior or require this workaround. + /// public class AttachedCardShadow : AttachedShadowBase { private const float MaxBlurRadius = 72; @@ -109,6 +114,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Media /// protected override CompositionClip GetShadowClip(AttachedShadowElementContext context) { + // The way this shadow works without the need to project on another element is because + // we're clipping the inner part of the shadow which would be cast on the element + // itself away. This method is creating an outline so that we are only showing the + // parts of the shadow that are outside the element's context. + // Note: This does cause an issue if the element does clip itself to its bounds, as then + // the shadowed area is clipped as well. var pathGeom = context.GetResource(PathGeometryResourceKey) ?? context.AddResource(PathGeometryResourceKey, context.Compositor.CreatePathGeometry()); var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(pathGeom)); diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs deleted file mode 100644 index 71d097349..000000000 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedShadowBaseWithOpacityMask.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Numerics; -using Microsoft.Graphics.Canvas.Effects; -using Windows.UI.Composition; -using Windows.UI.Xaml.Hosting; - -namespace Microsoft.Toolkit.Uwp.UI.Media -{ - /// - /// A base class for attached shadows that use an opacity mask to clip content from the shadow. - /// - public abstract class AttachedShadowBaseWithOpacityMask : AttachedShadowBase - { - private const string AlphaMaskSourceKey = "AttachedShadowAlphaMask"; - private const string SpriteVisualSourceKey = "AttachedShadowSpriteVisual"; - - private static readonly TypedResourceKey OpacityMaskEffectBrushResourceKey = "AttachedShadowSpriteVisualEffectBrush"; - private static readonly TypedResourceKey OpacityMaskResourceKey = "AttachedShadowSpriteVisualOpacityMask"; - private static readonly TypedResourceKey OpacityMaskVisualResourceKey = "AttachedShadowSpriteVisualOpacityMaskVisual"; - private static readonly TypedResourceKey OpacityMaskVisualSurfaceResourceKey = "AttachedShadowSpriteVisualOpacityMaskSurface"; - private static readonly TypedResourceKey OpacityMaskSurfaceBrushResourceKey = - "AttachedShadowSpriteVisualOpacityMaskSurfaceBrush"; - private static readonly TypedResourceKey AlphaMaskEffectResourceKey = "AttachedShadowSpriteVisualAlphaMaskEffect"; - - /// - /// Update the opacity mask for the shadow's . - /// - /// The this operation will be performed on. - protected void UpdateVisualOpacityMask(AttachedShadowElementContext context) - { - if (!context.IsInitialized) - { - return; - } - - var brush = GetisualOpacityMask(context); - if (brush != null) - { - context.AddResource(OpacityMaskResourceKey, brush); - } - else - { - context.RemoveResource(OpacityMaskResourceKey)?.Dispose(); - } - } - - /// - /// Override and return a that serves as an opacity mask for the shadow's - /// - protected abstract CompositionBrush GetisualOpacityMask(AttachedShadowElementContext context); - - protected override void OnElementContextInitialized(AttachedShadowElementContext context) - { - UpdateVisualOpacityMask(context); - base.OnElementContextInitialized(context); - } - - protected override void OnElementContextUninitialized(AttachedShadowElementContext context) - { - context.RemoveAndDisposeResource(OpacityMaskResourceKey); - context.RemoveAndDisposeResource(OpacityMaskVisualResourceKey); - context.RemoveAndDisposeResource(OpacityMaskVisualSurfaceResourceKey); - context.RemoveAndDisposeResource(OpacityMaskSurfaceBrushResourceKey); - context.RemoveAndDisposeResource(OpacityMaskEffectBrushResourceKey); - context.RemoveAndDisposeResource(AlphaMaskEffectResourceKey); - base.OnElementContextUninitialized(context); - } - - protected override void SetElementChildVisual(AttachedShadowElementContext context) - { - if (context.TryGetResource(OpacityMaskResourceKey, out var opacityMask)) - { - var visualSurface = context.GetResource(OpacityMaskVisualSurfaceResourceKey) ?? context.AddResource( - OpacityMaskVisualSurfaceResourceKey, - context.Compositor.CreateVisualSurface()); - visualSurface.SourceVisual = context.SpriteVisual; - visualSurface.StartAnimation(nameof(visualSurface.SourceSize), context.Compositor.CreateExpressionAnimation("this.SourceVisual.Size")); - - var surfaceBrush = context.GetResource(OpacityMaskSurfaceBrushResourceKey) ?? context.AddResource( - OpacityMaskSurfaceBrushResourceKey, - context.Compositor.CreateSurfaceBrush(visualSurface)); - var alphaMask = context.GetResource(AlphaMaskEffectResourceKey) ?? context.AddResource(AlphaMaskEffectResourceKey, new AlphaMaskEffect()); - alphaMask.Source = new CompositionEffectSourceParameter(SpriteVisualSourceKey); - alphaMask.AlphaMask = new CompositionEffectSourceParameter(AlphaMaskSourceKey); - - using (var factory = context.Compositor.CreateEffectFactory(alphaMask)) - { - context.RemoveResource(OpacityMaskEffectBrushResourceKey)?.Dispose(); - var brush = context.AddResource(OpacityMaskEffectBrushResourceKey, factory.CreateBrush()); - brush.SetSourceParameter(SpriteVisualSourceKey, surfaceBrush); - brush.SetSourceParameter(AlphaMaskSourceKey, opacityMask); - - var visual = context.GetResource(OpacityMaskVisualResourceKey) ?? context.AddResource( - OpacityMaskVisualResourceKey, - context.Compositor.CreateSpriteVisual()); - visual.RelativeSizeAdjustment = Vector2.One; - visual.Brush = brush; - ElementCompositionPreview.SetElementChildVisual(context.Element, visual); - } - } - else - { - base.SetElementChildVisual(context); - } - } - } -} diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs index 6df8015e3..d472a6a1d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Linq; @@ -27,12 +31,6 @@ namespace Microsoft.Toolkit.Uwp.UI /// protected internal override bool SupportsOnSizeChangedEvent => true; - private static readonly TypedResourceKey ShadowContainerResourceKey = "ShadowContainer"; - - private static readonly TypedResourceKey PathGeometryResourceKey = "PathGeometry"; - private static readonly TypedResourceKey RectangleGeometryResourceKey = "RectGeometry"; - private static readonly TypedResourceKey ClipResourceKey = "Clip"; - private static readonly TypedResourceKey RoundedRectangleGeometryResourceKey = "RoundedGeometry"; private static readonly TypedResourceKey ShapeResourceKey = "Shape"; private static readonly TypedResourceKey ShapeVisualResourceKey = "ShapeVisual"; @@ -277,43 +275,6 @@ namespace Microsoft.Toolkit.Uwp.UI UpdateShadowMask(context); } - /// - /*protected override CompositionClip GetShadowClip(AttachedShadowElementContext context) - { - var rectGeom = context.GetResource(RectangleGeometryResourceKey) ?? - context.AddResource(RectangleGeometryResourceKey, context.Compositor.CreateRoundedRectangleGeometry()); - - rectGeom.Offset = Offset.ToVector2(); - rectGeom.Size = new Vector2((float)context.Element.ActualWidth, (float)context.Element.ActualHeight); - rectGeom.CornerRadius = new Vector2((float)CornerRadius, (float)CornerRadius); - - var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectGeom)); - - return clip;*/ - - /*var pathGeom = context.GetResource(PathGeometryResourceKey) ?? - context.AddResource(PathGeometryResourceKey, context.Compositor.CreatePathGeometry()); - - // Create rounded rectangle geometry at a larger size that compensates for the size of the stroke, - // as we want the inside edge of the stroke to match the edges of the element. - // Additionally, the inside edge of the stroke will have a smaller radius than the radius we specified. - // Using "(StrokeThickness / 2) + Radius" as our rectangle's radius will give us an inside stroke radius that matches the radius we want. - var radius = (MaxBlurRadius / 2) + (float)CornerRadius; - var canvasRectangle = context.Compositor.CreateRoundedRectangleGeometry(); - canvasRectangle.Offset = new Vector2(-MaxBlurRadius / 2, -MaxBlurRadius / 2); - canvasRectangle.Size = new Vector2((float)context.Element.ActualWidth + MaxBlurRadius, (float)context.Element.ActualHeight + MaxBlurRadius); - canvasRectangle.CornerRadius = new Vector2(radius, radius); - - var rectangleShape = context.Compositor.CreateSpriteShape(canvasRectangle); - rectangleShape.StrokeThickness = MaxBlurRadius; - - var clip = context.GetResource(ClipResourceKey) ?? context.AddResource(ClipResourceKey, context.Compositor.CreateGeometricClip(rectangleShape.Geometry)); - - return clip;*/ - -//// return null; - ////} - /// protected internal override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize) { @@ -336,13 +297,13 @@ namespace Microsoft.Toolkit.Uwp.UI } else if (property == CornerRadiusProperty) { - //var geometry = context.GetResource(RectangleGeometryResourceKey); - //if (geometry != null) - //{ - // geometry.CornerRadius = new Vector2((float)(double)newValue); - //} + var geometry = context.GetResource(RoundedRectangleGeometryResourceKey); + if (geometry != null) + { + geometry.CornerRadius = new Vector2((float)(double)newValue); + } - UpdateShadowClip(context); + UpdateShadowMask(context); } else { diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 11e981f5a..bcd2d405c 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -17,7 +17,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// The base class for attached shadows. /// - public abstract class AttachedShadowBase : DependencyObject + public abstract class AttachedShadowBase : DependencyObject, IAttachedShadow { /// /// Gets a value indicating whether or not Composition's VisualSurface is supported. @@ -67,36 +67,28 @@ namespace Microsoft.Toolkit.Uwp.UI /// private ConditionalWeakTable ShadowElementContextTable { get; set; } - /// - /// Gets or sets the blur radius of the shadow. - /// + /// public double BlurRadius { get => (double)GetValue(BlurRadiusProperty); set => SetValue(BlurRadiusProperty, value); } - /// - /// Gets or sets the opacity of the shadow. - /// + /// public double Opacity { get => (double)GetValue(OpacityProperty); set => SetValue(OpacityProperty, value); } - /// - /// Gets or sets the offset of the shadow as a string representation of a . - /// + /// public string Offset { get => (string)GetValue(OffsetProperty); set => SetValue(OffsetProperty, value); } - /// - /// Gets or sets the color of the shadow. - /// + /// public Color Color { get => (Color)GetValue(ColorProperty); @@ -108,6 +100,14 @@ namespace Microsoft.Toolkit.Uwp.UI /// protected internal abstract bool SupportsOnSizeChangedEvent { get; } + /// + /// Use this method as the for DependencyProperties in derived classes. + /// + protected static void OnDependencyPropertyChanged(object sender, DependencyPropertyChangedEventArgs args) + { + (sender as AttachedShadowBase)?.CallPropertyChangedForEachElement(args.Property, args.OldValue, args.NewValue); + } + internal void ConnectElement(FrameworkElement element) { if (!IsSupported) @@ -165,10 +165,7 @@ namespace Microsoft.Toolkit.Uwp.UI ElementCompositionPreview.SetElementChildVisual(context.Element, null); } - /// - /// Get the associated for the specified . - /// - /// The for the element. + /// public AttachedShadowElementContext GetElementContext(FrameworkElement element) { if (ShadowElementContextTable != null && ShadowElementContextTable.TryGetValue(element, out var context)) @@ -179,10 +176,7 @@ namespace Microsoft.Toolkit.Uwp.UI return null; } - /// - /// Gets an enumerator over the current list of of elements using this shared shadow definition. - /// - /// Enumeration of objects. + /// public IEnumerable GetElementContextEnumerable() { foreach (var kvp in ShadowElementContextTable) @@ -200,14 +194,6 @@ namespace Microsoft.Toolkit.Uwp.UI ElementCompositionPreview.SetElementChildVisual(context.Element, context.SpriteVisual); } - /// - /// Use this method as the for DependencyProperties in derived classes. - /// - protected static void OnDependencyPropertyChanged(object sender, DependencyPropertyChangedEventArgs args) - { - (sender as AttachedShadowBase)?.CallPropertyChangedForEachElement(args.Property, args.OldValue, args.NewValue); - } - private void CallPropertyChangedForEachElement(DependencyProperty property, object oldValue, object newValue) { if (ShadowElementContextTable == null) @@ -227,6 +213,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Get a in the shape of the element that is casting the shadow. /// + /// A representing the shape of an element. protected virtual CompositionBrush GetShadowMask(AttachedShadowElementContext context) { return null; @@ -235,6 +222,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Get the for the shadow's /// + /// A for the extent of the shadowed area. protected virtual CompositionClip GetShadowClip(AttachedShadowElementContext context) { return null; diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs index c9cd0d02e..07116f4c3 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -11,6 +11,9 @@ using Windows.UI.Xaml.Hosting; namespace Microsoft.Toolkit.Uwp.UI { + /// + /// Class which maintains the context of a for a particular linked to the definition of that shadow provided by the implementation being used. + /// public sealed class AttachedShadowElementContext { private bool _isConnected; @@ -52,7 +55,12 @@ namespace Microsoft.Toolkit.Uwp.UI /// public DropShadow Shadow { get; private set; } - public void ConnectToElement(AttachedShadowBase parent, FrameworkElement element) + /// + /// Connects a to its parent definition. + /// + /// The that is using this context. + /// The that a shadow is being attached to. + internal void ConnectToElement(AttachedShadowBase parent, FrameworkElement element) { if (_isConnected) { @@ -159,7 +167,10 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Adds a resource to this instance's resource dictionary with the specified key /// - /// The resource that was added + /// The type of the resource being added. + /// Key to use to lookup the resource later. + /// Object to store within the resource dictionary. + /// The added resource public T AddResource(string key, T resource) { _resources = _resources ?? new Dictionary(); @@ -178,6 +189,9 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Retrieves a resource with the specified key and type if it exists /// + /// The type of the resource being retrieved. + /// Key to use to lookup the resource. + /// Object to retrieved from the resource dictionary or default value. /// True if the resource exists, false otherwise public bool TryGetResource(string key, out T resource) { @@ -194,6 +208,9 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Retries a resource with the specified key and type /// + /// The type of the resource being retrieved. + /// Key to use to lookup the resource. + /// The resource if available, otherwise default value. public T GetResource(string key) { if (TryGetResource(key, out T resource)) @@ -207,6 +224,8 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Removes an existing resource with the specified key and type /// + /// The type of the resource being removed. + /// Key to use to lookup the resource. /// The resource that was removed, if any public T RemoveResource(string key) { @@ -225,8 +244,11 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Removes an existing resource with the specified key and type, and disposes it /// + /// The type of the resource being removed. + /// Key to use to lookup the resource. /// The resource that was removed, if any - public T RemoveAndDisposeResource(string key) where T : IDisposable + public T RemoveAndDisposeResource(string key) + where T : IDisposable { if (_resources.TryGetValue(key, out var objResource)) { @@ -244,31 +266,38 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Adds a resource to this instance's collection with the specified key /// + /// The type of the resource being added. /// The resource that was added public T AddResource(TypedResourceKey key, T resource) => AddResource(key.Key, resource); /// /// Retrieves a resource with the specified key and type if it exists /// + /// The type of the resource being retrieved. /// True if the resource exists, false otherwise public bool TryGetResource(TypedResourceKey key, out T resource) => TryGetResource(key.Key, out resource); /// /// Retries a resource with the specified key and type /// + /// The type of the resource being retrieved. + /// The resource if it exists or a default value. public T GetResource(TypedResourceKey key) => GetResource(key.Key); /// /// Removes an existing resource with the specified key and type /// + /// The type of the resource being removed. /// The resource that was removed, if any public T RemoveResource(TypedResourceKey key) => RemoveResource(key.Key); /// /// Removes an existing resource with the specified key and type, and disposes it /// + /// The type of the resource being removed. /// The resource that was removed, if any - public T RemoveAndDisposeResource(TypedResourceKey key) where T : IDisposable => RemoveAndDisposeResource(key.Key); + public T RemoveAndDisposeResource(TypedResourceKey key) + where T : IDisposable => RemoveAndDisposeResource(key.Key); /// /// Disposes of any resources that implement and then clears all resources diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs index d7af17ae7..5cb2235c6 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/Effects.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -54,6 +54,5 @@ namespace Microsoft.Toolkit.Uwp.UI newShadow.ConnectElement(element); } } - } } diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs index 3ea72a1f3..afababd92 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Numerics; using Windows.Foundation; using Windows.UI; @@ -9,28 +10,41 @@ using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI { + /// + /// Interface representing the common properties found within an attached shadow, for implementation. + /// public interface IAttachedShadow { + /// + /// Gets or sets the blur radius of the shadow. + /// double BlurRadius { get; set; } + /// + /// Gets or sets the opacity of the shadow. + /// double Opacity { get; set; } - Vector3 Offset { get; set; } + /// + /// Gets or sets the offset of the shadow as a string representation of a . + /// + string Offset { get; set; } + /// + /// Gets or sets the color of the shadow. + /// Color Color { get; set; } - bool SupportsOnSizeChangedEvent { get; } - - void ConnectElement(FrameworkElement element); - - void DisconnectElement(FrameworkElement element); - - void OnElementContextInitialized(AttachedShadowElementContext context); - - void OnElementContextUninitialized(AttachedShadowElementContext context); - - void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize); - + /// + /// Get the associated for the specified . + /// + /// The for the element. AttachedShadowElementContext GetElementContext(FrameworkElement element); + + /// + /// Gets an enumeration over the current list of of elements using this shared shadow definition. + /// + /// Enumeration of objects. + IEnumerable GetElementContextEnumerable(); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit/TypedResourceKey.cs b/Microsoft.Toolkit/TypedResourceKey.cs index 9fc439fbe..9e46ed107 100644 --- a/Microsoft.Toolkit/TypedResourceKey.cs +++ b/Microsoft.Toolkit/TypedResourceKey.cs @@ -1,27 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; namespace Microsoft.Toolkit { /// - /// A generic classed that can be used to retrieve keyed resources of the specified type. + /// A generic class that can be used to retrieve keyed resources of the specified type. /// /// The of resource the will retrieve. public class TypedResourceKey { /// - /// Create a new with the specified key + /// Initializes a new instance of the class with the specified key. /// /// The resource's key public TypedResourceKey(string key) => Key = key; /// - /// The key of the resource to be retrieved. + /// Gets the key of the resource to be retrieved. /// public string Key { get; } + /// + /// Implicit operator for transforming a string into a key. + /// + /// The key string. public static implicit operator TypedResourceKey(string key) { return new TypedResourceKey(key); } } -} \ No newline at end of file +} From 164eefd5c01621c2f08e5d9ad4f3bb5df1091e24 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 25 Aug 2021 10:52:58 -0700 Subject: [PATCH 289/299] Clean-up ShadowAnimation type and add rest of animatable properties --- .../SamplePages/XamlOnlyPage.xaml | 3 +++ ...s => ShadowAnimation{TValue,TKeyFrame}.cs} | 18 ++++++------- .../Shadows/BlurRadiusDropShadowAnimation.cs | 23 ++++++++++++++++ .../Xaml/Shadows/ColorDropShadowAnimation.cs | 26 +++++++++++++++++++ .../Xaml/Shadows/OffsetDropShadowAnimation.cs | 3 +-- .../Shadows/OpacityDropShadowAnimation.cs | 23 ++++++++++++++++ 6 files changed, 84 insertions(+), 12 deletions(-) rename Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/{ShadowAnimation{TShadow,TValue,TKeyFrame}.cs => ShadowAnimation{TValue,TKeyFrame}.cs} (87%) create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/BlurRadiusDropShadowAnimation.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/ColorDropShadowAnimation.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OpacityDropShadowAnimation.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 1c39dc489..a0df1395c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -67,7 +67,10 @@ + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs similarity index 87% rename from Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs rename to Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs index f0c8420c9..b65654bd4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TShadow,TValue,TKeyFrame}.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs @@ -13,24 +13,22 @@ using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions; namespace Microsoft.Toolkit.Uwp.UI.Animations { /// - /// A custom animation targeting a property on an instance. + /// A custom animation targeting a property on an instance. /// - /// The containing the shadow to animate. /// /// The type to use for the public and /// properties. This can differ from to facilitate XAML parsing. /// /// The actual type of keyframe values in use. - public abstract class ShadowAnimation : Animation, IAttachedTimeline - where TShadow : AttachedShadowBase + public abstract class ShadowAnimation : Animation, IAttachedTimeline where TKeyFrame : unmanaged { /// - /// Gets or sets the linked instance to animate. + /// Gets or sets the linked instance to animate. /// - public TShadow? Target + public IAttachedShadow? Target { - get => (TShadow?)GetValue(TargetProperty); + get => (IAttachedShadow?)GetValue(TargetProperty); set => SetValue(TargetProperty, value); } @@ -39,8 +37,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public static readonly DependencyProperty TargetProperty = DependencyProperty.Register( nameof(Target), - typeof(TShadow), - typeof(ShadowAnimation), + typeof(IAttachedShadow), + typeof(ShadowAnimation), new PropertyMetadata(null)); /// @@ -63,7 +61,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations return ThrowArgumentNullException(); } - if (Target is TShadow allShadows) + if (Target is IAttachedShadow allShadows) { // in this case we'll animate all the shadows being used. foreach (var context in allShadows.GetElementContextEnumerable()) //// TODO: Find better way!!! diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/BlurRadiusDropShadowAnimation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/BlurRadiusDropShadowAnimation.cs new file mode 100644 index 000000000..4949ef568 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/BlurRadiusDropShadowAnimation.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A blur radius animation working on the composition layer. + /// + public sealed class BlurRadiusDropShadowAnimation : ShadowAnimation + { + /// + protected override string ExplicitTarget => nameof(DropShadow.BlurRadius); + + /// + protected override (double?, double?) GetParsedValues() + { + return (To, From); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/ColorDropShadowAnimation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/ColorDropShadowAnimation.cs new file mode 100644 index 000000000..68de1449a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/ColorDropShadowAnimation.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI; +using Windows.UI.Composition; + +#pragma warning disable CS0419 + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// A custom animation on a . + /// + public sealed class ColorDropShadowAnimation : ShadowAnimation + { + /// + protected override string ExplicitTarget => nameof(DropShadow.Color); + + /// + protected override (Color?, Color?) GetParsedValues() + { + return (To, From); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs index 4fcd561cc..0e8f586eb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OffsetDropShadowAnimation.cs @@ -4,14 +4,13 @@ using System.Numerics; using Windows.UI.Composition; -using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Animations { /// /// An offset animation working on the composition layer. /// - public sealed class OffsetDropShadowAnimation : ShadowAnimation + public sealed class OffsetDropShadowAnimation : ShadowAnimation { /// protected override string ExplicitTarget => nameof(DropShadow.Offset); diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OpacityDropShadowAnimation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OpacityDropShadowAnimation.cs new file mode 100644 index 000000000..61ff03941 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Shadows/OpacityDropShadowAnimation.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// An opacity animation working on the composition layer. + /// + public sealed class OpacityDropShadowAnimation : ShadowAnimation + { + /// + protected override string ExplicitTarget => nameof(DropShadow.Opacity); + + /// + protected override (double?, double?) GetParsedValues() + { + return (To, From); + } + } +} \ No newline at end of file From b34f441f6c93ff309e88ff6a2bad67eb98a27a9e Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 25 Aug 2021 11:52:52 -0700 Subject: [PATCH 290/299] Deprecate DropShadowPanel --- .../DropShadowPanel/DropShadowPanel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel/DropShadowPanel.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel/DropShadowPanel.cs index e2e031865..dc8d7cbfd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel/DropShadowPanel.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/DropShadowPanel/DropShadowPanel.cs @@ -17,6 +17,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// The control allows the creation of a DropShadow for any Xaml FrameworkElement in markup /// making it easier to add shadows to Xaml without having to directly drop down to Windows.UI.Composition APIs. /// + [Obsolete("DropShadowPanel will be removed in a future release, please use the AttachedDropShadow or AttachedCardShadow implementations instead.")] [TemplatePart(Name = PartShadow, Type = typeof(Border))] public partial class DropShadowPanel : ContentControl { From c9325796000cf59b6415626ca4d085d5997fbd6b Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Wed, 25 Aug 2021 16:16:21 -0700 Subject: [PATCH 291/299] Update String to Vector Unit Tests to address new scenarios added in Shadow PR --- .../Extensions/Test_StringExtensions.cs | 77 ++++++++++++++++--- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs index 8ed73b686..04b4ad5e3 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs @@ -59,7 +59,15 @@ namespace UnitTests.Extensions [TestCategory("StringExtensions")] [TestMethod] - [DataRow("")] + public void Test_StringExtensions_ToVector2_Zero() + { + var value = string.Empty.ToVector2(); + + Assert.AreEqual(Vector2.Zero, value); + } + + [TestCategory("StringExtensions")] + [TestMethod] [DataRow("Hello")] [DataRow("1, 2, 3")] [DataRow("<1, 2, 3")] @@ -117,11 +125,28 @@ namespace UnitTests.Extensions [TestCategory("StringExtensions")] [TestMethod] - [DataRow("")] + public void Test_StringExtensions_ToVector3_Zero() + { + var value = string.Empty.ToVector3(); + + Assert.AreEqual(Vector3.Zero, value); + } + + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_ToVector3_FromTwoValues() + { + var value = "4, 3".ToVector3(); + + Assert.AreEqual(new Vector3(new Vector2(4, 3), 0), value); + } + + [TestCategory("StringExtensions")] + [TestMethod] [DataRow("Hello")] - [DataRow("1, 2")] + [DataRow("1, ")] [DataRow("1, 2, 3, 99")] - [DataRow("<1, 2>")] + [DataRow("1, 2>")] [DataRow("<1, 2, 3")] [DataRow("<1, 2, 3, 4>")] [ExpectedException(typeof(FormatException))] @@ -179,12 +204,38 @@ namespace UnitTests.Extensions [TestCategory("StringExtensions")] [TestMethod] - [DataRow("")] + public void Test_StringExtensions_ToVector4_Zero() + { + var value = string.Empty.ToVector4(); + + Assert.AreEqual(Vector4.Zero, value); + } + + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_ToVector4_FromTwoValues() + { + var value = "4, 3".ToVector4(); + + Assert.AreEqual(new Vector4(new Vector2(4, 3), 0, 0), value); + } + + [TestCategory("StringExtensions")] + [TestMethod] + public void Test_StringExtensions_ToVector4_FromThreeValues() + { + var value = "4, 3, -2".ToVector4(); + + Assert.AreEqual(new Vector4(new Vector3(4, 3, -2), 0), value); + } + + [TestCategory("StringExtensions")] + [TestMethod] [DataRow("Hello")] - [DataRow("1, 2")] - [DataRow("1, 2, 3")] + [DataRow("1, 2, ")] + [DataRow("1, 2, 3, ")] [DataRow("1, 2, 3, 99, 100")] - [DataRow("<1, 2, 3>")] + [DataRow("<1, 2, 3")] [DataRow("<1, 2, 3, 4")] [DataRow("<1, 2, 3, 4, 5>")] [ExpectedException(typeof(FormatException))] @@ -223,7 +274,15 @@ namespace UnitTests.Extensions [TestCategory("StringExtensions")] [TestMethod] - [DataRow("")] + public void Test_StringExtensions_ToQuaternion_Zero() + { + var value = string.Empty.ToQuaternion(); + + Assert.AreEqual(default(Quaternion), value); + } + + [TestCategory("StringExtensions")] + [TestMethod] [DataRow("Hello")] [DataRow("1, 2")] [DataRow("1, 2, 3")] From 3d03ce7b21eab97dcf8eb0a82af3b5a98bba48ba Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Wed, 25 Aug 2021 17:24:55 -0700 Subject: [PATCH 292/299] dont use ApplicationView when XamlRoot is available --- .../RichSuggestBox/RichSuggestBoxPage.xaml.cs | 1 - .../RichSuggestBox/RichSuggestBox.Helpers.cs | 16 +++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RichSuggestBox/RichSuggestBoxPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RichSuggestBox/RichSuggestBoxPage.xaml.cs index f7a662d2a..20251c74c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RichSuggestBox/RichSuggestBoxPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RichSuggestBox/RichSuggestBoxPage.xaml.cs @@ -98,7 +98,6 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages public void OnXamlRendered(FrameworkElement control) { - if (this._rsb != null) { this._rsb.SuggestionChosen -= this.SuggestingBox_OnSuggestionChosen; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs index 3f01fe438..44b49d697 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs @@ -4,7 +4,6 @@ using System; using System.Linq; -using System.Threading; using Windows.Foundation; using Windows.Graphics.Display; using Windows.UI.Text; @@ -21,8 +20,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 0, double offsetY = 0) { - var toWindow = element.TransformToVisual(Window.Current.Content); - var windowBounds = ApplicationView.GetForCurrentView().VisibleBounds; + if (Window.Current == null) + { + return !ControlHelpers.IsXamlRootAvailable || element.XamlRoot.IsHostVisible; + } + + var toWindow = element.TransformToVisual(null); + var windowBounds = Window.Current.Bounds; var elementBounds = new Rect(offsetX, offsetY, element.ActualWidth, element.ActualHeight); elementBounds = toWindow.TransformBounds(elementBounds); elementBounds.X += windowBounds.X; @@ -35,8 +39,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private static bool IsElementInsideWindow(FrameworkElement element, double offsetX = 0, double offsetY = 0) { - var toWindow = element.TransformToVisual(Window.Current.Content); - var windowBounds = ApplicationView.GetForCurrentView().VisibleBounds; + var toWindow = element.TransformToVisual(null); + var windowBounds = ControlHelpers.IsXamlRootAvailable + ? element.XamlRoot.Size.ToRect() + : ApplicationView.GetForCurrentView().VisibleBounds; windowBounds = new Rect(0, 0, windowBounds.Width, windowBounds.Height); var elementBounds = new Rect(offsetX, offsetY, element.ActualWidth, element.ActualHeight); elementBounds = toWindow.TransformBounds(elementBounds); From 3401db0b5167d92f276aabeac8fa8ecc04ee2c18 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Thu, 26 Aug 2021 01:03:48 -0700 Subject: [PATCH 293/299] disable element on screen check --- .../RichSuggestBox/RichSuggestBox.Helpers.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs index 44b49d697..74a185d75 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs @@ -20,6 +20,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls { private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 0, double offsetY = 0) { + // DisplayInformation only works in UWP. No alternative to get DisplayInformation.ScreenHeightInRawPixels + // Tracking issues: + // https://github.com/microsoft/WindowsAppSDK/issues/114 + // https://github.com/microsoft/microsoft-ui-xaml/issues/4228 + // TODO: Remove when DisplayInformation.ScreenHeightInRawPixels alternative is available + return true; + +#pragma warning disable CS0162 // Unreachable code detected if (Window.Current == null) { return !ControlHelpers.IsXamlRootAvailable || element.XamlRoot.IsHostVisible; @@ -35,6 +43,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls var scaleFactor = displayInfo.RawPixelsPerViewPixel; var displayHeight = displayInfo.ScreenHeightInRawPixels; return elementBounds.Top * scaleFactor >= 0 && elementBounds.Bottom * scaleFactor <= displayHeight; +#pragma warning restore CS0162 // Unreachable code detected } private static bool IsElementInsideWindow(FrameworkElement element, double offsetX = 0, double offsetY = 0) From 4e327edd3b29e549bf376de406e6b3f1122e0237 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 26 Aug 2021 13:10:48 -0700 Subject: [PATCH 294/299] Still apply Screen size algorithm for UWP and annotate calculation --- .../RichSuggestBox/RichSuggestBox.Helpers.cs | 42 +++++++++++++------ .../Extensions/RectExtensions.cs | 14 +++++++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs index 74a185d75..ff89f727b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs @@ -21,41 +21,57 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 0, double offsetY = 0) { // DisplayInformation only works in UWP. No alternative to get DisplayInformation.ScreenHeightInRawPixels + // Or Window position in Window.Current.Bounds // Tracking issues: // https://github.com/microsoft/WindowsAppSDK/issues/114 // https://github.com/microsoft/microsoft-ui-xaml/issues/4228 // TODO: Remove when DisplayInformation.ScreenHeightInRawPixels alternative is available - return true; - -#pragma warning disable CS0162 // Unreachable code detected if (Window.Current == null) { - return !ControlHelpers.IsXamlRootAvailable || element.XamlRoot.IsHostVisible; + return true; } - var toWindow = element.TransformToVisual(null); + // Get bounds of element from root of tree + var elementBounds = element.CoordinatesFrom(null).ToRect(element.ActualWidth, element.ActualHeight); + + // Apply offset + elementBounds.X += offsetX; + elementBounds.Y += offsetY; + + // Get Window position var windowBounds = Window.Current.Bounds; - var elementBounds = new Rect(offsetX, offsetY, element.ActualWidth, element.ActualHeight); - elementBounds = toWindow.TransformBounds(elementBounds); + + // Offset Element within Window on Screen elementBounds.X += windowBounds.X; elementBounds.Y += windowBounds.Y; + + // Get Screen DPI info var displayInfo = DisplayInformation.GetForCurrentView(); var scaleFactor = displayInfo.RawPixelsPerViewPixel; var displayHeight = displayInfo.ScreenHeightInRawPixels; + + // Check if top/bottom are within confines of screen return elementBounds.Top * scaleFactor >= 0 && elementBounds.Bottom * scaleFactor <= displayHeight; -#pragma warning restore CS0162 // Unreachable code detected } private static bool IsElementInsideWindow(FrameworkElement element, double offsetX = 0, double offsetY = 0) { - var toWindow = element.TransformToVisual(null); + // Get bounds of element from root of tree + var elementBounds = element.CoordinatesFrom(null).ToRect(element.ActualWidth, element.ActualHeight); + + // Apply offset + elementBounds.X += offsetX; + elementBounds.Y += offsetY; + + // Get size of window itself var windowBounds = ControlHelpers.IsXamlRootAvailable ? element.XamlRoot.Size.ToRect() - : ApplicationView.GetForCurrentView().VisibleBounds; - windowBounds = new Rect(0, 0, windowBounds.Width, windowBounds.Height); - var elementBounds = new Rect(offsetX, offsetY, element.ActualWidth, element.ActualHeight); - elementBounds = toWindow.TransformBounds(elementBounds); + : ApplicationView.GetForCurrentView().VisibleBounds.ToSize().ToRect(); // Normalize + + // Calculate if there's an intersection elementBounds.Intersect(windowBounds); + + // See if we are still fully visible within the Window return elementBounds.Height >= element.ActualHeight; } diff --git a/Microsoft.Toolkit.Uwp/Extensions/RectExtensions.cs b/Microsoft.Toolkit.Uwp/Extensions/RectExtensions.cs index 367bfa3e1..6e9a1e66e 100644 --- a/Microsoft.Toolkit.Uwp/Extensions/RectExtensions.cs +++ b/Microsoft.Toolkit.Uwp/Extensions/RectExtensions.cs @@ -4,7 +4,9 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using Windows.Foundation; using Rect = Windows.Foundation.Rect; +using Size = Windows.Foundation.Size; namespace Microsoft.Toolkit.Uwp { @@ -33,5 +35,17 @@ namespace Microsoft.Toolkit.Uwp (rect1.Top <= rect2.Bottom) && (rect1.Bottom >= rect2.Top); } + + /// + /// Creates a new of the specified 's width and height. + /// + /// Rectangle to size. + /// Size of rectangle. + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size ToSize(this Rect rect) + { + return new Size(rect.Width, rect.Height); + } } } \ No newline at end of file From 46bd8622f3d9868db8b46d07b79b050f7b304926 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 26 Aug 2021 13:51:12 -0700 Subject: [PATCH 295/299] Provide better guards for UWP --- .../RichSuggestBox/RichSuggestBox.Helpers.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs index ff89f727b..0357e261b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Helpers.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using Windows.Foundation; using Windows.Graphics.Display; +using Windows.UI.Core; using Windows.UI.Text; using Windows.UI.ViewManagement; using Windows.UI.Xaml; @@ -26,7 +27,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls // https://github.com/microsoft/WindowsAppSDK/issues/114 // https://github.com/microsoft/microsoft-ui-xaml/issues/4228 // TODO: Remove when DisplayInformation.ScreenHeightInRawPixels alternative is available - if (Window.Current == null) + if (CoreWindow.GetForCurrentThread() == null) { return true; } @@ -64,7 +65,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls elementBounds.Y += offsetY; // Get size of window itself - var windowBounds = ControlHelpers.IsXamlRootAvailable + var windowBounds = ControlHelpers.IsXamlRootAvailable && element.XamlRoot != null ? element.XamlRoot.Size.ToRect() : ApplicationView.GetForCurrentView().VisibleBounds.ToSize().ToRect(); // Normalize From f3b0e5c40e9e2b7f3f2e19b2951b68d4582906a1 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 27 Aug 2021 12:34:56 -0700 Subject: [PATCH 296/299] Update Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowCompositionXaml.bind --- .../SamplePages/Shadows/AttachedShadowCompositionXaml.bind | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowCompositionXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowCompositionXaml.bind index 1d5fd2c52..b594d2509 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowCompositionXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowCompositionXaml.bind @@ -6,7 +6,6 @@ xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" mc:Ignorable="d"> - From 01c9fa37176dbf6b4ec151579a1a47710f8e7453 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 27 Aug 2021 14:05:28 -0700 Subject: [PATCH 297/299] Update Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs --- Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs b/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs index 2c4dd68e6..2aecfcbba 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs @@ -35,7 +35,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Common { case 1: var vectorValue = float.Parse(vectorString); - return new Vector3(vectorValue, vectorValue, vectorValue); + return new Vector3(vectorValue); case 2: var xValue = float.Parse(vectorTokens[0]); var yValue = float.Parse(vectorTokens[1]); From 2404be95039ddd5bde961c2f1a61d509b6a04db8 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 27 Aug 2021 14:36:19 -0700 Subject: [PATCH 298/299] Make field initialization static Co-authored-by: Sergio Pedri --- Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index bcd2d405c..5c2e5a0d5 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -22,12 +22,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// Gets a value indicating whether or not Composition's VisualSurface is supported. /// - protected static readonly bool SupportsCompositionVisualSurface; - - static AttachedShadowBase() - { - SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); // Note: This is 1903 (18362) min - } + protected static readonly bool SupportsCompositionVisualSurface = ApiInformation.IsTypePresent(typeof(CompositionVisualSurface).FullName); /// /// The for . From e418928cd9f38177d45728d659109b5a7836f321 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 27 Aug 2021 14:52:40 -0700 Subject: [PATCH 299/299] Clean-up Shadow internal part usage based on @Sergio0694 PR comments --- .../Abstract/ShadowAnimation{TValue,TKeyFrame}.cs | 2 +- .../Shadows/AttachedCardShadow.cs | 6 +++--- Microsoft.Toolkit.Uwp.UI/Properties/AssemblyInfo.cs | 7 +++++++ .../Shadows/AttachedDropShadow.cs | 12 ++++-------- .../Shadows/AttachedShadowBase.cs | 2 +- .../Shadows/AttachedShadowElementContext.cs | 10 +++++----- Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs | 2 +- .../Shadows}/TypedResourceKey.cs | 4 ++-- 8 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI/Properties/AssemblyInfo.cs rename {Microsoft.Toolkit => Microsoft.Toolkit.Uwp.UI/Shadows}/TypedResourceKey.cs (93%) diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs index b65654bd4..c7ab02d2a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ShadowAnimation{TValue,TKeyFrame}.cs @@ -64,7 +64,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations if (Target is IAttachedShadow allShadows) { // in this case we'll animate all the shadows being used. - foreach (var context in allShadows.GetElementContextEnumerable()) //// TODO: Find better way!!! + foreach (var context in allShadows.EnumerateElementContexts()) //// TODO: Find better way!!! { NormalizedKeyFrameAnimationBuilder.Composition keyFrameBuilder = new( explicitTarget, diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs index e0643ae08..2373f7ada 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -17,7 +17,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media /// /// This shadow will not work on which is directly clipping to its bounds (e.g. a using a ). An extra can instead be applied around the clipped border with the Shadow to create the desired effect. Most existing controls due to how they're templated will not encounter this behavior or require this workaround. /// - public class AttachedCardShadow : AttachedShadowBase + public sealed class AttachedCardShadow : AttachedShadowBase { private const float MaxBlurRadius = 72; private static readonly TypedResourceKey ClipResourceKey = "Clip"; @@ -52,7 +52,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media public override bool IsSupported => SupportsCompositionVisualSurface; /// - protected override bool SupportsOnSizeChangedEvent => true; + protected internal override bool SupportsOnSizeChangedEvent => true; /// protected override void OnPropertyChanged(AttachedShadowElementContext context, DependencyProperty property, object oldValue, object newValue) @@ -145,7 +145,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Media } /// - protected override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize) + protected internal override void OnSizeChanged(AttachedShadowElementContext context, Size newSize, Size previousSize) { var sizeAsVec2 = newSize.ToVector2(); diff --git a/Microsoft.Toolkit.Uwp.UI/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6cf4a5a1f --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Toolkit.Uwp.UI.Media")] diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs index d472a6a1d..070603cb7 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs @@ -2,12 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Text; -using System.Threading.Tasks; using Windows.Foundation; using Windows.UI; using Windows.UI.Composition; @@ -21,7 +17,7 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// A helper to add a composition based drop shadow to a . /// - public class AttachedDropShadow : AttachedShadowBase + public sealed class AttachedDropShadow : AttachedShadowBase { private const float MaxBlurRadius = 72; @@ -121,7 +117,7 @@ namespace Microsoft.Toolkit.Uwp.UI // Need to remove all old children from previous container if it's changed if (prevContainer != null && prevContainer != shadow._container) { - foreach (var context in shadow.GetElementContextEnumerable()) + foreach (var context in shadow.EnumerateElementContexts()) { if (context.IsInitialized && prevContainer.Children.Contains(context.SpriteVisual)) @@ -132,7 +128,7 @@ namespace Microsoft.Toolkit.Uwp.UI } // Make sure all child shadows are hooked into container - foreach (var context in shadow.GetElementContextEnumerable()) + foreach (var context in shadow.EnumerateElementContexts()) { if (context.IsInitialized) { @@ -152,7 +148,7 @@ namespace Microsoft.Toolkit.Uwp.UI { // Don't use sender or 'e' here as related to container element not // element for shadow, grab values off context. (Also may be null from internal call.) - foreach (var context in GetElementContextEnumerable()) + foreach (var context in EnumerateElementContexts()) { if (context.IsInitialized) { diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs index 5c2e5a0d5..b78e2a771 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -172,7 +172,7 @@ namespace Microsoft.Toolkit.Uwp.UI } /// - public IEnumerable GetElementContextEnumerable() + public IEnumerable EnumerateElementContexts() { foreach (var kvp in ShadowElementContextTable) { diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs index 07116f4c3..1d8803d23 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -268,35 +268,35 @@ namespace Microsoft.Toolkit.Uwp.UI /// /// The type of the resource being added. /// The resource that was added - public T AddResource(TypedResourceKey key, T resource) => AddResource(key.Key, resource); + internal T AddResource(TypedResourceKey key, T resource) => AddResource(key.Key, resource); /// /// Retrieves a resource with the specified key and type if it exists /// /// The type of the resource being retrieved. /// True if the resource exists, false otherwise - public bool TryGetResource(TypedResourceKey key, out T resource) => TryGetResource(key.Key, out resource); + internal bool TryGetResource(TypedResourceKey key, out T resource) => TryGetResource(key.Key, out resource); /// /// Retries a resource with the specified key and type /// /// The type of the resource being retrieved. /// The resource if it exists or a default value. - public T GetResource(TypedResourceKey key) => GetResource(key.Key); + internal T GetResource(TypedResourceKey key) => GetResource(key.Key); /// /// Removes an existing resource with the specified key and type /// /// The type of the resource being removed. /// The resource that was removed, if any - public T RemoveResource(TypedResourceKey key) => RemoveResource(key.Key); + internal T RemoveResource(TypedResourceKey key) => RemoveResource(key.Key); /// /// Removes an existing resource with the specified key and type, and disposes it /// /// The type of the resource being removed. /// The resource that was removed, if any - public T RemoveAndDisposeResource(TypedResourceKey key) + internal T RemoveAndDisposeResource(TypedResourceKey key) where T : IDisposable => RemoveAndDisposeResource(key.Key); /// diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs index afababd92..74eee795b 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs @@ -45,6 +45,6 @@ namespace Microsoft.Toolkit.Uwp.UI /// Gets an enumeration over the current list of of elements using this shared shadow definition. /// /// Enumeration of objects. - IEnumerable GetElementContextEnumerable(); + IEnumerable EnumerateElementContexts(); } } diff --git a/Microsoft.Toolkit/TypedResourceKey.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/TypedResourceKey.cs similarity index 93% rename from Microsoft.Toolkit/TypedResourceKey.cs rename to Microsoft.Toolkit.Uwp.UI/Shadows/TypedResourceKey.cs index 9e46ed107..43c7393d8 100644 --- a/Microsoft.Toolkit/TypedResourceKey.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/TypedResourceKey.cs @@ -4,13 +4,13 @@ using System; -namespace Microsoft.Toolkit +namespace Microsoft.Toolkit.Uwp.UI { /// /// A generic class that can be used to retrieve keyed resources of the specified type. /// /// The of resource the will retrieve. - public class TypedResourceKey + internal sealed class TypedResourceKey { /// /// Initializes a new instance of the class with the specified key.