From 54208748d6c7e7c1498b92cddfa90378f2b8e133 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Thu, 18 Mar 2021 18:36:55 -0700 Subject: [PATCH 001/200] 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 00000000000..75c89474c56 --- /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 8482a04f0e60c3db9826155648bd22a44c9c3969 Mon Sep 17 00:00:00 2001 From: Rosario Pulella Date: Wed, 12 May 2021 12:43:36 -0400 Subject: [PATCH 002/200] Paramitize pageSize and page count for unit tests --- .../Test_IncrementalLoadingCollection.cs | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/UnitTests/UnitTests.UWP/UI/Collection/Test_IncrementalLoadingCollection.cs b/UnitTests/UnitTests.UWP/UI/Collection/Test_IncrementalLoadingCollection.cs index d845e1d9c7a..f6b213ba1c6 100644 --- a/UnitTests/UnitTests.UWP/UI/Collection/Test_IncrementalLoadingCollection.cs +++ b/UnitTests/UnitTests.UWP/UI/Collection/Test_IncrementalLoadingCollection.cs @@ -15,9 +15,6 @@ namespace UnitTests.UI [TestClass] public class Test_IncrementalLoadingCollection { - private const int PageSize = 20; - private const int Pages = 5; - private static readonly DataSource.PageOperation[] FailPassSequence = new DataSource.PageOperation[] { @@ -28,35 +25,36 @@ private static readonly DataSource.PageOperation[] FailPassSequence DataSource.ThrowException, DataSource.PassThrough, }; - private static readonly int[] AllData - = Enumerable.Range(0, Pages * PageSize).ToArray(); - - [DataRow] - [DataRow(2500, 1000, 1000, 1000, 1000)] [TestMethod] - public async Task Requests(params int[] pageDelays) + [DataRow(20, 5)] + [DataRow(5, 5)] + [DataRow(20, 5, 2500, 1000, 1000, 1000, 1000)] + public async Task Requests(int pageSize, int pages, params int[] pageDelays) { - var source = new DataSource(AllData, pageDelays.Select(DataSource.MakeDelayOp)); - var collection = new IncrementalLoadingCollection, int>(source, PageSize); + var allData = Enumerable.Range(0, pages * pageSize).ToArray(); + var source = new DataSource(allData, pageDelays.Select(DataSource.MakeDelayOp)); + var collection = new IncrementalLoadingCollection, int>(source, pageSize); - for (int pageNum = 1; pageNum <= Pages; pageNum++) + for (var pageNum = 1; pageNum <= pages; pageNum++) { await collection.LoadMoreItemsAsync(0); - CollectionAssert.AreEqual(Enumerable.Range(0, PageSize * pageNum).ToArray(), collection); + CollectionAssert.AreEqual(Enumerable.Range(0, pageSize * pageNum).ToArray(), collection); } } - [DataRow] - [DataRow(2500, 1000, 1000, 1000, 1000)] [TestMethod] - public async Task RequestsAsync(params int[] pageDelays) + [DataRow(20, 5)] + [DataRow(5, 5)] + [DataRow(20, 5, 2500, 1000, 1000, 1000, 1000)] + public async Task RequestsAsync(int pageSize, int pages, params int[] pageDelays) { - var source = new DataSource(AllData, pageDelays.Select(DataSource.MakeDelayOp)); - var collection = new IncrementalLoadingCollection, int>(source, PageSize); + var allData = Enumerable.Range(0, pages * pageSize).ToArray(); + var source = new DataSource(allData, pageDelays.Select(DataSource.MakeDelayOp)); + var collection = new IncrementalLoadingCollection, int>(source, pageSize); var requests = new List(); - for (int pageNum = 1; pageNum <= Pages; pageNum++) + for (var pageNum = 1; pageNum <= pages; pageNum++) { requests.Add(collection.LoadMoreItemsAsync(0).AsTask() .ContinueWith(t => Assert.IsTrue(t.IsCompletedSuccessfully))); @@ -64,14 +62,17 @@ public async Task RequestsAsync(params int[] pageDelays) await Task.WhenAll(requests); - CollectionAssert.AreEqual(AllData, collection); + CollectionAssert.AreEqual(allData, collection); } [TestMethod] - public async Task FirstRequestFails() + [DataRow(5, 5)] + [DataRow(20, 5)] + public async Task FirstRequestFails(int pageSize, int pages) { - var source = new DataSource(AllData, DataSource.ThrowException); - var collection = new IncrementalLoadingCollection, int>(source, PageSize); + var allData = Enumerable.Range(0, pages * pageSize).ToArray(); + var source = new DataSource(allData, DataSource.ThrowException); + var collection = new IncrementalLoadingCollection, int>(source, pageSize); await Assert.ThrowsExceptionAsync(collection.LoadMoreItemsAsync(0).AsTask); @@ -79,7 +80,7 @@ public async Task FirstRequestFails() var requests = new List(); - for (int pageNum = 1; pageNum <= Pages; pageNum++) + for (var pageNum = 1; pageNum <= pages; pageNum++) { requests.Add(collection.LoadMoreItemsAsync(0).AsTask() .ContinueWith(t => Assert.IsTrue(t.IsCompletedSuccessfully))); @@ -87,17 +88,20 @@ public async Task FirstRequestFails() await Task.WhenAll(requests); - CollectionAssert.AreEqual(AllData, collection); + CollectionAssert.AreEqual(allData, collection); } [TestMethod] - public async Task EveryOtherRequestFails() + [DataRow(5, 5)] + [DataRow(20, 5)] + public async Task EveryOtherRequestFails(int pageSize, int pages) { - var source = new DataSource(AllData, FailPassSequence); - var collection = new IncrementalLoadingCollection, int>(source, PageSize); + var allData = Enumerable.Range(0, pages * pageSize).ToArray(); + var source = new DataSource(allData, FailPassSequence); + var collection = new IncrementalLoadingCollection, int>(source, pageSize); var willFail = true; - for (int submitedRequests = 0; submitedRequests < Pages * 2; submitedRequests++) + for (var submitedRequests = 0; submitedRequests < pages * 2; submitedRequests++) { if (willFail) { @@ -111,19 +115,22 @@ public async Task EveryOtherRequestFails() willFail = !willFail; } - CollectionAssert.AreEqual(AllData, collection); + CollectionAssert.AreEqual(allData, collection); } [TestMethod] - public async Task EveryOtherRequestFailsAsync() + [DataRow(5, 5)] + [DataRow(20, 5)] + public async Task EveryOtherRequestFailsAsync(int pageSize, int pages) { - var source = new DataSource(AllData, FailPassSequence); - var collection = new IncrementalLoadingCollection, int>(source, PageSize); + var allData = Enumerable.Range(0, pages * pageSize).ToArray(); + var source = new DataSource(allData, FailPassSequence); + var collection = new IncrementalLoadingCollection, int>(source, pageSize); var requests = new List(); var willFail = true; - for (int submitedRequests = 0; submitedRequests < Pages * 2; submitedRequests++) + for (var submitedRequests = 0; submitedRequests < pages * 2; submitedRequests++) { if (willFail) { @@ -139,7 +146,7 @@ public async Task EveryOtherRequestFailsAsync() await Task.WhenAll(requests); - CollectionAssert.AreEqual(AllData, collection); + CollectionAssert.AreEqual(allData, collection); } } } \ No newline at end of file From f6c65a535e02ed3cf1d94b51b74548fbbfa688d7 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Thu, 8 Jul 2021 21:31:56 +0200 Subject: [PATCH 003/200] Fix TokenizingTextBox header to behave like normal headers --- .../TokenizingTextBox/TokenizingTextBox.xaml | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml index 237969fc387..203f629c0ff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml @@ -65,18 +65,34 @@ + + + + + + + FooterTransitions="{TemplateBinding FooterTransitions}"/> @@ -106,14 +119,14 @@ + Storyboard.TargetProperty="Background"> + Value="{ThemeResource TextControlBackgroundDisabled}" /> + Storyboard.TargetProperty="BorderBrush"> + Value="{ThemeResource TextControlBorderBrushDisabled}" /> @@ -122,14 +135,14 @@ + Storyboard.TargetProperty="BorderBrush"> + Value="{ThemeResource TextControlBorderBrushPointerOver}" /> + Storyboard.TargetProperty="Background"> + Value="{ThemeResource TextControlBackgroundPointerOver}" /> From 684546419d204a453a9f528a38972b7c745c9010 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Thu, 8 Jul 2021 21:57:15 +0200 Subject: [PATCH 004/200] Fix formatting --- .../TokenizingTextBox/TokenizingTextBox.xaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml index 203f629c0ff..d1b006e7b06 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml @@ -119,14 +119,14 @@ + Storyboard.TargetProperty="Background"> + Value="{ThemeResource TextControlBackgroundDisabled}" /> + Storyboard.TargetProperty="BorderBrush"> + Value="{ThemeResource TextControlBorderBrushDisabled}" /> @@ -135,14 +135,14 @@ + Storyboard.TargetProperty="BorderBrush"> + Value="{ThemeResource TextControlBorderBrushPointerOver}" /> + Storyboard.TargetProperty="Background"> + Value="{ThemeResource TextControlBackgroundPointerOver}" /> From 7f040274215b0c680824d943dbaa28a87ebfb0e4 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Thu, 8 Jul 2021 21:58:04 +0200 Subject: [PATCH 005/200] Fix formatting the second --- .../TokenizingTextBox/TokenizingTextBox.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml index d1b006e7b06..7b689061d63 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml @@ -111,7 +111,7 @@ Margin="{StaticResource TokenizingTextBoxPresenterMargin}" Footer="{TemplateBinding Footer}" FooterTemplate="{TemplateBinding FooterTemplate}" - FooterTransitions="{TemplateBinding FooterTransitions}"/> + FooterTransitions="{TemplateBinding FooterTransitions}" /> From 90e8a34aaba519a2aee86cc50284d30b47d0c096 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 26 Jul 2021 22:50:01 -0400 Subject: [PATCH 006/200] Ensure only visible tabs are selected --- .../ColorPicker/ColorPicker.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs index 12a20c8f4cc..99523f15ecb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs @@ -40,6 +40,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls [TemplatePart(Name = nameof(ColorPicker.CheckeredBackground8Border), Type = typeof(Border))] [TemplatePart(Name = nameof(ColorPicker.CheckeredBackground9Border), Type = typeof(Border))] [TemplatePart(Name = nameof(ColorPicker.CheckeredBackground10Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(ColorPicker.ColorPanelSelector), Type = typeof(ListBox))] [TemplatePart(Name = nameof(ColorPicker.ColorSpectrumControl), Type = typeof(ColorSpectrum))] [TemplatePart(Name = nameof(ColorPicker.ColorSpectrumAlphaSlider), Type = typeof(ColorPickerSlider))] [TemplatePart(Name = nameof(ColorPicker.ColorSpectrumThirdDimensionSlider), Type = typeof(ColorPickerSlider))] @@ -78,6 +79,7 @@ public partial class ColorPicker : Microsoft.UI.Xaml.Controls.ColorPicker private Color? updatedRgbColor = null; private DispatcherQueueTimer dispatcherQueueTimer = null; + private ListBox ColorPanelSelector; private ColorSpectrum ColorSpectrumControl; private ColorPickerSlider ColorSpectrumAlphaSlider; private ColorPickerSlider ColorSpectrumThirdDimensionSlider; @@ -177,6 +179,8 @@ protected override void OnApplyTemplate() // We need to disconnect old events first this.ConnectEvents(false); + this.ColorPanelSelector = this.GetTemplateChild(nameof(ColorPanelSelector)); + this.ColorSpectrumControl = this.GetTemplateChild(nameof(ColorSpectrumControl)); this.ColorSpectrumAlphaSlider = this.GetTemplateChild(nameof(ColorSpectrumAlphaSlider)); this.ColorSpectrumThirdDimensionSlider = this.GetTemplateChild(nameof(ColorSpectrumThirdDimensionSlider)); @@ -216,6 +220,7 @@ protected override void OnApplyTemplate() base.OnApplyTemplate(); this.UpdateVisualState(false); + this.ValidateSelectedPanel(); this.isInitialized = true; this.SetActiveColorRepresentation(ColorRepresentation.Rgba); this.UpdateColorControlValues(); // TODO: This will also connect events after, can we optimize vs. doing it twice with the ConnectEvents above? @@ -1065,6 +1070,48 @@ private void SetDefaultPalette() return; } + /// + /// Validates and updates the current 'tab' or 'panel' selection. + /// If the currently selected tab is collapsed, the next visible tab will be selected. + /// + private void ValidateSelectedPanel() + { + object selectedItem = null; + + if (this.ColorPanelSelector != null) + { + if (this.ColorPanelSelector.SelectedItem == null && + this.ColorPanelSelector.Items.Count > 0) + { + // As a failsafe, forcefully select the first item + selectedItem = this.ColorPanelSelector.Items[0]; + } + else + { + selectedItem = this.ColorPanelSelector.SelectedItem; + } + + if (selectedItem is UIElement selectedElement && + selectedElement.Visibility == Visibility.Collapsed) + { + // Select the first visible item instead + foreach (object item in this.ColorPanelSelector.Items) + { + if (item is UIElement element && + element.Visibility == Visibility.Visible) + { + selectedItem = item; + break; + } + } + } + + this.ColorPanelSelector.SelectedItem = selectedItem; + } + + return; + } + /*************************************************************************************** * * Color Update Timer From f448ee31c6292bf8a96923c5cdd0f91c8e7aa6bc Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 26 Jul 2021 22:50:46 -0400 Subject: [PATCH 007/200] Add additional color picker samples showing only a palette --- .../ColorPicker/ColorPickerButtonXaml.bind | 38 +++++++++++++++++++ .../ColorPicker/ColorPickerXaml.bind | 25 +++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind index 8a84e01b04e..654d66c346a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind @@ -131,5 +131,43 @@ + + + + + Ring-shaped spectrum + Alpha channel enabled + Only Color Palette Shown + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind index 59d3454d6c0..c3116470e48 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind @@ -60,7 +60,7 @@ Alpha channel enabled - - + + + + Ring-shaped spectrum + Alpha channel enabled + Only Color Palette Shown + + + \ No newline at end of file From bc05c86bf0bb708cfc60f131b3b9c442492780b0 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 26 Jul 2021 23:30:36 -0400 Subject: [PATCH 008/200] Make tabs automatically size uniformly to width This improves the design when some tabs are hidden --- .../ColorPicker/ColorPicker.xaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.xaml index 370be9af979..3641acb2e3c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.xaml @@ -69,12 +69,13 @@ CornerRadius="4" /> - + - - - From e23a8952a98de3b984e2c22acf616cc9479c30ec Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 26 Jul 2021 23:32:40 -0400 Subject: [PATCH 009/200] Set slider DefaultForeground value in style This fixes potential issues with shared instances of the brush --- .../ColorPicker/ColorPickerSlider.Properties.cs | 2 +- .../ColorPicker/ColorPickerSlider.xaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.Properties.cs index bf99d884dfd..2a0471a420a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.Properties.cs @@ -104,7 +104,7 @@ public ColorRepresentation ColorRepresentation typeof(Brush), typeof(ColorPickerSlider), new PropertyMetadata( - new SolidColorBrush(Colors.Gray), + null, (s, e) => (s as ColorPickerSlider)?.OnDependencyPropertyChanged(s, e))); /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.xaml index 439e7040f2b..51027d2d0be 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPickerSlider.xaml @@ -9,6 +9,7 @@ TargetType="controls:ColorPickerSlider"> + From dde93f33c8d68f1b4dcd2f869836caf01155ad6d Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 26 Jul 2021 23:55:19 -0400 Subject: [PATCH 010/200] Add central OnDependencyPropertyChanged method in ColorPicker --- .../ColorPicker/ColorPicker.Properties.cs | 16 +++- .../ColorPicker/ColorPicker.cs | 94 +++++++++---------- 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.Properties.cs index 1c6e2cdd469..641e83131f5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.Properties.cs @@ -20,7 +20,9 @@ public partial class ColorPicker nameof(CustomPaletteColors), typeof(ObservableCollection), typeof(ColorPicker), - new PropertyMetadata(null)); + new PropertyMetadata( + null, + (s, e) => (s as ColorPicker)?.OnDependencyPropertyChanged(s, e))); /// /// Gets the list of custom palette colors. @@ -38,7 +40,9 @@ public ObservableCollection CustomPaletteColors nameof(CustomPaletteColumnCount), typeof(int), typeof(ColorPicker), - new PropertyMetadata(4)); + new PropertyMetadata( + 4, + (s, e) => (s as ColorPicker)?.OnDependencyPropertyChanged(s, e))); /// /// Gets or sets the number of colors in each row (section) of the custom color palette. @@ -64,7 +68,9 @@ public int CustomPaletteColumnCount nameof(CustomPalette), typeof(IColorPalette), typeof(ColorPicker), - new PropertyMetadata(null)); + new PropertyMetadata( + null, + (s, e) => (s as ColorPicker)?.OnDependencyPropertyChanged(s, e))); /// /// Gets or sets the custom color palette. @@ -91,7 +97,9 @@ public IColorPalette CustomPalette nameof(IsColorPaletteVisible), typeof(bool), typeof(ColorPicker), - new PropertyMetadata(true)); + new PropertyMetadata( + true, + (s, e) => (s as ColorPicker)?.OnDependencyPropertyChanged(s, e))); /// /// Gets or sets a value indicating whether the color palette is visible. diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs index 99523f15ecb..7a22dbc8f70 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs @@ -66,8 +66,6 @@ public partial class ColorPicker : Microsoft.UI.Xaml.Controls.ColorPicker private const int ColorUpdateInterval = 30; // Milliseconds private long tokenColor; - private long tokenCustomPalette; - private long tokenIsColorPaletteVisible; private bool callbacksConnected = false; private bool eventsConnected = false; @@ -252,23 +250,19 @@ private T GetTemplateChild(string childName, bool isRequired = false) /// True to connect callbacks, otherwise false. private void ConnectCallbacks(bool connected) { - if ((connected == true) && - (this.callbacksConnected == false)) + if (connected == true && + this.callbacksConnected == false) { // Add callbacks for dependency properties - this.tokenColor = this.RegisterPropertyChangedCallback(ColorProperty, OnColorChanged); - this.tokenCustomPalette = this.RegisterPropertyChangedCallback(CustomPaletteProperty, OnCustomPaletteChanged); - this.tokenIsColorPaletteVisible = this.RegisterPropertyChangedCallback(IsColorPaletteVisibleProperty, OnIsColorPaletteVisibleChanged); + this.tokenColor = this.RegisterPropertyChangedCallback(ColorProperty, OnColorChanged); this.callbacksConnected = true; } - else if ((connected == false) && - (this.callbacksConnected == true)) + else if (connected == false && + this.callbacksConnected == true) { // Remove callbacks for dependency properties - this.UnregisterPropertyChangedCallback(ColorProperty, this.tokenColor); - this.UnregisterPropertyChangedCallback(CustomPaletteProperty, this.tokenCustomPalette); - this.UnregisterPropertyChangedCallback(IsColorPaletteVisibleProperty, this.tokenIsColorPaletteVisible); + this.UnregisterPropertyChangedCallback(ColorProperty, this.tokenColor); this.callbacksConnected = false; } @@ -282,8 +276,8 @@ private void ConnectCallbacks(bool connected) /// True to connect event handlers, otherwise false. private void ConnectEvents(bool connected) { - if ((connected == true) && - (this.eventsConnected == false)) + if (connected == true && + this.eventsConnected == false) { // Add all events if (this.ColorSpectrumControl != null) { this.ColorSpectrumControl.ColorChanged += ColorSpectrum_ColorChanged; } @@ -336,8 +330,8 @@ private void ConnectEvents(bool connected) this.eventsConnected = true; } - else if ((connected == false) && - (this.eventsConnected == true)) + else if (connected == false && + this.eventsConnected == true) { // Remove all events if (this.ColorSpectrumControl != null) { this.ColorSpectrumControl.ColorChanged -= ColorSpectrum_ColorChanged; } @@ -1112,6 +1106,41 @@ private void ValidateSelectedPanel() return; } + private void OnDependencyPropertyChanged(object sender, DependencyPropertyChangedEventArgs args) + { + DependencyObject senderControl = sender as DependencyObject; + + /* Note: ColorProperty is defined in the base class and cannot be used here + * See the OnColorChanged callback below + */ + + if (object.ReferenceEquals(args.Property, CustomPaletteProperty)) + { + IColorPalette palette = this.CustomPalette; + + if (palette != null) + { + this.CustomPaletteColumnCount = palette.ColorCount; + this.CustomPaletteColors.Clear(); + + for (int shadeIndex = 0; shadeIndex < palette.ShadeCount; shadeIndex++) + { + for (int colorIndex = 0; colorIndex < palette.ColorCount; colorIndex++) + { + this.CustomPaletteColors.Add(palette.GetColor(colorIndex, shadeIndex)); + } + } + } + } + else if (object.ReferenceEquals(args.Property, IsColorPaletteVisibleProperty)) + { + this.UpdateVisualState(false); + this.ValidateSelectedPanel(); + } + + return; + } + /*************************************************************************************** * * Color Update Timer @@ -1194,39 +1223,6 @@ private void OnColorChanged(DependencyObject d, DependencyProperty e) return; } - /// - /// Callback for when the dependency property value changes. - /// - private void OnCustomPaletteChanged(DependencyObject d, DependencyProperty e) - { - IColorPalette palette = this.CustomPalette; - - if (palette != null) - { - this.CustomPaletteColumnCount = palette.ColorCount; - this.CustomPaletteColors.Clear(); - - for (int shadeIndex = 0; shadeIndex < palette.ShadeCount; shadeIndex++) - { - for (int colorIndex = 0; colorIndex < palette.ColorCount; colorIndex++) - { - this.CustomPaletteColors.Add(palette.GetColor(colorIndex, shadeIndex)); - } - } - } - - return; - } - - /// - /// Callback for when the dependency property value changes. - /// - private void OnIsColorPaletteVisibleChanged(DependencyObject d, DependencyProperty e) - { - this.UpdateVisualState(false); - return; - } - /*************************************************************************************** * * Event Handling From 5540ecef2e35d0d9440964ec3e7aef55e11ada70 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Tue, 27 Jul 2021 20:49:21 +0200 Subject: [PATCH 011/200] Add footer templatepart --- .../TokenizingTextBox/TokenizingTextBox.xaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml index 7b689061d63..04661d9d1e6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.xaml @@ -68,6 +68,7 @@ + + Margin="{StaticResource TokenizingTextBoxPresenterMargin}"/> + + From 5564df3d61b1a9ffdafa2f0c74d69941640d8f35 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 9 Aug 2021 15:21:05 -0700 Subject: [PATCH 012/200] Added TryRenameItemAsync and TryDeleteItemAsync to IFileStorageHelper --- ...pplicationDataStorageHelper.CacheFolder.cs | 23 ++++++--- .../ApplicationDataStorageHelper.cs | 48 ++++++++++++------- .../ObjectStorage/IFileStorageHelper.cs | 10 +++- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs index 78a73a566a9..40a51057bcc 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.CacheFolder.cs @@ -29,7 +29,7 @@ public partial class ApplicationDataStorageHelper /// Waiting task until completion with the object in the file. public Task ReadCacheFileAsync(string filePath, T @default = default) { - return ReadFileAsync(CacheFolder, filePath, @default); + return ReadFileAsync(this.CacheFolder, filePath, @default); } /// @@ -39,7 +39,7 @@ public Task ReadCacheFileAsync(string filePath, T @default = default) /// A list of file types and names in the target folder. public Task> ReadCacheFolderAsync(string folderPath) { - return ReadFolderAsync(CacheFolder, folderPath); + return ReadFolderAsync(this.CacheFolder, folderPath); } /// @@ -51,7 +51,7 @@ public Task ReadCacheFileAsync(string filePath, T @default = default) /// Waiting task until completion. public Task CreateCacheFileAsync(string filePath, T value) { - return SaveFileAsync(CacheFolder, filePath, value); + return CreateFileAsync(this.CacheFolder, filePath, value); } /// @@ -61,7 +61,7 @@ public Task CreateCacheFileAsync(string filePath, T value) /// Waiting task until completion. public Task CreateCacheFolderAsync(string folderPath) { - return CreateFolderAsync(CacheFolder, folderPath); + return CreateFolderAsync(this.CacheFolder, folderPath); } /// @@ -69,9 +69,20 @@ public Task CreateCacheFolderAsync(string folderPath) /// /// The path to the item for deletion. /// Waiting task until completion. - public Task DeleteCacheItemAsync(string itemPath) + public Task TryDeleteCacheItemAsync(string itemPath) { - return DeleteItemAsync(CacheFolder, itemPath); + return TryDeleteItemAsync(CacheFolder, itemPath); + } + + /// + /// Rename an item in the LocalCacheFolder. + /// + /// The path to the target item. + /// The new nam for the target item. + /// Waiting task until completion. + public Task TryRenameCacheItemAsync(string itemPath, string newName) + { + return TryRenameItemAsync(this.CacheFolder, itemPath, newName); } } } diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index 2cf1c568f54..ab915e39026 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -259,7 +259,7 @@ public bool TryDelete(string compositeKey, string key) /// public Task CreateFileAsync(string filePath, T value) { - return this.SaveFileAsync(this.Folder, filePath, value); + return this.CreateFileAsync(this.Folder, filePath, value); } /// @@ -269,21 +269,15 @@ public Task CreateFolderAsync(string folderPath) } /// - public Task DeleteItemAsync(string itemPath) + public Task TryDeleteItemAsync(string itemPath) { - return this.DeleteItemAsync(this.Folder, itemPath); + return TryDeleteItemAsync(this.Folder, itemPath); } - /// - /// 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) + /// + public Task TryRenameItemAsync(string itemPath, string newName) { - return this.SaveFileAsync(this.Folder, filePath, value); + return TryRenameItemAsync(this.Folder, itemPath, newName); } private async Task ReadFileAsync(StorageFolder folder, string filePath, T? @default = default) @@ -307,7 +301,7 @@ public Task SaveFileAsync(string filePath, T value) }); } - private Task SaveFileAsync(StorageFolder folder, string filePath, T value) + private Task CreateFileAsync(StorageFolder folder, string filePath, T value) { return StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); } @@ -317,10 +311,32 @@ private async Task CreateFolderAsync(StorageFolder folder, string folderPath) await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists); } - private async Task DeleteItemAsync(StorageFolder folder, string itemPath) + private async Task TryDeleteItemAsync(StorageFolder folder, string itemPath) { - var item = await folder.GetItemAsync(itemPath); - await item.DeleteAsync(); + try + { + var item = await folder.GetItemAsync(itemPath); + await item.DeleteAsync(); + return true; + } + catch + { + return false; + } + } + + private async Task TryRenameItemAsync(StorageFolder folder, string itemPath, string newName) + { + try + { + var item = await folder.GetItemAsync(itemPath); + await item.RenameAsync(newName, NameCollisionOption.FailIfExists); + return true; + } + catch + { + return false; + } } } } diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs index df3b1600c39..7e45d82d7c9 100644 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs @@ -55,6 +55,14 @@ public interface IFileStorageHelper /// /// The path to the item for deletion. /// Waiting task until completion. - Task DeleteItemAsync(string itemPath); + Task TryDeleteItemAsync(string itemPath); + + /// + /// Rename an item. + /// + /// The path to the target item. + /// The new nam for the target item. + /// Waiting task until completion. + Task TryRenameItemAsync(string itemPath, string newName); } } From a72b060db0ba27211f58a05609e8740246b49b28 Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Mon, 9 Aug 2021 16:56:39 -0700 Subject: [PATCH 013/200] wip --- .../ApplicationDataStorageHelper.cs | 32 +++++++- .../Test_ApplicationDataStorageHelper.cs | 75 +++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index ab915e39026..ffd4e60a1ab 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -282,12 +282,26 @@ public Task TryRenameItemAsync(string itemPath, string newName) private async Task ReadFileAsync(StorageFolder folder, string filePath, T? @default = default) { + var dirName = System.IO.Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(dirName)) + { + folder = await folder.GetFolderAsync(dirName); + filePath = System.IO.Path.GetFileName(filePath); + } + string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath); return (value != null) ? this.Serializer.Deserialize(value) : @default; } private async Task> ReadFolderAsync(StorageFolder folder, string folderPath) { + var dirName = System.IO.Path.GetDirectoryName(folderPath); + if (!string.IsNullOrEmpty(dirName)) + { + folder = await folder.GetFolderAsync(dirName); + folderPath = System.IO.Path.GetFileName(folderPath); + } + var targetFolder = await folder.GetFolderAsync(folderPath); var items = await targetFolder.GetItemsAsync(); @@ -301,13 +315,27 @@ public Task TryRenameItemAsync(string itemPath, string newName) }); } - private Task CreateFileAsync(StorageFolder folder, string filePath, T value) + private async Task CreateFileAsync(StorageFolder folder, string filePath, T value) { - return StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); + var dirName = System.IO.Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(dirName)) + { + folder = await folder.GetFolderAsync(dirName); + filePath = System.IO.Path.GetFileName(filePath); + } + + return await StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); } private async Task CreateFolderAsync(StorageFolder folder, string folderPath) { + var dirName = System.IO.Path.GetDirectoryName(folderPath); + if (!string.IsNullOrEmpty(dirName)) + { + folder = await folder.GetFolderAsync(dirName); + folderPath = System.IO.Path.GetFileName(folderPath); + } + await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists); } diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs index 3181736f824..fd8d4acd76e 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs @@ -171,6 +171,81 @@ public void Test_StorageHelper_NewPersonTest() Assert.AreEqual(input.Age, output.Age); } + [TestCategory("Helpers")] + [TestMethod] + public async Task Test_StorageHelper_FileCRUDTest() + { + var fileName = "TestFile.txt"; + var fileName2 = "TestFile2.txt"; + var fileContents = "this is a test"; + var fileContents2 = "this is also a test"; + var storageHelper = ApplicationDataStorageHelper.GetCurrent(); + + // Create a file + await storageHelper.CreateFileAsync(fileName, fileContents); + + // Read a file + var readContents = await storageHelper.ReadFileAsync(fileName); + Assert.AreEqual(fileContents, readContents); + + // Update a file + await storageHelper.CreateFileAsync(fileName, fileContents2); + var readContents2 = await storageHelper.ReadFileAsync(fileName); + Assert.AreEqual(fileContents2, readContents2); + + // Rename a file + var itemRenamed = await storageHelper.TryRenameItemAsync(fileName, fileName2); + Assert.IsTrue(itemRenamed); + + // Delete a file + var itemDeleted = await storageHelper.TryDeleteItemAsync(fileName2); + Assert.IsTrue(itemDeleted); + } + + [TestCategory("Helpers")] + [TestMethod] + public async Task Test_StorageHelper_SubFolderCRUDTest() + { + var folderName = "TestFolder"; + var subFolderName = "TestSubFolder"; + var subFolderName2 = "TestSubFolder2"; + var subFolderPath = $"{folderName}/{subFolderName}"; + var subFolderPath2 = $"{folderName}/{subFolderName2}"; + var fileName = "TestFile.txt"; + var filePath = $"{subFolderPath}/{fileName}"; + + var fileContents = "this is a test"; + var storageHelper = ApplicationDataStorageHelper.GetCurrent(); + + // Create a folder + await storageHelper.CreateFolderAsync(folderName); + + // Create a subfolder + await storageHelper.CreateFolderAsync(subFolderPath); + + // Create a file in the subfolder + await storageHelper.CreateFileAsync(filePath, fileContents); + + // Read a file from the subfolder + var readContents = await storageHelper.ReadFileAsync(filePath); + Assert.AreEqual(fileContents, readContents); + + // List subfolder contents + var folderItems = await storageHelper.ReadFolderAsync(subFolderPath); + var folderItemsList = folderItems.ToList(); + Assert.AreEqual(1, folderItemsList.Count()); + Assert.AreEqual(fileName, folderItemsList[0].Name); + Assert.AreEqual(Microsoft.Toolkit.Helpers.DirectoryItemType.File, folderItemsList[0].ItemType); + + // Rename a subfolder + var itemRenamed = await storageHelper.TryRenameItemAsync(subFolderPath, subFolderName2); + Assert.IsTrue(itemRenamed); + + // Delete a subfolder + var itemDeleted = await storageHelper.TryDeleteItemAsync(subFolderPath2); + Assert.IsTrue(itemDeleted); + } + public class Person { public string Name { get; set; } From 9e9a223fc0a2d26ece1ae4d56b4d75aeea8a0fda Mon Sep 17 00:00:00 2001 From: Shane Weaver Date: Tue, 10 Aug 2021 13:16:38 -0700 Subject: [PATCH 014/200] Updated AppDataStorageHelper to better support items in sub-folders. --- .../ApplicationDataStorageHelper.cs | 46 +++++-------------- .../Test_ApplicationDataStorageHelper.cs | 30 ++++++++---- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs index ffd4e60a1ab..7140dcfbd23 100644 --- a/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs +++ b/Microsoft.Toolkit.Uwp/Helpers/ObjectStorage/ApplicationDataStorageHelper.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.Toolkit.Helpers; @@ -282,27 +283,13 @@ public Task TryRenameItemAsync(string itemPath, string newName) private async Task ReadFileAsync(StorageFolder folder, string filePath, T? @default = default) { - var dirName = System.IO.Path.GetDirectoryName(filePath); - if (!string.IsNullOrEmpty(dirName)) - { - folder = await folder.GetFolderAsync(dirName); - filePath = System.IO.Path.GetFileName(filePath); - } - - string value = await StorageFileHelper.ReadTextFromFileAsync(folder, filePath); + string value = await StorageFileHelper.ReadTextFromFileAsync(folder, NormalizePath(filePath)); return (value != null) ? this.Serializer.Deserialize(value) : @default; } private async Task> ReadFolderAsync(StorageFolder folder, string folderPath) { - var dirName = System.IO.Path.GetDirectoryName(folderPath); - if (!string.IsNullOrEmpty(dirName)) - { - folder = await folder.GetFolderAsync(dirName); - folderPath = System.IO.Path.GetFileName(folderPath); - } - - var targetFolder = await folder.GetFolderAsync(folderPath); + var targetFolder = await folder.GetFolderAsync(NormalizePath(folderPath)); var items = await targetFolder.GetItemsAsync(); return items.Select((item) => @@ -317,33 +304,19 @@ public Task TryRenameItemAsync(string itemPath, string newName) private async Task CreateFileAsync(StorageFolder folder, string filePath, T value) { - var dirName = System.IO.Path.GetDirectoryName(filePath); - if (!string.IsNullOrEmpty(dirName)) - { - folder = await folder.GetFolderAsync(dirName); - filePath = System.IO.Path.GetFileName(filePath); - } - - return await StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), filePath, CreationCollisionOption.ReplaceExisting); + return await StorageFileHelper.WriteTextToFileAsync(folder, this.Serializer.Serialize(value)?.ToString(), NormalizePath(filePath), CreationCollisionOption.ReplaceExisting); } private async Task CreateFolderAsync(StorageFolder folder, string folderPath) { - var dirName = System.IO.Path.GetDirectoryName(folderPath); - if (!string.IsNullOrEmpty(dirName)) - { - folder = await folder.GetFolderAsync(dirName); - folderPath = System.IO.Path.GetFileName(folderPath); - } - - await folder.CreateFolderAsync(folderPath, CreationCollisionOption.OpenIfExists); + await folder.CreateFolderAsync(NormalizePath(folderPath), CreationCollisionOption.OpenIfExists); } private async Task TryDeleteItemAsync(StorageFolder folder, string itemPath) { try { - var item = await folder.GetItemAsync(itemPath); + var item = await folder.GetItemAsync(NormalizePath(itemPath)); await item.DeleteAsync(); return true; } @@ -357,7 +330,7 @@ private async Task TryRenameItemAsync(StorageFolder folder, string itemPat { try { - var item = await folder.GetItemAsync(itemPath); + var item = await folder.GetItemAsync(NormalizePath(itemPath)); await item.RenameAsync(newName, NameCollisionOption.FailIfExists); return true; } @@ -366,5 +339,10 @@ private async Task TryRenameItemAsync(StorageFolder folder, string itemPat return false; } } + + private string NormalizePath(string path) + { + return Path.Combine(Path.GetDirectoryName(path), Path.GetFileName(path)); + } } } diff --git a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs index fd8d4acd76e..35e472be805 100644 --- a/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs +++ b/UnitTests/UnitTests.UWP/Helpers/Test_ApplicationDataStorageHelper.cs @@ -206,19 +206,21 @@ public async Task Test_StorageHelper_FileCRUDTest() [TestMethod] public async Task Test_StorageHelper_SubFolderCRUDTest() { - var folderName = "TestFolder"; + var folderName = "TestFolder1"; var subFolderName = "TestSubFolder"; var subFolderName2 = "TestSubFolder2"; var subFolderPath = $"{folderName}/{subFolderName}"; var subFolderPath2 = $"{folderName}/{subFolderName2}"; var fileName = "TestFile.txt"; + var fileName2 = "TestFile2.txt"; var filePath = $"{subFolderPath}/{fileName}"; - + var filePath2 = $"{subFolderPath2}/{fileName2}"; var fileContents = "this is a test"; + var storageHelper = ApplicationDataStorageHelper.GetCurrent(); - // Create a folder - await storageHelper.CreateFolderAsync(folderName); + // Attempt to delete the folder to clean up from any previously failed test runs. + await storageHelper.TryDeleteItemAsync(folderName); // Create a subfolder await storageHelper.CreateFolderAsync(subFolderPath); @@ -237,13 +239,25 @@ public async Task Test_StorageHelper_SubFolderCRUDTest() Assert.AreEqual(fileName, folderItemsList[0].Name); Assert.AreEqual(Microsoft.Toolkit.Helpers.DirectoryItemType.File, folderItemsList[0].ItemType); - // Rename a subfolder - var itemRenamed = await storageHelper.TryRenameItemAsync(subFolderPath, subFolderName2); + // Rename a file in a subfolder + var itemRenamed = await storageHelper.TryRenameItemAsync(filePath, fileName2); Assert.IsTrue(itemRenamed); + // Rename a subfolder + var folderRenamed = await storageHelper.TryRenameItemAsync(subFolderPath, subFolderName2); + Assert.IsTrue(folderRenamed); + + // Delete a file in a subfolder + var fileDeleted = await storageHelper.TryDeleteItemAsync(filePath2); + Assert.IsTrue(fileDeleted); + // Delete a subfolder - var itemDeleted = await storageHelper.TryDeleteItemAsync(subFolderPath2); - Assert.IsTrue(itemDeleted); + var subFolderDeleted = await storageHelper.TryDeleteItemAsync(subFolderPath2); + Assert.IsTrue(subFolderDeleted); + + // Delete the folder to clean up. + var folderDeleted = await storageHelper.TryDeleteItemAsync(folderName); + Assert.IsTrue(folderDeleted); } public class Person From 41b580a87984549c7e7eafb321c2d5c787694496 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Wed, 11 Aug 2021 20:32:13 -0700 Subject: [PATCH 015/200] Adding support for height --- .../Triggers/ControlWidthTrigger.cs | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs index 75c89474c56..0aef0b9ecbd 100644 --- a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.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. @@ -29,15 +29,10 @@ public bool CanTrigger nameof(CanTrigger), typeof(bool), typeof(ControlWidthTrigger), - new PropertyMetadata(true, OnCanTriggerProperty)); - - private static void OnCanTriggerProperty(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((ControlWidthTrigger)d).UpdateTrigger(); - } + new PropertyMetadata(true, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); /// - /// Gets or sets the max size at which to trigger. + /// Gets or sets the max width at which to trigger. /// public double MaxWidth { @@ -52,15 +47,10 @@ public double MaxWidth nameof(MaxWidth), typeof(double), typeof(ControlWidthTrigger), - new PropertyMetadata(double.PositiveInfinity, OnMaxWidthPropertyChanged)); - - private static void OnMaxWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((ControlWidthTrigger)d).UpdateTrigger(); - } + new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); /// - /// Gets or sets the min size at which to trigger. + /// Gets or sets the min width at which to trigger. /// public double MinWidth { @@ -75,13 +65,44 @@ public double MinWidth nameof(MinWidth), typeof(double), typeof(ControlWidthTrigger), - new PropertyMetadata(0.0, OnMinWidthPropertyChanged)); + new PropertyMetadata(0.0, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); - private static void OnMinWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + /// + /// Gets or sets the max height at which to trigger. + /// + public double MaxHeight { - ((ControlWidthTrigger)d).UpdateTrigger(); + get => (double)GetValue(MaxHeightProperty); + set => SetValue(MaxHeightProperty, value); } + /// + /// Identifies the DependencyProperty. + /// + public static readonly DependencyProperty MaxHeightProperty = DependencyProperty.Register( + nameof(MaxHeight), + typeof(double), + typeof(ControlWidthTrigger), + new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + + /// + /// Gets or sets the min height at which to trigger. + /// + public double MinHeight + { + get => (double)GetValue(MinHeightProperty); + set => SetValue(MinHeightProperty, value); + } + + /// + /// Identifies the DependencyProperty. + /// + public static readonly DependencyProperty MinHeightProperty = DependencyProperty.Register( + nameof(MinHeight), + typeof(double), + typeof(ControlWidthTrigger), + new PropertyMetadata(0.0, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + /// /// Gets or sets the element whose width will observed /// for the trigger. @@ -139,7 +160,11 @@ private void UpdateTrigger() return; } - SetActive(MinWidth <= TargetElement.ActualWidth && TargetElement.ActualWidth < MaxWidth); + SetActive( + MinWidth <= TargetElement.ActualWidth && + TargetElement.ActualWidth < MaxWidth && + MinHeight <= TargetElement.ActualHeight && + TargetElement.ActualHeight < MaxHeight); } } } From c0f7ff368480b2ed3e668a6445946780db41fd17 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Wed, 11 Aug 2021 20:33:25 -0700 Subject: [PATCH 016/200] Renamed trigger --- ...lWidthTrigger.cs => ControlSizeTrigger.cs} | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) rename Microsoft.Toolkit.Uwp.UI/Triggers/{ControlWidthTrigger.cs => ControlSizeTrigger.cs} (86%) diff --git a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs similarity index 86% rename from Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs rename to Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs index 0aef0b9ecbd..2dc74655e44 100644 --- a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlWidthTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs @@ -8,9 +8,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Triggers { /// /// A conditional state trigger that functions - /// based on the target control's width. + /// based on the target control's width or height. /// - public class ControlWidthTrigger : StateTriggerBase + public class ControlSizeTrigger : StateTriggerBase { /// /// Gets or sets a value indicating @@ -28,8 +28,8 @@ public bool CanTrigger public static readonly DependencyProperty CanTriggerProperty = DependencyProperty.Register( nameof(CanTrigger), typeof(bool), - typeof(ControlWidthTrigger), - new PropertyMetadata(true, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + typeof(ControlSizeTrigger), + new PropertyMetadata(true, (d, e) => ((ControlSizeTrigger)d).UpdateTrigger())); /// /// Gets or sets the max width at which to trigger. @@ -46,8 +46,8 @@ public double MaxWidth public static readonly DependencyProperty MaxWidthProperty = DependencyProperty.Register( nameof(MaxWidth), typeof(double), - typeof(ControlWidthTrigger), - new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + typeof(ControlSizeTrigger), + new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlSizeTrigger)d).UpdateTrigger())); /// /// Gets or sets the min width at which to trigger. @@ -64,8 +64,8 @@ public double MinWidth public static readonly DependencyProperty MinWidthProperty = DependencyProperty.Register( nameof(MinWidth), typeof(double), - typeof(ControlWidthTrigger), - new PropertyMetadata(0.0, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + typeof(ControlSizeTrigger), + new PropertyMetadata(0.0, (d, e) => ((ControlSizeTrigger)d).UpdateTrigger())); /// /// Gets or sets the max height at which to trigger. @@ -82,8 +82,8 @@ public double MaxHeight public static readonly DependencyProperty MaxHeightProperty = DependencyProperty.Register( nameof(MaxHeight), typeof(double), - typeof(ControlWidthTrigger), - new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + typeof(ControlSizeTrigger), + new PropertyMetadata(double.PositiveInfinity, (d, e) => ((ControlSizeTrigger)d).UpdateTrigger())); /// /// Gets or sets the min height at which to trigger. @@ -100,8 +100,8 @@ public double MinHeight public static readonly DependencyProperty MinHeightProperty = DependencyProperty.Register( nameof(MinHeight), typeof(double), - typeof(ControlWidthTrigger), - new PropertyMetadata(0.0, (d, e) => ((ControlWidthTrigger)d).UpdateTrigger())); + typeof(ControlSizeTrigger), + new PropertyMetadata(0.0, (d, e) => ((ControlSizeTrigger)d).UpdateTrigger())); /// /// Gets or sets the element whose width will observed @@ -122,12 +122,12 @@ public FrameworkElement TargetElement public static readonly DependencyProperty TargetElementProperty = DependencyProperty.Register( nameof(TargetElement), typeof(FrameworkElement), - typeof(ControlWidthTrigger), + typeof(ControlSizeTrigger), new PropertyMetadata(null, OnTargetElementPropertyChanged)); private static void OnTargetElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - ((ControlWidthTrigger)d).UpdateTargetElement((FrameworkElement)e.OldValue, (FrameworkElement)e.NewValue); + ((ControlSizeTrigger)d).UpdateTargetElement((FrameworkElement)e.OldValue, (FrameworkElement)e.NewValue); } // Handle event to get current values From b31758da2d411b6f8fc5de98942596ddbf47bab4 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Wed, 11 Aug 2021 21:35:28 -0700 Subject: [PATCH 017/200] Added sample page --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 4 ++ .../Triggers/ControlSizeTrigger.bind | 43 +++++++++++++++++++ .../SamplePages/XamlOnlyPage.xaml | 1 + .../SamplePages/samples.json | 17 ++++++-- 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 07b878addb8..c94beaa13c1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -1078,6 +1078,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind new file mode 100644 index 00000000000..8ffbf70a104 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 631cf5e5c95..5beea3c5e25 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -31,6 +31,7 @@ + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index b07fedab76b..d0540fd83c0 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -785,7 +785,7 @@ "Type": "ThemeListenerPage", "Subcategory": "Systems", "About": "The ThemeListener allows you to keep track of changes to the System Theme.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Helpers/ThemeListener.cs", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Helpers/ThemeListener.cs", "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/ThemeListener.md" }, @@ -844,7 +844,7 @@ "Type": "CanvasPathGeometryPage", "Subcategory": "Parser", "About": "CanvasPathGeometry class allows you to convert Win2d Path Mini Language string to CanvasGeometry, Brushes, CanvasStrokes or CanvasStrokeStyles.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Geometry", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Uwp.UI.Media/Geometry", "Icon": "/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/CanvasPathGeometry.md" }, @@ -882,7 +882,7 @@ "Name": "Guard APIs", "Subcategory": "Developer", "About": "The Guard APIs can be used to validate method arguments in a streamlined manner, which is also faster, less verbose, more expressive and less error prone than manually writing checks and throwing exceptions.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Diagnostics", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Diagnostics", "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/developer-tools/Guard.md" }, @@ -890,7 +890,7 @@ "Name": "High Performance APIs", "Subcategory": "Developer", "About": "The High Performance package contains a set of APIs that are heavily focused on optimization. All the new APIs have been carefully crafted to achieve the best possible performance when using them, either through reduced memory allocation, micro-optimizations at the assembly level, or by structuring the APIs in a way that facilitates writing performance oriented code in general.", - "CodeUrl" : "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.HighPerformance", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.HighPerformance", "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/high-performance/Introduction.md" }, @@ -911,6 +911,15 @@ "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/Triggers.md" }, + { + "Name": "ControlSizeTrigger", + "Subcategory": "State Triggers", + "About": "Enables a state if the target control meets the specified size.", + "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs", + "XamlCodeFile": "/SamplePages/Triggers/ControlSizeTrigger.bind", + "Icon": "/Assets/Helpers.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/helpers/Triggers.md" + }, { "Name": "IsEqualStateTrigger", "Subcategory": "State Triggers", From a582f346e2f4f30b17e4a998b67406121b2ae618 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Wed, 11 Aug 2021 21:52:40 -0700 Subject: [PATCH 018/200] Bug fix --- .../SamplePages/Triggers/ControlSizeTrigger.bind | 2 +- Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind index 8ffbf70a104..534eb404fa2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind @@ -12,7 +12,7 @@ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index d0540fd83c0..adc8674db85 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -914,7 +914,7 @@ { "Name": "ControlSizeTrigger", "Subcategory": "State Triggers", - "About": "Enables a state if the target control meets the specified size.", + "About": "Enables a state if the target control meets the specified size", "CodeUrl": "https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs", "XamlCodeFile": "/SamplePages/Triggers/ControlSizeTrigger.bind", "Icon": "/Assets/Helpers.png", From cdf48d04be8d2c2d29738fc03f397510e85b9c27 Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Wed, 11 Aug 2021 22:14:08 -0700 Subject: [PATCH 019/200] Fixed sample bug --- .../SamplePages/Triggers/ControlSizeTrigger.bind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind index 534eb404fa2..50819d207c7 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind @@ -12,7 +12,7 @@ From e62eb8bf8bcf2e1553ca8f94e8698aadafc0d29f Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Aug 2021 16:32:49 -0700 Subject: [PATCH 020/200] Improving comments --- Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs index 2dc74655e44..b7aead3c22b 100644 --- a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs @@ -33,6 +33,8 @@ public bool CanTrigger /// /// Gets or sets the max width at which to trigger. + /// This value is exclusive, meaning this trigger + /// could be active if the value is < MaxWidth. /// public double MaxWidth { @@ -51,6 +53,8 @@ public double MaxWidth /// /// Gets or sets the min width at which to trigger. + /// This value is inclusive, meaning this trigger + /// could be active if the value is >= MinWidth. /// public double MinWidth { @@ -69,6 +73,8 @@ public double MinWidth /// /// Gets or sets the max height at which to trigger. + /// This value is exclusive, meaning this trigger + /// could be active if the value is < MaxHeight. /// public double MaxHeight { @@ -87,6 +93,8 @@ public double MaxHeight /// /// Gets or sets the min height at which to trigger. + /// This value is inclusive, meaning this trigger + /// could be active if the value is >= MinHeight. /// public double MinHeight { From fd8aee572c46affb6d4abd47b2b4fb369b6b096b Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Aug 2021 16:33:41 -0700 Subject: [PATCH 021/200] Fixing xbind in sample --- .../SamplePages/Triggers/ControlSizeTrigger.bind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind index 50819d207c7..583f4cbbbda 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/ControlSizeTrigger.bind @@ -12,7 +12,7 @@ From 5a523c9aaa44f65c9d897b2d726b906a01c69bbb Mon Sep 17 00:00:00 2001 From: Daniel Paulino Date: Sat, 14 Aug 2021 18:44:10 -0700 Subject: [PATCH 022/200] Added unit test --- .../Triggers/ControlSizeTrigger.cs | 21 +++++--- .../UI/Triggers/Test_ControlSizeTrigger.cs | 50 +++++++++++++++++++ UnitTests/UnitTests.UWP/UnitTests.UWP.csproj | 1 + 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 UnitTests/UnitTests.UWP/UI/Triggers/Test_ControlSizeTrigger.cs diff --git a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs index b7aead3c22b..1fcbd28291e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI/Triggers/ControlSizeTrigger.cs @@ -34,7 +34,7 @@ public bool CanTrigger /// /// Gets or sets the max width at which to trigger. /// This value is exclusive, meaning this trigger - /// could be active if the value is < MaxWidth. + /// could be active if the value is less than MaxWidth. /// public double MaxWidth { @@ -74,7 +74,7 @@ public double MinWidth /// /// Gets or sets the max height at which to trigger. /// This value is exclusive, meaning this trigger - /// could be active if the value is < MaxHeight. + /// could be active if the value is less than MaxHeight. /// public double MaxHeight { @@ -133,6 +133,11 @@ public FrameworkElement TargetElement typeof(ControlSizeTrigger), new PropertyMetadata(null, OnTargetElementPropertyChanged)); + /// + /// Gets a value indicating whether the trigger is active. + /// + public bool IsActive { get; private set; } + private static void OnTargetElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ControlSizeTrigger)d).UpdateTargetElement((FrameworkElement)e.OldValue, (FrameworkElement)e.NewValue); @@ -168,11 +173,13 @@ private void UpdateTrigger() return; } - SetActive( - MinWidth <= TargetElement.ActualWidth && - TargetElement.ActualWidth < MaxWidth && - MinHeight <= TargetElement.ActualHeight && - TargetElement.ActualHeight < MaxHeight); + bool activate = MinWidth <= TargetElement.ActualWidth && + TargetElement.ActualWidth < MaxWidth && + MinHeight <= TargetElement.ActualHeight && + TargetElement.ActualHeight < MaxHeight; + + IsActive = activate; + SetActive(activate); } } } diff --git a/UnitTests/UnitTests.UWP/UI/Triggers/Test_ControlSizeTrigger.cs b/UnitTests/UnitTests.UWP/UI/Triggers/Test_ControlSizeTrigger.cs new file mode 100644 index 00000000000..7ccfad59f26 --- /dev/null +++ b/UnitTests/UnitTests.UWP/UI/Triggers/Test_ControlSizeTrigger.cs @@ -0,0 +1,50 @@ +using Microsoft.Toolkit.Uwp; +using Microsoft.Toolkit.Uwp.UI.Triggers; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; + +namespace UnitTests.UWP.UI.Triggers +{ + [TestClass] + [TestCategory("Test_ControlSizeTrigger")] + public class Test_ControlSizeTrigger : VisualUITestBase + { + [DataTestMethod] + [DataRow(450, 450, true)] + [DataRow(400, 400, true)] + [DataRow(500, 500, false)] + [DataRow(399, 400, false)] + [DataRow(400, 399, false)] + public async Task ControlSizeTriggerTest(double width, double height, bool expectedResult) + { + await App.DispatcherQueue.EnqueueAsync(() => + { + Grid grid = CreateGrid(width, height); + var trigger = new ControlSizeTrigger(); + + trigger.TargetElement = grid; + trigger.MaxHeight = 500; + trigger.MinHeight = 400; + trigger.MaxWidth = 500; + trigger.MinWidth = 400; + + Assert.AreEqual(expectedResult, trigger.IsActive); + }); + } + + private Grid CreateGrid(double width, double height) + { + var grid = new Grid() + { + Height = height, + Width = width + }; + grid.Measure(new Windows.Foundation.Size(1000, 1000)); + grid.Arrange(new Windows.Foundation.Rect(0, 0, 1000, 1000)); + grid.UpdateLayout(); + + return grid; + } + } +} diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index 1659951ec88..8620e2c1234 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -250,6 +250,7 @@ + From a5fc9ebbe4a05a756a2f504f0b49b0b0ba92d1d4 Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Sun, 15 Aug 2021 14:12:34 +0200 Subject: [PATCH 023/200] Windows 10X TwoPaneView for ListDetailsView --- .../Controls/ListDetailsView.Metadata.cs | 12 +- .../Controls/ListDetailsView.Typedata.cs | 9 +- .../ListDetailsView/BackButtonBehavior.cs | 4 +- .../ListDetailsView.BackButton.cs | 166 +++++++ .../ListDetailsView/ListDetailsView.Events.cs | 6 +- .../ListDetailsView.Properties.cs | 185 +++++++- .../ListDetailsView/ListDetailsView.cs | 408 +++++++----------- .../ListDetailsView/ListDetailsView.xaml | 262 +++++------ .../ListDetailsView/ListDetailsViewState.cs | 8 +- .../UI/Controls/Test_ListDetailsView.cs | 94 +++- 10 files changed, 734 insertions(+), 420 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Metadata.cs index 3013c7fdd2b..45bba363e2f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Metadata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Metadata.cs @@ -2,12 +2,10 @@ // 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.ComponentModel; - using Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties; - using Microsoft.VisualStudio.DesignTools.Extensibility; using Microsoft.VisualStudio.DesignTools.Extensibility.Metadata; +using System.ComponentModel; namespace Microsoft.Toolkit.Uwp.UI.Controls.Design { @@ -25,6 +23,7 @@ public ListDetailsViewMetadata() new EditorBrowsableAttribute(EditorBrowsableState.Advanced) ); b.AddCustomAttributes(nameof(ListDetailsView.ListPaneBackground), new CategoryAttribute(Resources.CategoryBrush)); + b.AddCustomAttributes(nameof(ListDetailsView.DetailsPaneBackground), new CategoryAttribute(Resources.CategoryBrush)); b.AddCustomAttributes(nameof(ListDetailsView.ListHeader), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(ListDetailsView.ListHeaderTemplate), new CategoryAttribute(Resources.CategoryAppearance), @@ -36,6 +35,11 @@ public ListDetailsViewMetadata() new CategoryAttribute(Resources.CategoryCommon), new EditorBrowsableAttribute(EditorBrowsableState.Advanced) ); + AddCustomAttributes(nameof(ListDetailsView.ListPaneNoItemsContent), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(ListDetailsView.ListPaneNoItemsContentTemplate), + new CategoryAttribute(Resources.CategoryCommon), + new EditorBrowsableAttribute(EditorBrowsableState.Advanced) + ); b.AddCustomAttributes(nameof(ListDetailsView.ViewState), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(ListDetailsView.ListCommandBar), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(ListDetailsView.DetailsCommandBar), new CategoryAttribute(Resources.CategoryCommon)); @@ -44,4 +48,4 @@ public ListDetailsViewMetadata() ); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Typedata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Typedata.cs index 15c2bcb7d65..d2472467486 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Typedata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Controls/ListDetailsView.Typedata.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; - namespace Microsoft.Toolkit.Uwp.UI.Controls.Design { internal static partial class ControlTypes @@ -15,6 +13,11 @@ internal static class ListDetailsView { internal const string DetailsCommandBar = nameof(DetailsCommandBar); internal const string DetailsTemplate = nameof(DetailsTemplate); + internal const string DetailsPaneBackground = nameof(DetailsPaneBackground); + internal const string DetailsContentTemplateSelector = nameof(DetailsContentTemplateSelector); + internal const string ListPaneItemTemplateSelector = nameof(ListPaneItemTemplateSelector); + internal const string ListPaneNoItemsContentTemplate = nameof(ListPaneNoItemsContentTemplate); + internal const string ListPaneNoItemsContent = nameof(ListPaneNoItemsContent); internal const string ListCommandBar = nameof(ListCommandBar); internal const string ListHeader = nameof(ListHeader); internal const string ListHeaderTemplate = nameof(ListHeaderTemplate); @@ -25,4 +28,4 @@ internal static class ListDetailsView internal const string SelectedItem = nameof(SelectedItem); internal const string ViewState = nameof(ViewState); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/BackButtonBehavior.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/BackButtonBehavior.cs index d3c8093d7d2..9fa9ee80ce4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/BackButtonBehavior.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/BackButtonBehavior.cs @@ -14,7 +14,7 @@ public enum BackButtonBehavior /// /// /// If the back button controlled by is already visible, the will hook into that button. - /// If the new NavigationView provided by the Windows UI nuget package is used, the will enable and show that button. + /// If the new NavigationView provided by the Windows UI NuGet package is used, the will enable and show that button. /// Otherwise the inline button is used. /// Automatic, @@ -34,4 +34,4 @@ public enum BackButtonBehavior /// Manual, } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs new file mode 100644 index 00000000000..ff046c6781b --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs @@ -0,0 +1,166 @@ +// 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.Reflection; +using Windows.ApplicationModel; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// Panel that allows for a List/Details pattern. + /// + /// + public partial class ListDetailsView + { + 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 Button _inlineBackButton; + private object _navigationView; + private Frame _frame; + + /// + /// Sets the back button visibility based on the current visual state and selected item + /// + private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) + { + const int backButtonVisible = 1; + + if (DesignMode.DesignModeEnabled) + { + return; + } + + if (ViewState == ListDetailsViewState.Details) + { + if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + { + _inlineBackButton.Visibility = Visibility.Visible; + } + else if (BackButtonBehavior == BackButtonBehavior.Automatic) + { + // Continue to support the system back button if it is being used + SystemNavigationManager navigationManager = SystemNavigationManager.GetForCurrentView(); + if (navigationManager.AppViewBackButtonVisibility == AppViewBackButtonVisibility.Visible) + { + // Setting this indicates that the system back button is being used + _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; + } + else if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) + { + // We can only use the new NavigationView if we also have a Frame + // If there is no frame we have to use the inline button + _inlineBackButton.Visibility = Visibility.Visible; + } + else + { + SetNavigationViewBackButtonState(backButtonVisible, true); + } + } + else if (BackButtonBehavior != BackButtonBehavior.Manual) + { + SystemNavigationManager navigationManager = SystemNavigationManager.GetForCurrentView(); + _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; + + navigationManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; + } + } + else if (previousState == ListDetailsViewState.Details) + { + if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + { + _inlineBackButton.Visibility = Visibility.Collapsed; + } + else if (BackButtonBehavior == BackButtonBehavior.Automatic) + { + if (!_previousSystemBackButtonVisibility.HasValue) + { + if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) + { + _inlineBackButton.Visibility = Visibility.Collapsed; + } + else + { + SetNavigationViewBackButtonState(_previousNavigationViewBackVisibilty, _previousNavigationViewBackEnabled); + } + } + } + + if (_previousSystemBackButtonVisibility.HasValue) + { + // Make sure we show the back button if the stack can navigate back + SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = _previousSystemBackButtonVisibility.Value; + _previousSystemBackButtonVisibility = null; + } + } + } + + private void SetNavigationViewBackButtonState(int visible, bool enabled) + { + if (_navigationView == null) + { + return; + } + + System.Type navType = _navigationView.GetType(); + PropertyInfo visibleProperty = navType.GetProperty("IsBackButtonVisible"); + if (visibleProperty != null) + { + _previousNavigationViewBackVisibilty = (int)visibleProperty.GetValue(_navigationView); + visibleProperty.SetValue(_navigationView, visible); + } + + PropertyInfo enabledProperty = navType.GetProperty("IsBackEnabled"); + if (enabledProperty != null) + { + _previousNavigationViewBackEnabled = (bool)enabledProperty.GetValue(_navigationView); + enabledProperty.SetValue(_navigationView, enabled); + } + } + + /// + /// Closes the details pane if we are in narrow state + /// + /// The sender + /// The event args + private void OnFrameNavigating(object sender, NavigatingCancelEventArgs args) + { + if ((args.NavigationMode == NavigationMode.Back) && (ViewState == ListDetailsViewState.Details)) + { + ClearSelectedItem(); + args.Cancel = true; + } + } + + /// + /// Closes the details pane if we are in narrow state + /// + /// The sender + /// The event args + private void OnBackRequested(object sender, BackRequestedEventArgs args) + { + if (ViewState == ListDetailsViewState.Details) + { + // let the OnFrameNavigating method handle it if + if (_frame == null || !_frame.CanGoBack) + { + ClearSelectedItem(); + } + + args.Handled = true; + } + } + + private void OnInlineBackButtonClicked(object sender, RoutedEventArgs e) + { + ClearSelectedItem(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Events.cs index ed3009f2ac5..d4ebd8a67cf 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Events.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Events.cs @@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// /// Panel that allows for a List/Details pattern. /// - /// + /// public partial class ListDetailsView { /// @@ -19,7 +19,7 @@ public partial class ListDetailsView public event SelectionChangedEventHandler SelectionChanged; /// - /// Occurs when the view state changes + /// Occurs when the view state changes. /// public event EventHandler ViewStateChanged; @@ -28,4 +28,4 @@ private void OnSelectionChanged(SelectionChangedEventArgs e) SelectionChanged?.Invoke(this, e); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs index 1cd9caa3c09..d9c5df75e5d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs @@ -45,6 +45,36 @@ public partial class ListDetailsView typeof(ListDetailsView), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsContentTemplateSelectorProperty = DependencyProperty.Register( + nameof(DetailsContentTemplateSelector), + typeof(DataTemplateSelector), + typeof(ListDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty ListPaneItemTemplateSelectorProperty = DependencyProperty.Register( + nameof(ListPaneItemTemplateSelector), + typeof(DataTemplateSelector), + typeof(ListDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty DetailsPaneBackgroundProperty = DependencyProperty.Register( + nameof(DetailsPaneBackground), + typeof(Brush), + typeof(ListDetailsView), + new PropertyMetadata(null)); + /// /// Identifies the dependency property. /// @@ -75,6 +105,26 @@ public partial class ListDetailsView typeof(ListDetailsView), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty ListPaneNoItemsContentProperty = DependencyProperty.Register( + nameof(ListPaneNoItemsContent), + typeof(object), + typeof(ListDetailsView), + new PropertyMetadata(null)); + + /// + /// Identifies the dependency property. + /// + /// The identifier for the dependency property. + public static readonly DependencyProperty ListPaneNoItemsContentTemplateProperty = DependencyProperty.Register( + nameof(ListPaneNoItemsContentTemplate), + typeof(DataTemplate), + typeof(ListDetailsView), + new PropertyMetadata(null)); + /// /// Identifies the dependency property. /// @@ -103,7 +153,7 @@ public partial class ListDetailsView nameof(ListPaneWidth), typeof(double), typeof(ListDetailsView), - new PropertyMetadata(320d)); + new PropertyMetadata(320d, OnListPaneWidthChanged)); /// /// Identifies the dependency property. @@ -162,7 +212,7 @@ public partial class ListDetailsView nameof(CompactModeThresholdWidth), typeof(double), typeof(ListDetailsView), - new PropertyMetadata(720d, OnCompactModeThresholdWidthChanged)); + new PropertyMetadata(640d)); /// /// Identifies the dependency property @@ -202,6 +252,35 @@ public DataTemplate DetailsTemplate set { SetValue(DetailsTemplateProperty, value); } } + /// + /// Gets or sets the for the details presenter. + /// + public DataTemplateSelector DetailsContentTemplateSelector + { + get { return (DataTemplateSelector)GetValue(DetailsContentTemplateSelectorProperty); } + set { SetValue(DetailsContentTemplateSelectorProperty, value); } + } + + /// + /// Gets or sets the for the list pane items. + /// + public DataTemplateSelector ListPaneItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(ListPaneItemTemplateSelectorProperty); } + set { SetValue(ListPaneItemTemplateSelectorProperty, value); } + } + + /// + /// Gets or sets the content for the list pane's header + /// Gets or sets the Brush to apply to the background of the details area of the control. + /// + /// The Brush to apply to the background of the details area of the control. + public Brush DetailsPaneBackground + { + get { return (Brush)GetValue(DetailsPaneBackgroundProperty); } + set { SetValue(DetailsPaneBackgroundProperty, value); } + } + /// /// Gets or sets the Brush to apply to the background of the list area of the control. /// @@ -236,6 +315,30 @@ public DataTemplate ListHeaderTemplate set { SetValue(ListHeaderTemplateProperty, value); } } + /// + /// Gets or sets the content for the list pane's no items presenter. + /// + /// + /// The content of the list pane's header. The default is null. + /// + public object ListPaneNoItemsContent + { + get { return GetValue(ListPaneNoItemsContentProperty); } + set { SetValue(ListPaneNoItemsContentProperty, value); } + } + + /// + /// Gets or sets the DataTemplate used to display the list pane's no items presenter. + /// + /// + /// The template that specifies the visualization of the list pane no items object. The default is null. + /// + public DataTemplate ListPaneNoItemsContentTemplate + { + get { return (DataTemplate)GetValue(ListPaneNoItemsContentTemplateProperty); } + set { SetValue(ListPaneNoItemsContentTemplateProperty, value); } + } + /// /// Gets or sets the content for the details pane's header /// @@ -346,5 +449,81 @@ public BackButtonBehavior BackButtonBehavior /// This new model will be the DataContext of the Details area. /// public Func MapDetails { get; set; } + + /// + /// Fired when the DetailsCommandBar changes. + /// + /// The sender. + /// The event args. + private static void OnDetailsCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).OnDetailsCommandBarChanged(); + } + + /// + /// Fired when the changes. + /// + /// The sender. + /// The event args. + private static void OnListCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).OnListCommandBarChanged(); + } + + /// + /// Fired when the SelectedItem changes. + /// + /// The sender. + /// The event args. + /// + /// Sets up animations for the DetailsPresenter for animating in/out. + /// + private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).OnSelectedItemChanged(e); + } + + /// + /// Fired when the SelectedIndex changes. + /// + /// The sender. + /// The event args. + /// + /// Sets up animations for the DetailsPresenter for animating in/out. + /// + private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).OnSelectedIndexChanged(e); + } + + /// + /// Fired when the is changed. + /// + /// The sender. + /// The event args. + private static void OnBackButtonBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).SetBackButtonVisibility(); + } + + /// + /// Fired when the is changed. + /// + /// The sender. + /// The event args. + private static void OnListHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).SetListHeaderVisibility(); + } + + /// + /// Fired when the is changed. + /// + /// The sender. + /// The event args. + private static void OnListPaneWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((ListDetailsView)d).OnListPaneWidthChanged(); + } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs index 896a2c75a26..2dbcc0da1c4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -3,13 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Linq; using Windows.ApplicationModel; using Windows.UI.Core; 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 @@ -23,32 +21,32 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls [TemplateVisualState(Name = NoSelectionWideState, GroupName = SelectionStates)] [TemplateVisualState(Name = HasSelectionWideState, GroupName = SelectionStates)] [TemplateVisualState(Name = HasSelectionNarrowState, GroupName = SelectionStates)] - [TemplateVisualState(Name = NarrowState, GroupName = WidthStates)] - [TemplateVisualState(Name = WideState, GroupName = WidthStates)] public partial class ListDetailsView : ItemsControl { - private const string PartDetailsPresenter = "DetailsPresenter"; - private const string PartDetailsPanel = "DetailsPanel"; - private const string PartBackButton = "ListDetailsBackButton"; - private const string PartHeaderContentPresenter = "HeaderContentPresenter"; - private const string NarrowState = "NarrowState"; - private const string WideState = "WideState"; - private const string WidthStates = "WidthStates"; + // All view states: private const string SelectionStates = "SelectionStates"; - private const string HasSelectionNarrowState = "HasSelectionNarrow"; + private const string NoSelectionWideState = "NoSelectionWide"; private const string HasSelectionWideState = "HasSelectionWide"; private const string NoSelectionNarrowState = "NoSelectionNarrow"; - private const string NoSelectionWideState = "NoSelectionWide"; + private const string HasSelectionNarrowState = "HasSelectionNarrow"; + + private const string HasItemsStates = "HasItemsStates"; + private const string HasItemsState = "HasItemsState"; + private const string HasNoItemsState = "HasNoItemsState"; - private AppViewBackButtonVisibility? _previousSystemBackButtonVisibility; - private bool _previousNavigationViewBackEnabled; + // Control names: + private const string PartRootPanel = "RootPanel"; + private const string PartDetailsPresenter = "DetailsPresenter"; + private const string PartDetailsPanel = "DetailsPanel"; + private const string PartMasterList = "MasterList"; + private const string PartBackButton = "ListDetailsBackButton"; + private const string PartHeaderContentPresenter = "HeaderContentPresenter"; + private const string PartListPaneCommandBarPanel = "ListPaneCommandBarPanel"; + private const string PartDetailsPaneCommandBarPanel = "DetailsPaneCommandBarPanel"; - private NavigationView.NavigationViewBackButtonVisible _previousNavigationViewBackVisibilty; - private NavigationView.NavigationView _navigationView; private ContentPresenter _detailsPresenter; + private NavigationView.TwoPaneView _twoPaneView; private VisualStateGroup _selectionStateGroup; - private Button _inlineBackButton; - private Frame _frame; /// /// Initializes a new instance of the class. @@ -61,6 +59,14 @@ public ListDetailsView() Unloaded += OnUnloaded; } + /// + /// Clears the and prevent flickering of the UI if only the order of the items changed. + /// + public void ClearSelectedItem() + { + SelectedItem = null; + } + /// /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call /// ApplyTemplate. In simplest terms, this means the method is called just before a UI element displays @@ -81,15 +87,25 @@ protected override void OnApplyTemplate() _inlineBackButton.Click += OnInlineBackButtonClicked; } + _selectionStateGroup = (VisualStateGroup)GetTemplateChild(SelectionStates); + if (_selectionStateGroup != null) + { + _selectionStateGroup.CurrentStateChanged += OnSelectionStateChanged; + } + + _twoPaneView = (NavigationView.TwoPaneView)GetTemplateChild(PartRootPanel); + if (_twoPaneView != null) + { + _twoPaneView.ModeChanged += OnModeChanged; + } + _detailsPresenter = (ContentPresenter)GetTemplateChild(PartDetailsPresenter); SetDetailsContent(); SetListHeaderVisibility(); OnDetailsCommandBarChanged(); OnListCommandBarChanged(); - - SizeChanged -= ListDetailsView_SizeChanged; - SizeChanged += ListDetailsView_SizeChanged; + OnListPaneWidthChanged(); UpdateView(true); } @@ -97,101 +113,61 @@ protected override void OnApplyTemplate() /// /// Fired when the SelectedIndex changes. /// - /// The sender - /// The event args + /// The event args. /// /// Sets up animations for the DetailsPresenter for animating in/out. /// - private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private void OnSelectedIndexChanged(DependencyPropertyChangedEventArgs e) { - var view = (ListDetailsView)d; - - var newValue = (int)e.NewValue < 0 ? null : view.Items[(int)e.NewValue]; - var oldValue = e.OldValue == null ? null : view.Items.ElementAtOrDefault((int)e.OldValue); - - // check if selection actually changed - if (view.SelectedItem != newValue) + if (e.NewValue is int newIndex) { - // sync SelectedItem - view.SetValue(SelectedItemProperty, newValue); - view.UpdateSelection(oldValue, newValue); + object newItem = newIndex >= 0 && Items.Count > newIndex ? Items[newIndex] : null; + object oldItem = e.OldValue is int oldIndex && oldIndex >= 0 && Items.Count > oldIndex ? Items[oldIndex] : null; + if (SelectedItem != newItem) + { + if (newItem is null) + { + ClearSelectedItem(); + } + else + { + SetValue(SelectedItemProperty, newItem); + UpdateSelection(oldItem, newItem); + } + } } } /// /// Fired when the SelectedItem changes. /// - /// The sender - /// The event args + /// The event args. /// /// Sets up animations for the DetailsPresenter for animating in/out. /// - private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private void OnSelectedItemChanged(DependencyPropertyChangedEventArgs e) { - var view = (ListDetailsView)d; - var index = e.NewValue == null ? -1 : view.Items.IndexOf(e.NewValue); + int index = SelectedItem is null ? -1 : Items.IndexOf(SelectedItem); - // check if selection actually changed - if (view.SelectedIndex != index) + // If there is no selection, do not remove the DetailsPresenter content but let it animate out. + if (index >= 0) { - // sync SelectedIndex - view.SetValue(SelectedIndexProperty, index); - view.UpdateSelection(e.OldValue, e.NewValue); + SetDetailsContent(); } - } - - /// - /// Fired when the is changed. - /// - /// The sender - /// The event args - private static void OnListHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var view = (ListDetailsView)d; - view.SetListHeaderVisibility(); - } - - /// - /// Fired when the DetailsCommandBar changes. - /// - /// The sender - /// The event args - private static void OnDetailsCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var view = (ListDetailsView)d; - view.OnDetailsCommandBarChanged(); - } - - /// - /// Fired when CompactModeThresholdWIdthChanged - /// - /// The sender - /// The event args - private static void OnCompactModeThresholdWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((ListDetailsView)d).HandleStateChanges(); - } - private static void OnBackButtonBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var view = (ListDetailsView)d; - view.SetBackButtonVisibility(); - } + if (SelectedIndex != index) + { + SetValue(SelectedIndexProperty, index); + UpdateSelection(e.OldValue, e.NewValue); + } - /// - /// Fired when the changes. - /// - /// The sender - /// The event args - private static void OnListCommandBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var view = (ListDetailsView)d; - view.OnListCommandBarChanged(); + OnSelectionChanged(new SelectionChangedEventArgs(new List { e.OldValue }, new List { e.NewValue })); + UpdateView(true); } private void OnLoaded(object sender, RoutedEventArgs e) { - if (DesignMode.DesignModeEnabled == false) + if (!DesignMode.DesignModeEnabled) { SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; if (_frame != null) @@ -205,14 +181,6 @@ private void OnLoaded(object sender, RoutedEventArgs e) { _frame.Navigating += OnFrameNavigating; } - - _selectionStateGroup = (VisualStateGroup)GetTemplateChild(SelectionStates); - if (_selectionStateGroup != null) - { - _selectionStateGroup.CurrentStateChanged += OnSelectionStateChanged; - } - - UpdateView(true); } } @@ -235,21 +203,6 @@ private void OnUnloaded(object sender, RoutedEventArgs e) } } - private void ListDetailsView_SizeChanged(object sender, SizeChangedEventArgs e) - { - // if size is changing - if ((e.PreviousSize.Width < CompactModeThresholdWidth && e.NewSize.Width >= CompactModeThresholdWidth) || - (e.PreviousSize.Width >= CompactModeThresholdWidth && e.NewSize.Width < CompactModeThresholdWidth)) - { - HandleStateChanges(); - } - } - - private void OnInlineBackButtonClicked(object sender, RoutedEventArgs e) - { - SelectedItem = null; - } - /// /// Raises SelectionChanged event and updates view. /// @@ -268,45 +221,6 @@ private void UpdateSelection(object oldSelection, object newSelection) } } - private void HandleStateChanges() - { - UpdateView(true); - SetListSelectionWithKeyboardFocusOnVisualStateChanged(ViewState); - } - - /// - /// Closes the details pane if we are in narrow state - /// - /// The sender - /// The event args - private void OnFrameNavigating(object sender, NavigatingCancelEventArgs args) - { - if ((args.NavigationMode == NavigationMode.Back) && (ViewState == ListDetailsViewState.Details)) - { - SelectedItem = null; - args.Cancel = true; - } - } - - /// - /// Closes the details pane if we are in narrow state - /// - /// The sender - /// The event args - private void OnBackRequested(object sender, BackRequestedEventArgs args) - { - if (ViewState == ListDetailsViewState.Details) - { - // let the OnFrameNavigating method handle it if - if (_frame == null || !_frame.CanGoBack) - { - SelectedItem = null; - } - - args.Handled = true; - } - } - private void SetListHeaderVisibility() { if (GetTemplateChild(PartHeaderContentPresenter) is FrameworkElement headerPresenter) @@ -323,88 +237,23 @@ private void UpdateView(bool animate) SetVisualState(animate); } - /// - /// Sets the back button visibility based on the current visual state and selected item - /// - private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) + private void UpdateViewState() { - if (DesignMode.DesignModeEnabled) - { - return; - } + ListDetailsViewState previousState = ViewState; - if (ViewState == ListDetailsViewState.Details) + if (_twoPaneView == null) { - if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) - { - _inlineBackButton.Visibility = Visibility.Visible; - } - else if (BackButtonBehavior == BackButtonBehavior.Automatic) - { - // Continue to support the system back button if it is being used - var navigationManager = SystemNavigationManager.GetForCurrentView(); - if (navigationManager.AppViewBackButtonVisibility == AppViewBackButtonVisibility.Visible) - { - // Setting this indicates that the system back button is being used - _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; - } - else if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) - { - // We can only use the new NavigationView if we also have a Frame - // If there is no frame we have to use the inline button - _inlineBackButton.Visibility = Visibility.Visible; - } - else - { - SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible.Visible, true); - } - } - else if (BackButtonBehavior != BackButtonBehavior.Manual) - { - var navigationManager = SystemNavigationManager.GetForCurrentView(); - _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; - - navigationManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible; - } - } - else if (previousState == ListDetailsViewState.Details) - { - if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) - { - _inlineBackButton.Visibility = Visibility.Collapsed; - } - else if (BackButtonBehavior == BackButtonBehavior.Automatic) - { - if (_previousSystemBackButtonVisibility.HasValue == false) - { - if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) - { - _inlineBackButton.Visibility = Visibility.Collapsed; - } - else - { - SetNavigationViewBackButtonState(_previousNavigationViewBackVisibilty, _previousNavigationViewBackEnabled); - } - } - } - - if (_previousSystemBackButtonVisibility.HasValue) - { - // Make sure we show the back button if the stack can navigate back - SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = _previousSystemBackButtonVisibility.Value; - _previousSystemBackButtonVisibility = null; - } + ViewState = ListDetailsViewState.Both; } - } - - private void UpdateViewState() - { - var previousState = ViewState; - if (ActualWidth < CompactModeThresholdWidth) + // Single pane: + else if (_twoPaneView.Mode == NavigationView.TwoPaneViewMode.SinglePane) { ViewState = SelectedItem == null ? ListDetailsViewState.List : ListDetailsViewState.Details; + _twoPaneView.PanePriority = SelectedItem == null ? NavigationView.TwoPaneViewPriority.Pane1 : NavigationView.TwoPaneViewPriority.Pane2; } + + // Dual pane: else { ViewState = ListDetailsViewState.Both; @@ -419,44 +268,37 @@ private void UpdateViewState() private void SetVisualState(bool animate) { - string state; string noSelectionState; string hasSelectionState; - if (ActualWidth < CompactModeThresholdWidth) - { - state = NarrowState; - noSelectionState = NoSelectionNarrowState; - hasSelectionState = HasSelectionNarrowState; - } - else + if (ViewState == ListDetailsViewState.Both) { - state = WideState; noSelectionState = NoSelectionWideState; hasSelectionState = HasSelectionWideState; } - - VisualStateManager.GoToState(this, state, animate); - VisualStateManager.GoToState(this, SelectedItem == null ? noSelectionState : hasSelectionState, animate); - } - - private void SetNavigationViewBackButtonState(NavigationView.NavigationViewBackButtonVisible visibility, bool enabled) - { - if (_navigationView == null) + else { - return; + noSelectionState = NoSelectionNarrowState; + hasSelectionState = HasSelectionNarrowState; } - _previousNavigationViewBackVisibilty = _navigationView.IsBackButtonVisible; - _navigationView.IsBackButtonVisible = visibility; - - _previousNavigationViewBackEnabled = _navigationView.IsBackEnabled; - _navigationView.IsBackEnabled = enabled; + VisualStateManager.GoToState(this, SelectedItem == null ? noSelectionState : hasSelectionState, animate); + VisualStateManager.GoToState(this, Items.Count > 0 ? HasItemsState : HasNoItemsState, animate); } + /// + /// Sets the content of the based on current function. + /// private void SetDetailsContent() { if (_detailsPresenter != null) { + // Update the content template: + if (_detailsPresenter.ContentTemplateSelector != null) + { + _detailsPresenter.ContentTemplate = _detailsPresenter.ContentTemplateSelector.SelectTemplate(SelectedItem, _detailsPresenter); + } + + // Update the content: _detailsPresenter.Content = MapDetails == null ? SelectedItem : SelectedItem != null ? MapDetails(SelectedItem) : null; @@ -465,12 +307,12 @@ private void SetDetailsContent() private void OnListCommandBarChanged() { - OnCommandBarChanged("ListCommandBarPanel", ListCommandBar); + OnCommandBarChanged("ListCommandBar", ListCommandBar); } private void OnDetailsCommandBarChanged() { - OnCommandBarChanged("DetailsCommandBarPanel", DetailsCommandBar); + OnCommandBarChanged("DetailsCommandBar", DetailsCommandBar); } private void OnCommandBarChanged(string panelName, CommandBar commandbar) @@ -509,7 +351,7 @@ private void SetListSelectionWithKeyboardFocusOnVisualStateChanged(ListDetailsVi /// private void SetListSelectionWithKeyboardFocus(bool singleSelectionFollowsFocus) { - if (GetTemplateChild("List") is Windows.UI.Xaml.Controls.ListViewBase list) + if (GetTemplateChild("List") is ListViewBase list) { list.SingleSelectionFollowsFocus = singleSelectionFollowsFocus; } @@ -563,10 +405,62 @@ private void FocusFirstFocusableElementInDetails() /// private void FocusItemList() { - if (GetTemplateChild("List") is Control list) + if (GetTemplateChild("PartMasterList") is Control list) { list.Focus(FocusState.Programmatic); } } + + /// + /// Fired when the changed its view mode. + /// + /// The sender. + /// The event args. + private void OnModeChanged(NavigationView.TwoPaneView sender, object args) + { + UpdateView(true); + SetListSelectionWithKeyboardFocusOnVisualStateChanged(ViewState); + } + + /// + /// Invoked once the items changed and ensures the visual state is constant. + /// + protected override void OnItemsChanged(object e) + { + base.OnItemsChanged(e); + UpdateView(true); + + if (SelectedIndex < 0) + { + return; + } + + // Ensure we still have the correct index and selected item for the new collection. + // This prevents flickering when the order of the collection changes. + int index = -1; + if (!(Items is null)) + { + index = Items.IndexOf(SelectedItem); + } + + if (index < 0) + { + ClearSelectedItem(); + } + else if (SelectedIndex != index) + { + SetValue(SelectedIndexProperty, index); + } + } + + /// + /// Updates the since it is of type , + /// but is of type . + /// This should be changed in a further release. + /// + private void OnListPaneWidthChanged() + { + _twoPaneView.Pane1Length = new GridLength(ListPaneWidth); + } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml index ebc218ada45..3fb14c4d4c1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml @@ -1,6 +1,7 @@ + xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" + xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> - \ No newline at end of file + diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsViewState.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsViewState.cs index 217511a5045..9a177605713 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsViewState.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsViewState.cs @@ -10,18 +10,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls public enum ListDetailsViewState { /// - /// Only the List view is shown + /// Only the List view is shown- /// List, /// - /// Only the Details view is shown + /// Only the Details view is shown- /// Details, /// - /// Both the List and Details views are shown + /// Both the List and Details views are shown- /// Both } -} \ No newline at end of file +} diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_ListDetailsView.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_ListDetailsView.cs index 13f1e938c76..c0166b6077f 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_ListDetailsView.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_ListDetailsView.cs @@ -5,9 +5,10 @@ using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using System.Collections.ObjectModel; using System.Linq; -namespace UnitTests.UWP.UI.Controls +namespace UnitTests.UI.Controls { [TestClass] public class Test_ListDetailsView @@ -17,8 +18,10 @@ public class Test_ListDetailsView public void Test_SelectedIndex_Default() { var items = Enumerable.Range(1, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; + var listDetailsView = new ListDetailsView + { + ItemsSource = items + }; Assert.AreEqual(-1, listDetailsView.SelectedIndex); } @@ -27,8 +30,10 @@ public void Test_SelectedIndex_Default() public void Test_SelectedItem_Default() { var items = Enumerable.Range(1, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; + var listDetailsView = new ListDetailsView + { + ItemsSource = items + }; Assert.IsNull(listDetailsView.SelectedItem); } @@ -37,9 +42,11 @@ public void Test_SelectedItem_Default() public void Test_SelectedIndex_Syncs_SelectedItem() { var items = Enumerable.Range(1, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; - listDetailsView.SelectedIndex = 6; + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedIndex = 6 + }; Assert.AreEqual(items[6], listDetailsView.SelectedItem); } @@ -48,9 +55,11 @@ public void Test_SelectedIndex_Syncs_SelectedItem() public void Test_UnselectUsingIndex() { var items = Enumerable.Range(1, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; - listDetailsView.SelectedIndex = 5; + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedIndex = 5 + }; listDetailsView.SelectedIndex = -1; Assert.IsNull(listDetailsView.SelectedItem); } @@ -60,9 +69,11 @@ public void Test_UnselectUsingIndex() public void Test_UnselectUsingItem() { var items = Enumerable.Range(1, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; - listDetailsView.SelectedItem = items[5]; + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedItem = items[5] + }; listDetailsView.SelectedItem = null; Assert.AreEqual(-1, listDetailsView.SelectedIndex); } @@ -72,10 +83,57 @@ public void Test_UnselectUsingItem() public void Test_SelectedItem_Syncs_SelectedIndex() { var items = Enumerable.Range(0, 10).ToArray(); - var listDetailsView = new ListDetailsView(); - listDetailsView.ItemsSource = items; - listDetailsView.SelectedItem = items[3]; + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedItem = items[3] + }; Assert.AreEqual(3, listDetailsView.SelectedIndex); } + + [TestCategory("ListDetailsView")] + [UITestMethod] + public void Test_Sorting_Keeps_SelectedIndex() + { + var items = Enumerable.Range(0, 10).ToArray(); + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedItem = items[3] + }; + Assert.AreEqual(3, listDetailsView.SelectedIndex); + } + + [TestCategory("ListDetailsView")] + [UITestMethod] + public void Test_Sorting_Keeps_SelectedItem() + { + var items = new ObservableCollection(Enumerable.Range(0, 10)); + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedIndex = 3 + }; + var item = listDetailsView.SelectedItem; + listDetailsView.ItemsSource = new ObservableCollection(items.OrderByDescending(i => i)); + Assert.AreEqual(item, listDetailsView.SelectedItem); + listDetailsView.ItemsSource = new ObservableCollection(items.OrderBy(i => i)); + Assert.AreEqual(item, listDetailsView.SelectedItem); + } + + [TestCategory("ListDetailsView")] + [UITestMethod] + public void Test_ItemsRemoved() + { + var items = new ObservableCollection(Enumerable.Range(0, 10)); + var listDetailsView = new ListDetailsView + { + ItemsSource = items, + SelectedIndex = 3 + }; + listDetailsView.ItemsSource = null; + Assert.AreEqual(null, listDetailsView.SelectedItem); + Assert.AreEqual(-1, listDetailsView.SelectedIndex); + } } -} \ No newline at end of file +} From e3f5a7f60b24f7caed6fb7945fe4df7ef00ee0c7 Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Sun, 15 Aug 2021 14:43:25 +0200 Subject: [PATCH 024/200] Fixed ambiguous doc hint --- .../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 2dbcc0da1c4..0fbefdeceb3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -454,7 +454,7 @@ protected override void OnItemsChanged(object e) } /// - /// Updates the since it is of type , + /// Updates the since it is of type 'GridLength', /// but is of type . /// This should be changed in a further release. /// From 435ec70088c4153125ab9dda2891b1dc91173878 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Sat, 2 Jan 2021 16:38:32 -0800 Subject: [PATCH 025/200] add RichSuggestBox to repo --- .../PlainTextCommandBarFlyout.cs | 17 + .../RichSuggestBox/RichSuggestBox.Events.cs | 15 + .../RichSuggestBox.Properties.cs | 137 +++++ .../RichSuggestBox/RichSuggestBox.cs | 554 ++++++++++++++++++ .../RichSuggestBox/RichSuggestBox.xaml | 110 ++++ .../SuggestionChosenEventArgs.cs | 20 + .../RichSuggestBox/SuggestionInfo.cs | 59 ++ .../RichSuggestBox/SuggestionTokenFormat.cs | 18 + .../SuggestionsRequestedEventArgs.cs | 11 + 9 files changed, 941 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs new file mode 100644 index 00000000000..7adbc988c2e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/PlainTextCommandBarFlyout.cs @@ -0,0 +1,17 @@ +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + internal class PlainTextCommandBarFlyout : TextCommandBarFlyout + { + public PlainTextCommandBarFlyout() + { + Opening += (sender, o) => + { + PrimaryCommands.Clear(); + + // TODO: Limit Pasting to plain-text only + }; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs new file mode 100644 index 00000000000..73f6229aa30 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Events.cs @@ -0,0 +1,15 @@ +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public partial class RichSuggestBox + { + public event TypedEventHandler SuggestionsRequested; + + public event TypedEventHandler SuggestionChosen; + + 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 new file mode 100644 index 00000000000..b5083430df5 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.Properties.cs @@ -0,0 +1,137 @@ +using System.Collections.ObjectModel; +using Windows.UI.Text; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + public partial class RichSuggestBox + { + public static readonly DependencyProperty PlaceholderTextProperty = + DependencyProperty.Register( + nameof(PlaceholderText), + typeof(string), + typeof(RichSuggestBox), + new PropertyMetadata(string.Empty)); + + public static readonly DependencyProperty RichEditBoxStyleProperty = + DependencyProperty.Register( + nameof(RichEditBoxStyle), + typeof(Style), + typeof(RichSuggestBox), + new PropertyMetadata(null)); + + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register( + nameof(Header), + typeof(object), + typeof(RichSuggestBox), + new PropertyMetadata(null)); + + public static readonly DependencyProperty DescriptionProperty = + DependencyProperty.Register( + nameof(Description), + typeof(object), + typeof(RichSuggestBox), + new PropertyMetadata(null)); + + public static readonly DependencyProperty TextWrappingProperty = + DependencyProperty.Register( + nameof(TextWrapping), + typeof(TextWrapping), + typeof(RichSuggestBox), + new PropertyMetadata(TextWrapping.NoWrap)); + + public static readonly DependencyProperty ClipboardCopyFormatProperty = + DependencyProperty.Register( + nameof(ClipboardCopyFormat), + typeof(RichEditClipboardFormat), + typeof(RichSuggestBox), + new PropertyMetadata(RichEditClipboardFormat.PlainText)); + + public static readonly DependencyProperty SuggestionBackgroundProperty = + DependencyProperty.Register( + nameof(SuggestionBackground), + typeof(SolidColorBrush), + typeof(RichSuggestBox), + new PropertyMetadata(null)); + + public static readonly DependencyProperty SuggestionForegroundProperty = + DependencyProperty.Register( + nameof(SuggestionForeground), + typeof(SolidColorBrush), + typeof(RichSuggestBox), + new PropertyMetadata(null)); + + public static readonly DependencyProperty PrefixesProperty = + DependencyProperty.Register( + nameof(Prefixes), + typeof(string), + typeof(RichSuggestBox), + new PropertyMetadata("@", OnPrefixesChanged)); + + public string PlaceholderText + { + get => (string)GetValue(PlaceholderTextProperty); + set => SetValue(PlaceholderTextProperty, value); + } + + public Style RichEditBoxStyle + { + get => (Style)GetValue(RichEditBoxStyleProperty); + set => SetValue(RichEditBoxStyleProperty, value); + } + + public object Header + { + get => GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + public object Description + { + get => GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); + } + + public TextWrapping TextWrapping + { + get => (TextWrapping)GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); + } + + public RichEditClipboardFormat ClipboardCopyFormat + { + get => (RichEditClipboardFormat)GetValue(ClipboardCopyFormatProperty); + set => SetValue(ClipboardCopyFormatProperty, value); + } + + public SolidColorBrush SuggestionBackground + { + get => (SolidColorBrush)GetValue(SuggestionBackgroundProperty); + set => SetValue(SuggestionBackgroundProperty, value); + } + + public SolidColorBrush SuggestionForeground + { + get => (SolidColorBrush)GetValue(SuggestionForegroundProperty); + set => SetValue(SuggestionForegroundProperty, value); + } + + public string Prefixes + { + get => (string)GetValue(PrefixesProperty); + set => SetValue(PrefixesProperty, value); + } + + /// + /// Gets object used for lock + /// + protected object LockObj { get; } + + public RichEditTextDocument TextDocument => _richEditBox?.TextDocument; + + public ReadOnlyObservableCollection Tokens { get; } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs new file mode 100644 index 00000000000..bd00ece615f --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -0,0 +1,554 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Toolkit.Uwp.Deferred; +using Windows.System; +using Windows.UI.Text; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Input; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + [TemplatePart(Name = PartRichEditBox, Type = typeof(RichEditBox))] + [TemplatePart(Name = PartSuggestionsPopup, Type = typeof(Popup))] + [TemplatePart(Name = PartSuggestionsList, Type = typeof(ListViewBase))] + public partial class RichSuggestBox : ItemsControl + { + private const string PartRichEditBox = "RichEditBox"; + private const string PartSuggestionsPopup = "SuggestionsPopup"; + private const string PartSuggestionsList = "SuggestionsList"; + + private Popup _suggestionPopup; + private RichEditBox _richEditBox; + private ListViewBase _suggestionsList; + + private int _suggestionChoice; + private string _currentQuery; + private string _currentPrefix; + private bool _ignoreChange; + private ITextRange _currentRange; + private CancellationTokenSource _suggestionRequestedTokenSource; + private readonly Dictionary _tokens; + private readonly ObservableCollection _visibleTokens; + + /// + /// Initializes a new instance of the class. + /// + public RichSuggestBox() + { + _tokens = new Dictionary(); + _visibleTokens = new ObservableCollection(); + Tokens = new ReadOnlyObservableCollection(_visibleTokens); + LockObj = new object(); + + DefaultStyleKey = typeof(RichSuggestBox); + + RegisterPropertyChangedCallback(ItemsSourceProperty, ItemsSource_PropertyChanged); + } + + public void ClearUndoRedoSuggestionHistory() + { + TextDocument.ClearUndoRedoHistory(); + if (_tokens.Count == 0) + { + return; + } + + var keysToDelete = _tokens.Where(pair => !pair.Value.Active).Select(pair => pair.Key).ToArray(); + foreach (var key in keysToDelete) + { + _tokens.Remove(key); + } + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _suggestionPopup = (Popup)GetTemplateChild(PartSuggestionsPopup); + _richEditBox = (RichEditBox)GetTemplateChild(PartRichEditBox); + _suggestionsList = (ListViewBase)GetTemplateChild(PartSuggestionsList); + + _richEditBox.SizeChanged += RichEditBox_SizeChanged; + _richEditBox.TextChanging += RichEditBox_TextChanging; + _richEditBox.TextChanged += RichEditBox_TextChanged; + _richEditBox.SelectionChanging += RichEditBox_SelectionChanging; + _richEditBox.SelectionChanged += RichEditBox_SelectionChanged; + _richEditBox.AddHandler(PointerPressedEvent, new PointerEventHandler(RichEditBoxPointerEventHandler), true); + AddKeyboardAccelerators(); + _suggestionsList.ItemClick += SuggestionsList_ItemClick; + + _suggestionsList.GotFocus += (sender, args) => _richEditBox.Focus(FocusState.Programmatic); + LostFocus += (sender, args) => ShowSuggestionsPopup(false); + } + + private static void OnPrefixesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var view = (RichSuggestBox)d; + + var newValue = (string)e.NewValue; + var prefixes = EnforcePrefixesRequirements(newValue); + + if (newValue != prefixes) + { + view.SetValue(PrefixesProperty, prefixes); + } + } + + private static string EnforcePrefixesRequirements(string value) + { + if (string.IsNullOrEmpty(value)) + { + return "@"; + } + + var possibles = string.Concat(value.Where(char.IsPunctuation)); + return string.IsNullOrEmpty(possibles) ? "@" : possibles; + } + + private void RichEditBox_SelectionChanging(RichEditBox sender, RichEditBoxSelectionChangingEventArgs args) + { + TextDocument.BeginUndoGroup(); + + var selection = TextDocument.Selection; + if (selection.Type != SelectionType.InsertionPoint) + { + return; + } + + var range = selection.GetClone(); + range.Expand(TextRangeUnit.Link); + if (!_tokens.ContainsKey(range.Link)) + { + return; + } + + if (range.StartPosition < selection.StartPosition && selection.EndPosition < range.EndPosition) + { + // Prevent user from manually editing the link + selection.SetRange(range.StartPosition, range.EndPosition); + } + else if (selection.StartPosition == range.StartPosition) + { + // Reset formatting if selection is sandwiched between 2 adjacent links + // or if the link is at the beginning of the document + range.MoveStart(TextRangeUnit.Link, -1); + if (selection.StartPosition != range.StartPosition || selection.StartPosition == 0) + { + ApplyDefaultFormatToRange(selection); + } + } + } + + private async void RichEditBox_SelectionChanged(object sender, RoutedEventArgs e) + { + await RequestForSuggestionsAsync(); + } + + private void RichEditBoxPointerEventHandler(object sender, PointerRoutedEventArgs e) + { + ShowSuggestionsPopup(false); + } + + private void AddKeyboardAccelerators() + { + var enterKeyAccelerator = new KeyboardAccelerator { Key = VirtualKey.Enter }; + var downKeyAccelerator = new KeyboardAccelerator { Key = VirtualKey.Down }; + var upKeyAccelerator = new KeyboardAccelerator { Key = VirtualKey.Up }; + var escapeKeyAccelerator = new KeyboardAccelerator { Key = VirtualKey.Escape }; + + enterKeyAccelerator.Invoked += RichEditBoxKeyboardAccelerator_Invoked; + downKeyAccelerator.Invoked += RichEditBoxKeyboardAccelerator_Invoked; + upKeyAccelerator.Invoked += RichEditBoxKeyboardAccelerator_Invoked; + escapeKeyAccelerator.Invoked += RichEditBoxKeyboardAccelerator_Invoked; + + _richEditBox.KeyboardAccelerators.Add(enterKeyAccelerator); + _richEditBox.KeyboardAccelerators.Add(downKeyAccelerator); + _richEditBox.KeyboardAccelerators.Add(upKeyAccelerator); + _richEditBox.KeyboardAccelerators.Add(escapeKeyAccelerator); + } + + private void RichEditBoxKeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) + { + var itemsList = _suggestionsList.Items; + if (!_suggestionPopup.IsOpen || itemsList == null || itemsList.Count == 0) + { + return; + } + + var key = args.KeyboardAccelerator.Key; + switch (key) + { + case VirtualKey.Up when itemsList.Count == 1: + case VirtualKey.Down when itemsList.Count == 1: + { + _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; + } + case VirtualKey.Down: + { + _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; + } + case VirtualKey.Escape: + { + ShowSuggestionsPopup(false); + args.Handled = true; + break; + } + } + } + + private async void SuggestionsList_ItemClick(object sender, ItemClickEventArgs e) + { + var selectedItem = e.ClickedItem; + await OnSuggestionSelectedAsync(selectedItem); + } + + private void RichEditBox_TextChanging(RichEditBox sender, RichEditBoxTextChangingEventArgs args) + { + if (_ignoreChange) + { + return; + } + + _ignoreChange = true; + ValidateTokensInDocument(); + TextDocument.EndUndoGroup(); + TextDocument.BeginUndoGroup(); + _ignoreChange = false; + } + + private void RichEditBox_TextChanged(object sender, RoutedEventArgs e) + { + TextChanged?.Invoke((RichEditBox)sender, e); + UpdateVisibleTokenList(); + } + + private void RichEditBox_SizeChanged(object sender, SizeChangedEventArgs e) + { + _suggestionPopup.VerticalOffset = e.NewSize.Height; + _suggestionsList.MaxWidth = e.NewSize.Width; + } + + private void ItemsSource_PropertyChanged(DependencyObject sender, DependencyProperty dp) + { + _suggestionChoice = 0; + ShowSuggestionsPopup(ItemsSource is IEnumerable); + } + + private async Task RequestForSuggestionsAsync() + { + _suggestionRequestedTokenSource?.Cancel(); + + if (TryExtractQueryFromSelection(out var prefix, out var query, out var range) && + SuggestionsRequested != null) + { + _suggestionRequestedTokenSource = new CancellationTokenSource(); + _currentPrefix = prefix; + _currentQuery = query; + _currentRange = range; + + var cancellationToken = _suggestionRequestedTokenSource.Token; + var eventArgs = new SuggestionsRequestedEventArgs { Query = query, Prefix = prefix }; + try + { + await SuggestionsRequested.InvokeAsync(this, eventArgs, cancellationToken); + } + catch (TaskCanceledException) + { + eventArgs.Cancel = true; + } + } + else + { + ShowSuggestionsPopup(false); + } + } + + private async Task OnSuggestionSelectedAsync(object selectedItem) + { + var range = _currentRange; + var id = Guid.NewGuid(); + var prefix = _currentPrefix; + var query = _currentQuery; + + // range has length of 0 at the end of the commit. + // Checking length == 0 to avoid committing twice. + if (SuggestionChosen == null || prefix == null || query == null || range == null || + range.Length == 0) + { + return; + } + + var eventArgs = new SuggestionChosenEventArgs + { + Id = id, + Prefix = prefix, + Query = query, + SelectedItem = selectedItem, + Format = CreateSuggestionTokenFormat() + }; + var textBefore = range.Text; + await SuggestionChosen.InvokeAsync(this, eventArgs); + var text = eventArgs.Text; + + // Since this operation is async, the document may have changed at this point. + // Double check if the range still has the expected query. + if (string.IsNullOrEmpty(text) || textBefore != range.Text || + !TryExtractQueryFromRange(range, out var testPrefix, out var testQuery) || + testPrefix != prefix || testQuery != query) + { + return; + } + + lock (LockObj) + { + var displayText = prefix + text; + var tokenRange = CommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format); + + var token = new SuggestionInfo(id, displayText, this) { Active = true, Item = selectedItem }; + token.UpdateTextRange(tokenRange); + _tokens.Add(tokenRange.Link, token); + } + } + + private ITextRange CommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, SuggestionTokenFormat format) + { + _ignoreChange = true; + TextDocument.BeginUndoGroup(); + + range.SetText(TextSetOptions.Unhide, displayText); + range.Link = $"\"{id}\""; + + range.CharacterFormat.BackgroundColor = format.Background; + range.CharacterFormat.ForegroundColor = format.Foreground; + range.CharacterFormat.Bold = format.Bold; + range.CharacterFormat.Italic = format.Italic; + range.CharacterFormat.Underline = format.Underline; + + var returnRange = TextDocument.GetRange(range.StartPosition, range.EndPosition); + + range.Collapse(false); + range.SetText(TextSetOptions.Unhide, " "); + range.Collapse(false); + TextDocument.Selection.SetRange(range.EndPosition, range.EndPosition); + + TextDocument.EndUndoGroup(); + _ignoreChange = false; + return returnRange; + } + + private void ValidateTokensInDocument() + { + foreach (var (_, token) in _tokens) + { + token.Active = false; + } + + var range = TextDocument.GetRange(0, 0); + range.SetIndex(TextRangeUnit.Character, -1, false); + + // Handle link at the very end of the document where GetIndex fails to detect + range.Expand(TextRangeUnit.Link); + ValidateTokenFromRange(range); + + var nextIndex = range.GetIndex(TextRangeUnit.Link); + while (nextIndex != 0 && nextIndex != 1) + { + range.Move(TextRangeUnit.Link, -1); + + var validateRange = range.GetClone(); + validateRange.Expand(TextRangeUnit.Link); + + // Adjacent links have the same index. Manually check each link with Collapse and Expand. + var previousText = validateRange.Text; + var hasAdjacentToken = true; + while (hasAdjacentToken) + { + ValidateTokenFromRange(validateRange); + validateRange.Collapse(false); + validateRange.Expand(TextRangeUnit.Link); + + hasAdjacentToken = validateRange.Text != previousText; + previousText = validateRange.Text; + } + + nextIndex = range.GetIndex(TextRangeUnit.Link); + } + } + + private bool ValidateTokenFromRange(ITextRange range) + { + if (range.Length == 0 || string.IsNullOrEmpty(range.Link) || + !_tokens.TryGetValue(range.Link, out var token)) + { + // Handle case where range.Link is empty but it still recognized and rendered as a link + if (range.CharacterFormat.LinkType == LinkType.FriendlyLinkName) + { + range.Link = string.Empty; + } + return false; + } + + if (token.ToString() != range.Text) + { + //range.Link = string.Empty; + range.CharacterFormat = TextDocument.GetDefaultCharacterFormat(); + token.Active = false; + return false; + } + + token.UpdateTextRange(range); + token.Active = true; + return true; + } + + private void ShowSuggestionsPopup(bool show) + { + _suggestionPopup.IsOpen = show; + if (!show) + { + _suggestionChoice = 0; + } + } + + private bool TryExtractQueryFromSelection(out string prefix, out string query, out ITextRange range) + { + prefix = string.Empty; + query = string.Empty; + range = null; + if (TextDocument.Selection.Type != SelectionType.InsertionPoint) + { + return false; + } + + // Check if selection is on existing link (suggestion) + var expandCount = TextDocument.Selection.GetClone().Expand(TextRangeUnit.Link); + if (expandCount != 0) + { + return false; + } + + var selection = TextDocument.Selection.GetClone(); + selection.MoveStart(TextRangeUnit.Word, -1); + if (selection.Length == 0) + { + return false; + } + + range = selection; + if (TryExtractQueryFromRange(selection, out prefix, out query)) + { + return true; + } + + selection.MoveStart(TextRangeUnit.Word, -1); + if (TryExtractQueryFromRange(selection, out prefix, out query)) + { + return true; + } + + range = null; + return false; + } + + private bool TryExtractQueryFromRange(ITextRange range, out string prefix, out string query) + { + prefix = string.Empty; + query = string.Empty; + range.GetText(TextGetOptions.NoHidden, out var possibleQuery); + if (possibleQuery.Length > 0 && Prefixes.Contains(possibleQuery[0]) && + !possibleQuery.Any(char.IsWhiteSpace) && string.IsNullOrEmpty(range.Link)) + { + if (possibleQuery.Length == 1) + { + prefix = possibleQuery; + return true; + } + + prefix = possibleQuery[0].ToString(); + query = possibleQuery.Substring(1); + return true; + } + + return false; + } + + private SuggestionTokenFormat CreateSuggestionTokenFormat() + { + var defaultFormat = TextDocument.GetDefaultCharacterFormat(); + if (SuggestionBackground != null) + { + defaultFormat.BackgroundColor = SuggestionBackground.Color; + } + + if (SuggestionForeground != null) + { + defaultFormat.ForegroundColor = SuggestionForeground.Color; + } + + return new SuggestionTokenFormat + { + Foreground = defaultFormat.ForegroundColor, + Background = defaultFormat.BackgroundColor, + Italic = defaultFormat.Italic, + Bold = defaultFormat.Bold, + Underline = defaultFormat.Underline + }; + } + + private void ApplyDefaultFormatToRange(ITextRange range) + { + var defaultFormat = TextDocument.GetDefaultCharacterFormat(); + range.CharacterFormat.BackgroundColor = defaultFormat.BackgroundColor; + range.CharacterFormat.ForegroundColor = defaultFormat.ForegroundColor; + range.CharacterFormat.Bold = defaultFormat.Bold; + range.CharacterFormat.Italic = defaultFormat.Italic; + range.CharacterFormat.Underline = defaultFormat.Underline; + } + + private void UpdateVisibleTokenList() + { + lock (LockObj) + { + var toBeRemoved = _visibleTokens.Where(x => !x.Active).ToArray(); + + foreach (var elem in toBeRemoved) + { + _visibleTokens.Remove(elem); + } + + var toBeAdded = _tokens.Where(pair => pair.Value.Active && !_visibleTokens.Contains(pair.Value)) + .Select(pair => pair.Value).ToArray(); + + foreach (var elem in toBeAdded) + { + _visibleTokens.Add(elem); + } + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml new file mode 100644 index 00000000000..5f3cb590dd6 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.xaml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + 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 00000000000..6ba680e6a3b --- /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 00000000000..9893cde2ae5 --- /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 00000000000..61d7ad16720 --- /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 00000000000..def23b9ec13 --- /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 026/200] 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 7adbc988c2e..342514f5298 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 73f6229aa30..8b735c3935f 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 b5083430df5..882ac0138e5 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 bd00ece615f..dd87d688355 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 6ba680e6a3b..1b10a12a9e5 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 9893cde2ae5..f0332dc7176 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 61d7ad16720..ebb1b48f99c 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 def23b9ec13..c9f7a71d43c 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 027/200] 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 8b735c3935f..3351c3d81c5 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 @@ 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 882ac0138e5..59b499e9e4f 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 @@ 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 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(string.Empty)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty RichEditBoxStyleProperty = DependencyProperty.Register( nameof(RichEditBoxStyle), @@ -26,6 +35,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( nameof(Header), @@ -33,6 +45,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( nameof(Description), @@ -40,6 +55,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register( nameof(TextWrapping), @@ -47,6 +65,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(TextWrapping.NoWrap)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty ClipboardCopyFormatProperty = DependencyProperty.Register( nameof(ClipboardCopyFormat), @@ -54,6 +75,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(RichEditClipboardFormat.PlainText)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty SuggestionBackgroundProperty = DependencyProperty.Register( nameof(SuggestionBackground), @@ -61,6 +85,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty SuggestionForegroundProperty = DependencyProperty.Register( nameof(SuggestionForeground), @@ -68,6 +95,9 @@ public partial class RichSuggestBox typeof(RichSuggestBox), new PropertyMetadata(null)); + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty PrefixesProperty = DependencyProperty.Register( nameof(Prefixes), @@ -75,54 +105,81 @@ public partial class RichSuggestBox 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 @@ public string Prefixes /// 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 dd87d688355..3d311912f8b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -19,6 +19,9 @@ 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 @@ public RichSuggestBox() 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 @@ public void ClearUndoRedoSuggestionHistory() } } + /// 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 1b10a12a9e5..3ab5f5a80c0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionChosenEventArgs.cs @@ -7,18 +7,39 @@ 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 f0332dc7176..823c8def124 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionInfo.cs @@ -8,28 +8,59 @@ 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 @@ internal void UpdateTextRange(ITextRange range) } } + /// 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 ebb1b48f99c..588076f562f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionTokenFormat.cs @@ -7,16 +7,34 @@ 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 c9f7a71d43c..60e358b2ff9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/SuggestionsRequestedEventArgs.cs @@ -6,10 +6,19 @@ 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 028/200] 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 3d311912f8b..ca8b9738e32 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -199,37 +199,31 @@ private void RichEditBoxKeyboardAccelerator_Invoked(KeyboardAccelerator sender, { 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 029/200] 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 ca8b9738e32..55d8a2850ba 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/RichSuggestBox/RichSuggestBox.cs @@ -31,6 +31,9 @@ public partial class RichSuggestBox : ItemsControl 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 @@ public partial class RichSuggestBox : ItemsControl 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 @@ private bool ValidateTokenFromRange(ITextRange range) { 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 030/200] 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 5f3cb590dd6..44065225f1c 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 0c6c06a95a6..00000000000 --- 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 175499746cd..678a9eaf17d 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 780f14479e8..5eddad6e0fa 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 2447ae36db2..4cbc804ec09 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 142/200] 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 4cbc804ec09..426164933d6 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 143/200] 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 60efaed47aa..f398a422b89 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 92950a93362..c9687d04160 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 @@ public sealed class AttachedShadowElementContext 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 @@ public sealed class AttachedShadowElementContext 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 @@ public sealed class AttachedShadowElementContext 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 f3adc98848d..3ea72a1f3d1 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 144/200] 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 5deb64afc8b..aceb04473cd 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 746cc7f8451..71d09734992 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 145/200] 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 00000000000..2c4dd68e623 --- /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 51526c86135..e992f56166c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/PropertyControl.xaml.cs @@ -179,6 +179,14 @@ private void PropertyControl_OnDataContextChanged(FrameworkElement sender, DataC 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 0180bf898a5..37e339ff9e3 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 fcb07035933..f45876a6aae 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/PropertyDescriptor/PropertyKind.cs @@ -13,6 +13,7 @@ public enum PropertyKind 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 11b5325b1c1..00000000000 --- 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 6e512819c70..e26a5076895 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -418,8 +418,12 @@ public string UpdatedXamlCode { 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 @@ public async Task PreparePropertyDescriptorAsync() 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 7f4982ff932..0aeadc70d80 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs @@ -95,6 +95,10 @@ public static Vector3 ToVector3(this string text) return new(x, y, z); } } + else if (values.Length == 2) + { + return new(text.ToVector2(), 0); + } } } @@ -140,6 +144,14 @@ public static Vector4 ToVector4(this string text) 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 146/200] 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 37e339ff9e3..adcd8ef2a48 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 00000000000..4e8ceb2cccf --- /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 5eddad6e0fa..7aac9725731 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 426164933d6..4756dff8571 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 0aeadc70d80..68c5a6cfbbe 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/StringExtensions.cs @@ -26,7 +26,11 @@ public static class StringExtensions [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 @@ public static Vector2 ToVector2(this string text) [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 @@ public static Vector3 ToVector3(this string text) [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 @@ public static Vector4 ToVector4(this string text) [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 f398a422b89..2d8e2ccd682 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 @@ private static void OnShadowChanged(DependencyObject d, DependencyPropertyChange 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 @@ public double Opacity } /// - /// 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 @@ public Color Color /// protected internal abstract bool SupportsOnSizeChangedEvent { get; } - private void ConnectElement(FrameworkElement element) + internal void ConnectElement(FrameworkElement element) { if (!IsSupported) { @@ -158,7 +114,7 @@ private void ConnectElement(FrameworkElement element) ShadowElementContextTable.Add(element, context); } - private void DisconnectElement(FrameworkElement element) + internal void DisconnectElement(FrameworkElement element) { if (ShadowElementContextTable == null) { @@ -308,7 +264,7 @@ protected virtual void OnPropertyChanged(AttachedShadowElementContext context, D } 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 00000000000..2755dbb797f --- /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 147/200] 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 4756dff8571..9689141122c 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 aceb04473cd..8c57b8a9c1b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -17,7 +17,6 @@ public class AttachedCardShadow : AttachedShadowBase 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 @@ public class AttachedCardShadow : AttachedShadowBase 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 @@ public double CornerRadius 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 @@ protected override void OnPropertyChanged(AttachedShadowElementContext context, } } + /// protected override CompositionBrush GetShadowMask(AttachedShadowElementContext context) { if (!SupportsCompositionVisualSurface) @@ -115,6 +119,7 @@ protected override CompositionBrush GetShadowMask(AttachedShadowElementContext c return surfaceBrush; } + /// protected override CompositionClip GetShadowClip(AttachedShadowElementContext context) { var pathGeom = context.GetResource(PathGeometryResourceKey) ?? @@ -141,6 +146,7 @@ protected override CompositionClip GetShadowClip(AttachedShadowElementContext co 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 2d8e2ccd682..685fca8ac72 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -46,7 +46,7 @@ public abstract class AttachedShadowBase : DependencyObject 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 @@ public abstract class AttachedShadowBase : DependencyObject 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 @@ public Color Color } /// - /// 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 @@ protected internal virtual void OnElementContextUninitialized(AttachedShadowElem /// /// 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 c9687d04160..ae17bdf1706 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -279,6 +279,7 @@ public void ClearAndDisposeResources() { (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 148/200] 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 4e8ceb2cccf..4f3679a3e23 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 7aac9725731..0741b8a8f7f 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 2755dbb797f..d7af17ae7d0 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 149/200] 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 4f3679a3e23..04e4a8ffdfc 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 8c57b8a9c1b..2a937a58c33 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Shadows/AttachedCardShadow.cs @@ -16,7 +16,6 @@ public class AttachedCardShadow : AttachedShadowBase { 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 @@ public class AttachedCardShadow : AttachedShadowBase 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 @@ public double CornerRadius /// 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 00000000000..235e60cbf02 --- /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 685fca8ac72..70d7db7245e 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 @@ public abstract class AttachedShadowBase : DependencyObject /// /// 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 @@ protected internal virtual void OnElementContextInitialized(AttachedShadowElemen /// 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 ae17bdf1706..c9cd0d02e17 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -275,12 +275,15 @@ public T RemoveAndDisposeResource(string key) where T : IDisposable /// 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 dbd6be7e8ba..011560e8201 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 af0a413ad0d..e8c393bc779 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 @@ public static Rect ToRect(this Point point, Size size) { 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 152/200] 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 05b7f2d9e8a..9ce613af59a 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 1a0f0eb9081..faa19146253 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 b9d3571a05b..94d361ddf36 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 c9a678455d8..f0c8420c92b 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 @@ public TShadow? Target /// 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 @@ static AnimationBuilder ThrowArgumentNullException() 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; + } + 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); - return builder.ExternalAnimation(shadow, animation); + 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 c3631c9d945..39472358cb0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs @@ -119,7 +119,15 @@ public async Task StartAsync(UIElement element, CancellationToken token) { 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 @@ public async Task StartAsync(UIElement element, CancellationToken token) { 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 00000000000..ea4339a2cf2 --- /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 092ff4b9114..12544e0af99 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 9c37718e99b..4fcd561cccf 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 0ed6bbb2df9..827dc55560b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs @@ -58,13 +58,19 @@ public object Execute(object sender, object parameter) 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 b85bfc94120..b4a7e0f5e26 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs @@ -58,13 +58,19 @@ public object Execute(object sender, object parameter) 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 235e60cbf02..6df8015e39e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedDropShadow.cs @@ -123,22 +123,22 @@ private static void OnCastToPropertyChanged(DependencyObject d, DependencyProper // 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 @@ 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) + 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 70d7db7245e..11e981f5a07 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 @@ static AttachedShadowBase() /// /// 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 @@ public AttachedShadowElementContext GetElementContext(FrameworkElement element) 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 154/200] 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 2a937a58c33..e0643ae088e 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 @@ protected override CompositionBrush GetShadowMask(AttachedShadowElementContext c /// 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 71d09734992..00000000000 --- 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 6df8015e39e..d472a6a1d27 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 @@ public class AttachedDropShadow : AttachedShadowBase /// 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 @@ private void CustomMaskedElement_Loaded(object sender, RoutedEventArgs e) 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 @@ protected override void OnPropertyChanged(AttachedShadowElementContext context, } 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 11e981f5a07..bcd2d405cd0 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 @@ static AttachedShadowBase() /// 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 @@ public Color Color /// 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 @@ protected internal virtual void OnElementContextUninitialized(AttachedShadowElem 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 @@ public AttachedShadowElementContext GetElementContext(FrameworkElement element) 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 @@ protected virtual void SetElementChildVisual(AttachedShadowElementContext contex 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 @@ private void CallPropertyChangedForEachElement(DependencyProperty property, obje /// /// 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 @@ protected virtual CompositionBrush GetShadowMask(AttachedShadowElementContext co /// /// 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 c9cd0d02e17..07116f4c3d2 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -11,6 +11,9 @@ 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 @@ public sealed class AttachedShadowElementContext /// 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 @@ private void OnElementSizeChanged(object sender, SizeChangedEventArgs e) /// /// 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 @@ public T AddResource(string key, T resource) /// /// 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 @@ public bool TryGetResource(string key, out T resource) /// /// 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 @@ public T GetResource(string key) /// /// 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 @@ public T RemoveResource(string key) /// /// 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 @@ public T RemoveAndDisposeResource(string key) where T : IDisposable /// /// 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 d7af17ae7d0..5cb2235c60f 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 @@ private static void OnShadowChanged(DependencyObject d, DependencyPropertyChange newShadow.ConnectElement(element); } } - } } diff --git a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs index 3ea72a1f3d1..afababd92a4 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 @@ 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 9fc439fbe8d..9e46ed107ce 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 155/200] 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 1c39dc489f6..a0df1395c79 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 f0c8420c92b..b65654bd4bd 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 @@ 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 @@ public TShadow? Target /// public static readonly DependencyProperty TargetProperty = DependencyProperty.Register( nameof(Target), - typeof(TShadow), - typeof(ShadowAnimation), + typeof(IAttachedShadow), + typeof(ShadowAnimation), new PropertyMetadata(null)); /// @@ -63,7 +61,7 @@ static AnimationBuilder ThrowArgumentNullException() 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 00000000000..4949ef5686a --- /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 00000000000..68de1449a0f --- /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 4fcd561cccf..0e8f586ebf2 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 00000000000..61ff03941db --- /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 156/200] 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 e2e031865b6..dc8d7cbfdac 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 157/200] 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 8ed73b68686..04b4ad5e321 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_StringExtensions.cs @@ -59,7 +59,15 @@ public void Test_StringExtensions_ToVector2_XYZW(float x, float y) [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 @@ public void Test_StringExtensions_ToVector3_XYZW(float x, float y, float z) [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 @@ public void Test_StringExtensions_ToVector4_XYZW(float x, float y, float z, floa [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 @@ public void Test_StringExtensions_ToQuaternion_XYZW(float x, float y, float z, f [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 158/200] 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 f7a662d2a3f..20251c74c32 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 @@ public RichSuggestBoxPage() 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 3f01fe4383a..44b49d697db 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 @@ public partial class RichSuggestBox { 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 @@ private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 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 159/200] 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 44b49d697db..74a185d75cf 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 @@ public partial class RichSuggestBox { 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 @@ private static bool IsElementOnScreen(FrameworkElement element, double offsetX = 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 160/200] 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 74a185d75cf..ff89f727b7f 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 @@ public partial class RichSuggestBox 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 367bfa3e105..6e9a1e66ec5 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 @@ public static bool IntersectsWith(this Rect rect1, Rect rect2) (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 161/200] 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 ff89f727b7f..0357e261b4b 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.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 @@ private static bool IsElementOnScreen(FrameworkElement element, double offsetX = // 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 @@ private static bool IsElementInsideWindow(FrameworkElement element, double offse 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 88af510dcf40ec77ace1e811ebb8da604682d010 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 27 Aug 2021 16:31:35 +0200 Subject: [PATCH 162/200] Fix MVVM Toolkit source generator tests --- .../Test_SourceGeneratorsDiagnostics.cs | 3 +++ .../UnitTests.SourceGenerators.csproj | 1 + 2 files changed, 4 insertions(+) diff --git a/UnitTests/UnitTests.SourceGenerators/Test_SourceGeneratorsDiagnostics.cs b/UnitTests/UnitTests.SourceGenerators/Test_SourceGeneratorsDiagnostics.cs index 1df3ac3ab44..36bdb455eb7 100644 --- a/UnitTests/UnitTests.SourceGenerators/Test_SourceGeneratorsDiagnostics.cs +++ b/UnitTests/UnitTests.SourceGenerators/Test_SourceGeneratorsDiagnostics.cs @@ -9,6 +9,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Toolkit.Mvvm.ComponentModel; using Microsoft.Toolkit.Mvvm.SourceGenerators; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -249,6 +250,7 @@ public partial class SampleViewModel private void VerifyGeneratedDiagnostics(string source, params string[] diagnosticsIds) where TGenerator : class, ISourceGenerator, new() { + Type observableObjectType = typeof(ObservableObject); Type validationAttributeType = typeof(ValidationAttribute); SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source); @@ -271,6 +273,7 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies() Assert.IsTrue(resultingIds.SetEquals(diagnosticsIds)); + GC.KeepAlive(observableObjectType); GC.KeepAlive(validationAttributeType); } } diff --git a/UnitTests/UnitTests.SourceGenerators/UnitTests.SourceGenerators.csproj b/UnitTests/UnitTests.SourceGenerators/UnitTests.SourceGenerators.csproj index 6fe4ab2d352..81fd8f291e2 100644 --- a/UnitTests/UnitTests.SourceGenerators/UnitTests.SourceGenerators.csproj +++ b/UnitTests/UnitTests.SourceGenerators/UnitTests.SourceGenerators.csproj @@ -15,6 +15,7 @@ + From 33bdd300944c982b8c55f0ddc07bd045f7a99396 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 27 Aug 2021 16:33:17 +0200 Subject: [PATCH 163/200] Enable source generator tests in CI --- build/build.cake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build/build.cake b/build/build.cake index 585ad201e90..f4f8558189a 100644 --- a/build/build.cake +++ b/build/build.cake @@ -270,6 +270,18 @@ Task("Test") ArgumentCustomization = arg => arg.Append($"-s {baseDir}/.runsettings /p:Platform=AnyCPU"), }; DotNetCoreTest(file.FullPath, testSettings); +}).DoesForEach(GetFiles(baseDir + "/**/UnitTests.SourceGenerators.csproj"), (file) => +{ + Information("\nRunning NetCore Source Generator Unit Tests"); + var testSettings = new DotNetCoreTestSettings + { + Configuration = configuration, + NoBuild = true, + Loggers = new[] { "trx;LogFilePrefix=VsTestResults" }, + Verbosity = DotNetCoreVerbosity.Normal, + ArgumentCustomization = arg => arg.Append($"-s {baseDir}/.runsettings /p:Platform=AnyCPU"), + }; + DotNetCoreTest(file.FullPath, testSettings); }).DeferOnError(); Task("UITest") 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 164/200] 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 1d5fd2c52d6..b594d25096b 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 165/200] 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 2c4dd68e623..2aecfcbba28 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Common/Vector3Converter.cs @@ -35,7 +35,7 @@ public object ConvertBack(object value, Type targetType, object parameter, strin { 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 166/200] 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 bcd2d405cd0..5c2e5a0d51f 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -22,12 +22,7 @@ public abstract class AttachedShadowBase : DependencyObject, IAttachedShadow /// /// 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 167/200] 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 b65654bd4bd..c7ab02d2af2 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 @@ static AnimationBuilder ThrowArgumentNullException() 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 e0643ae088e..2373f7adac3 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 @@ public double CornerRadius 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 @@ protected override CompositionClip GetShadowClip(AttachedShadowElementContext co } /// - 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 00000000000..6cf4a5a1fad --- /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 d472a6a1d27..070603cb7d0 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 @@ private static void OnCastToPropertyChanged(DependencyObject d, DependencyProper // 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 @@ private static void OnCastToPropertyChanged(DependencyObject d, DependencyProper } // 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 @@ 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 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 5c2e5a0d51f..b78e2a771fc 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowBase.cs @@ -172,7 +172,7 @@ public AttachedShadowElementContext GetElementContext(FrameworkElement element) } /// - 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 07116f4c3d2..1d8803d23af 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/AttachedShadowElementContext.cs @@ -268,35 +268,35 @@ public T RemoveAndDisposeResource(string key) /// /// 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 afababd92a4..74eee795b05 100644 --- a/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs +++ b/Microsoft.Toolkit.Uwp.UI/Shadows/IAttachedShadow.cs @@ -45,6 +45,6 @@ public interface IAttachedShadow /// 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 9e46ed107ce..43c7393d83d 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. From 836a50126b2d4b04ab2852f7ce9b161efaa6f85a Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Sat, 28 Aug 2021 00:56:18 -0700 Subject: [PATCH 168/200] add design metadata for RichSuggestBox --- .../Controls/RichSuggestBox.Metadata.cs | 44 +++++++++++++++++++ .../Controls/RichSuggestBox.Typedata.cs | 32 ++++++++++++++ ...t.Uwp.UI.Controls.Input.DesignTools.csproj | 4 +- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Typedata.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs new file mode 100644 index 00000000000..1dcb3daa666 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs @@ -0,0 +1,44 @@ +// 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.ComponentModel; + +using Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties; + +using Microsoft.VisualStudio.DesignTools.Extensibility; +using Microsoft.VisualStudio.DesignTools.Extensibility.Metadata; + +namespace Microsoft.Toolkit.Uwp.UI.Controls.Design +{ + internal class RichSuggestBoxMetadata : AttributeTableBuilder + { + public RichSuggestBoxMetadata() + : base() + { + AddCallback(ControlTypes.RichSuggestBox, + b => + { + b.AddCustomAttributes(nameof(RichSuggestBox.ClipboardCopyFormat), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.ClipboardPasteFormat), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.Description), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.DisabledFormattingAccelerators), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.Header), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.HeaderTemplate), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PlaceholderText), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupCornerRadius), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooter), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooterTemplate), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupHeader), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupHeaderTemplate), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupPlacement), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.Prefixes), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.RichEditBoxStyle), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.TokenBackground), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.TokenForeground), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false)); + } + ); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Typedata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Typedata.cs new file mode 100644 index 00000000000..c36eb0b50c7 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Typedata.cs @@ -0,0 +1,32 @@ +// 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.UI.Controls.Design +{ + internal static partial class ControlTypes + { + internal const string RichSuggestBox = RootNamespace + "." + nameof(RichSuggestBox); + } + + internal static class RichSuggestBox + { + internal const string PlaceholderText = nameof(PlaceholderText); + internal const string RichEditBoxStyle = nameof(RichEditBoxStyle); + internal const string Header = nameof(Header); + internal const string HeaderTemplate = nameof(HeaderTemplate); + internal const string Description = nameof(Description); + internal const string PopupPlacement = nameof(PopupPlacement); + internal const string PopupCornerRadius = nameof(PopupCornerRadius); + internal const string PopupHeader = nameof(PopupHeader); + internal const string PopupHeaderTemplate = nameof(PopupHeaderTemplate); + internal const string PopupFooter = nameof(PopupFooter); + internal const string PopupFooterTemplate = nameof(PopupFooterTemplate); + internal const string TokenBackground = nameof(TokenBackground); + internal const string TokenForeground = nameof(TokenForeground); + internal const string Prefixes = nameof(Prefixes); + internal const string ClipboardPasteFormat = nameof(ClipboardPasteFormat); + internal const string ClipboardCopyFormat = nameof(ClipboardCopyFormat); + internal const string DisabledFormattingAccelerators = nameof(DisabledFormattingAccelerators); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj index bea936c0671..2d2510304dd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj @@ -1,4 +1,4 @@ - + Debug @@ -84,6 +84,8 @@ + + Code From a37860dc3c8d2350f8e3a3bd5d1510cbf35300d1 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Sat, 28 Aug 2021 01:37:02 -0700 Subject: [PATCH 169/200] adjust category attributes --- .../Controls/RichSuggestBox.Metadata.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs index 1dcb3daa666..3835ff66236 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs @@ -24,18 +24,27 @@ public RichSuggestBoxMetadata() b.AddCustomAttributes(nameof(RichSuggestBox.Description), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(RichSuggestBox.DisabledFormattingAccelerators), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(RichSuggestBox.Header), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.HeaderTemplate), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.HeaderTemplate), + new CategoryAttribute(Resources.CategoryAppearance), + new EditorBrowsableAttribute(EditorBrowsableState.Advanced) + ); b.AddCustomAttributes(nameof(RichSuggestBox.PlaceholderText), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.PopupCornerRadius), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupCornerRadius), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooter), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooterTemplate), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooterTemplate), + new CategoryAttribute(Resources.CategoryAppearance), + new EditorBrowsableAttribute(EditorBrowsableState.Advanced) + ); b.AddCustomAttributes(nameof(RichSuggestBox.PopupHeader), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.PopupHeaderTemplate), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.PopupPlacement), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupHeaderTemplate), + new CategoryAttribute(Resources.CategoryAppearance), + new EditorBrowsableAttribute(EditorBrowsableState.Advanced) + ); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupPlacement), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(RichSuggestBox.Prefixes), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.RichEditBoxStyle), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.TokenBackground), new CategoryAttribute(Resources.CategoryCommon)); - b.AddCustomAttributes(nameof(RichSuggestBox.TokenForeground), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.RichEditBoxStyle), new CategoryAttribute(Resources.CategoryAppearance)); + b.AddCustomAttributes(nameof(RichSuggestBox.TokenBackground), new CategoryAttribute(Resources.CategoryBrush)); + b.AddCustomAttributes(nameof(RichSuggestBox.TokenForeground), new CategoryAttribute(Resources.CategoryBrush)); b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false)); } ); From 8fa7a8dfd01a5efecec9354429231f330df3b2ab Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Sat, 28 Aug 2021 18:21:56 +0200 Subject: [PATCH 170/200] Applied the suggested name changes by @michael-hawker --- .../ListDetailsView.Properties.cs | 28 +++++++++---------- .../ListDetailsView/ListDetailsView.cs | 4 +-- .../ListDetailsView/ListDetailsView.xaml | 20 ++++++------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs index d9c5df75e5d..53a822bfd8c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.Properties.cs @@ -106,21 +106,21 @@ public partial class ListDetailsView new PropertyMetadata(null)); /// - /// Identifies the dependency property. + /// Identifies the dependency property. /// - /// The identifier for the dependency property. - public static readonly DependencyProperty ListPaneNoItemsContentProperty = DependencyProperty.Register( - nameof(ListPaneNoItemsContent), + /// The identifier for the dependency property. + public static readonly DependencyProperty ListPaneEmptyContentProperty = DependencyProperty.Register( + nameof(ListPaneEmptyContent), typeof(object), typeof(ListDetailsView), new PropertyMetadata(null)); /// - /// Identifies the dependency property. + /// Identifies the dependency property. /// - /// The identifier for the dependency property. - public static readonly DependencyProperty ListPaneNoItemsContentTemplateProperty = DependencyProperty.Register( - nameof(ListPaneNoItemsContentTemplate), + /// The identifier for the dependency property. + public static readonly DependencyProperty ListPaneEmptyContentTemplateProperty = DependencyProperty.Register( + nameof(ListPaneEmptyContentTemplate), typeof(DataTemplate), typeof(ListDetailsView), new PropertyMetadata(null)); @@ -321,10 +321,10 @@ public DataTemplate ListHeaderTemplate /// /// The content of the list pane's header. The default is null. /// - public object ListPaneNoItemsContent + public object ListPaneEmptyContent { - get { return GetValue(ListPaneNoItemsContentProperty); } - set { SetValue(ListPaneNoItemsContentProperty, value); } + get { return GetValue(ListPaneEmptyContentProperty); } + set { SetValue(ListPaneEmptyContentProperty, value); } } /// @@ -333,10 +333,10 @@ public object ListPaneNoItemsContent /// /// The template that specifies the visualization of the list pane no items object. The default is null. /// - public DataTemplate ListPaneNoItemsContentTemplate + public DataTemplate ListPaneEmptyContentTemplate { - get { return (DataTemplate)GetValue(ListPaneNoItemsContentTemplateProperty); } - set { SetValue(ListPaneNoItemsContentTemplateProperty, value); } + get { return (DataTemplate)GetValue(ListPaneEmptyContentTemplateProperty); } + set { SetValue(ListPaneEmptyContentTemplateProperty, value); } } /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs index 0fbefdeceb3..26b69afa100 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.cs @@ -38,7 +38,7 @@ public partial class ListDetailsView : ItemsControl private const string PartRootPanel = "RootPanel"; private const string PartDetailsPresenter = "DetailsPresenter"; private const string PartDetailsPanel = "DetailsPanel"; - private const string PartMasterList = "MasterList"; + private const string PartMainList = "MainList"; private const string PartBackButton = "ListDetailsBackButton"; private const string PartHeaderContentPresenter = "HeaderContentPresenter"; private const string PartListPaneCommandBarPanel = "ListPaneCommandBarPanel"; @@ -405,7 +405,7 @@ private void FocusFirstFocusableElementInDetails() /// private void FocusItemList() { - if (GetTemplateChild("PartMasterList") is Control list) + if (GetTemplateChild("PartMainList") is Control list) { list.Focus(FocusState.Programmatic); } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml index 3fb14c4d4c1..0c22b4468c1 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.xaml @@ -91,7 +91,7 @@ To="NoSelectionNarrow"> - - + + - - + + @@ -149,11 +149,11 @@ Content="{TemplateBinding ListHeader}" ContentTemplate="{TemplateBinding ListHeaderTemplate}" Visibility="Collapsed" /> - - + - + From a867faddee47caba347464aefd0f1a87fda2d5f0 Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Sat, 28 Aug 2021 18:24:57 +0200 Subject: [PATCH 171/200] Removed the additional parentheses spotted by @XAML-Knight --- .../ListDetailsView/ListDetailsView.BackButton.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs index ff046c6781b..45e08b80c1e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/ListDetailsView/ListDetailsView.BackButton.cs @@ -40,7 +40,7 @@ private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) if (ViewState == ListDetailsViewState.Details) { - if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + if (BackButtonBehavior == BackButtonBehavior.Inline && _inlineBackButton != null) { _inlineBackButton.Visibility = Visibility.Visible; } @@ -53,7 +53,7 @@ private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) // Setting this indicates that the system back button is being used _previousSystemBackButtonVisibility = navigationManager.AppViewBackButtonVisibility; } - else if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) + else if (_inlineBackButton != null && (_navigationView == null || _frame == null)) { // We can only use the new NavigationView if we also have a Frame // If there is no frame we have to use the inline button @@ -74,7 +74,7 @@ private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) } else if (previousState == ListDetailsViewState.Details) { - if ((BackButtonBehavior == BackButtonBehavior.Inline) && (_inlineBackButton != null)) + if (BackButtonBehavior == BackButtonBehavior.Inline && _inlineBackButton != null) { _inlineBackButton.Visibility = Visibility.Collapsed; } @@ -82,7 +82,7 @@ private void SetBackButtonVisibility(ListDetailsViewState? previousState = null) { if (!_previousSystemBackButtonVisibility.HasValue) { - if ((_inlineBackButton != null) && ((_navigationView == null) || (_frame == null))) + if (_inlineBackButton != null && (_navigationView == null || _frame == null)) { _inlineBackButton.Visibility = Visibility.Collapsed; } @@ -132,7 +132,7 @@ private void SetNavigationViewBackButtonState(int visible, bool enabled) /// The event args private void OnFrameNavigating(object sender, NavigatingCancelEventArgs args) { - if ((args.NavigationMode == NavigationMode.Back) && (ViewState == ListDetailsViewState.Details)) + if (args.NavigationMode == NavigationMode.Back && ViewState == ListDetailsViewState.Details) { ClearSelectedItem(); args.Cancel = true; From 02bbc4db9c4352bd0fc1d9dfc6d1bc91038ad50a Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Sat, 28 Aug 2021 12:26:46 -0700 Subject: [PATCH 172/200] adjust category attribute --- .../Controls/RichSuggestBox.Metadata.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs index 3835ff66236..3b3bc0c3e34 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Controls/RichSuggestBox.Metadata.cs @@ -28,7 +28,7 @@ public RichSuggestBoxMetadata() new CategoryAttribute(Resources.CategoryAppearance), new EditorBrowsableAttribute(EditorBrowsableState.Advanced) ); - b.AddCustomAttributes(nameof(RichSuggestBox.PlaceholderText), new CategoryAttribute(Resources.CategoryCommon)); + b.AddCustomAttributes(nameof(RichSuggestBox.PlaceholderText), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(RichSuggestBox.PopupCornerRadius), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooter), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(RichSuggestBox.PopupFooterTemplate), @@ -40,7 +40,7 @@ public RichSuggestBoxMetadata() new CategoryAttribute(Resources.CategoryAppearance), new EditorBrowsableAttribute(EditorBrowsableState.Advanced) ); - b.AddCustomAttributes(nameof(RichSuggestBox.PopupPlacement), new CategoryAttribute(Resources.CategoryAppearance)); + b.AddCustomAttributes(nameof(RichSuggestBox.PopupPlacement), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(RichSuggestBox.Prefixes), new CategoryAttribute(Resources.CategoryCommon)); b.AddCustomAttributes(nameof(RichSuggestBox.RichEditBoxStyle), new CategoryAttribute(Resources.CategoryAppearance)); b.AddCustomAttributes(nameof(RichSuggestBox.TokenBackground), new CategoryAttribute(Resources.CategoryBrush)); From 79f29316572001a2b8147896c26ed2546c3400de Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 29 Aug 2021 13:05:05 -0400 Subject: [PATCH 173/200] Fix ColorPickerButton samples and switch to StackPanel This is now the same as ColorPicker samples --- .../ColorPicker/ColorPickerButtonXaml.bind | 104 ++++++------------ .../ColorPicker/ColorPickerXaml.bind | 42 +++---- 2 files changed, 54 insertions(+), 92 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind index 654d66c346a..939f067a2b3 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerButtonXaml.bind @@ -10,19 +10,11 @@ - - - - - - - - - + + - - - + - - - + - - - + + + + + Ring-shaped spectrum + Alpha channel enabled + Only Color Palette Shown + + + + + + + - - - - - Ring-shaped spectrum - Alpha channel enabled - Only Color Palette Shown - - - - - - - - - + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind index c3116470e48..1502d4ad1f0 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ColorPicker/ColorPickerXaml.bind @@ -84,27 +84,27 @@ ColorSpectrumComponents="SaturationValue" IsAlphaEnabled="True" IsHexInputVisible="True"/> - - - - Ring-shaped spectrum - Alpha channel enabled - Only Color Palette Shown - - - + + + + Ring-shaped spectrum + Alpha channel enabled + Only Color Palette Shown + + + \ No newline at end of file From f606fa8e7c77af3870148e5acd638a0ff54f58a2 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Mon, 30 Aug 2021 11:26:56 -0700 Subject: [PATCH 174/200] Update UI Test certificate, expires 8/30/2022 --- UITests/UITests.App/UITests.App.csproj | 2 +- UITests/UITests.App/UITests.App.pfx | Bin 2528 -> 2528 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/UITests/UITests.App/UITests.App.csproj b/UITests/UITests.App/UITests.App.csproj index a502e95f063..52c7d1b9801 100644 --- a/UITests/UITests.App/UITests.App.csproj +++ b/UITests/UITests.App/UITests.App.csproj @@ -22,7 +22,7 @@ True 0 UITests.App.pfx - 24D62F3B13B8B9514EAD9C4DE48CC30F7CC6151D + A39FFC9A7EDC3BC1F62B1FC6409AD9EDD84E8CF0 SHA256 true True diff --git a/UITests/UITests.App/UITests.App.pfx b/UITests/UITests.App/UITests.App.pfx index ec83ace8dee66bd5ed3b56449eab930d83815869..946bbe25ddc57fab6ec1cc8c5902770be6f13b34 100644 GIT binary patch delta 2273 zcmV<72p;#~6W|k&Xn$_neIcN0?5zR<2haq91lXp!ipOM}#N|57;cNpf0h*!_#Lji1 zeg=;4f0WG1>zG=79_-;JDgOjP@ka?Bf`g>t5&VRkyky(GE&62_C*FUyx>rNzI#4_l zo))^=80mABQR zpD}mHdP7htIPmsPC;8mMpq1&4)Uq&ICUoKGb7Jx^WdXvdBgEGtu!AR*f@ph^K_QND zQR?CeG}Hv6SbwNNLfT@k=KtJriZtO3X&Qs%)QsGYb?Ca+h$)V$`5~`zL0@17*NpPe ze0__E)G?jd(`5X&(mM?hw%0>t2(gOg9mcn@?;;*MS3sKri^D}YIAnq5LgIARjOJ&&?RN%#g9bcm6(ggKn=gc@ z(wMlbtlraDMXhF;GIsPnhL(%1essv%ceiEVFn^qGcWER1VA0{6WH3Wzc=%LlmpMa-pfdd3uWb5#U7EOv~+2i=KYDHL>|n zj|gP5)rsmp2_}>EqSMwLpUVp?Q?MW*?|;;{?}dFO+&-PCta+e&mn2JeK&-guLOb&4 z!1aS%qXi=rj^#mTV|k*G*RU;cb&&Z&uHzUk5%XHPj9~wV%_%RxPlQN<2;kUASFy(U zJ^Bc%${R9Hu0(KrWW04TqCOOzm7IeKU=z^2uzV&w?0CCbsPdcGRKVoQgu#q2j(;e~ z&t@Z@7TKSNR7iG)GW;acvj&uXW1ooFIY93EF{EycC1@g0Fp?m zp2;p=*RJVWIdgYEb_z{qN6om=eSewZLTe9{#xT2gt(8yowS}w5cxnSs%B^WnC$qAd zKY|`eA!fY9rvuafD&LNQUh$sVH)tbB|-^N+Iz1I6R%hMmn{Ykc}7r3S; z$LgTu`Y8MlPC`Lnhq61eiwN(h%*UVl9RyY!#%qCj{~^?ob|65U_9X#-mw*sj->{+N zxzI@L9!M4R7#K<T=#s@1tRUZS4#X9$vJ2}@)$OX?cuq)lcAd7H=}fXY}|OLFMaA6crD|Rtp#A7IDV$(Cdf2zg<0+@?y&v= z(WIn%;4W%%ft{XP#Z;Pq;4)%s+Ztw&(cF~JaU~$=2Km;^Go}kJAT|xl#cY>)Qs_@R z_he+napkQE_@^aW4+Q?Nwm2J8Dq<{h2I{*SCrkp92bSWo)Crc32NOvT8XIyzZ`T1! z=m;N)EObH3;<>?e&)?!?eIH|>);MN~pNYi4rpOGP5e@jLp(4kB5Hav-lXrc-524eb zkDz)qev$7&&JIW>#Uj?4#z6Kat?^JiR{3IojSyv3+4pTWwRGE!9n`sc|3alHc%OhS zr;1qjk2ZQCyKJI7AYsPobG>d>vE0KqOm;e7*vchHJG#3tR{fWwp64{!tAWn?x$9%+ zgBhoId$1PcMc49wBS7HkyvI)Bv9HDDVQCg6(uug6cMVX!GB)wzE&oD`PUz``(K8l} z(bC{{x$T+BjG0Z3#Mos!Fo$byoY*Aot;mBXEC6IfiaaA-?}Fi^liX~qa{O*9X*g?Q zJlXz82bmqFtRYBlTLrAnIOba(CovJPQJI70$^u+jbVdn(yCI^t?3&;p9?oTsc#+4N zoE9;tvGW-~rkD8$INlX?n7OFb_0|_S#NQQ(pX<(!aNMdHR2W z?-TqcJ5o`9_dqEp5D$c)$L8_79(+4j`}U+*^`ejyAc=xr}g@$lO;vz6RFdr}n1_dh)0|FWZ6syR2 v=yD+SQY7(4SMknF-JLLjwgePU<6^~g@aCmpIyd7CJ23Ui@fYE70s;rn1msNt delta 2273 zcmV<72p;#~6W|k&Xn$7#D1bN5J+A@+2haq91lZtg4DUK8g~vIKp($^8^)7MHBFc{f zX@qKCopS-WPICT4!V#&^Ha84>47R*hRqXUG{3 zsKg?iInbawi2RGuUtpKotC8oW$A_6rc{s-;4ZdhnbAL@ffhi0d-N=)IIm#BR4DoO0 z4ER}=Qxt#)_9$&=%9fyfe$S39H-_ zc()oUw10@$?m^_PQX@as?(9-kPSbw$E9xvq=*A1dzd8J72fsRAj)00H$pMAyqxaj~ z7ihITrbo_>9llN)*c@Unt37M`!}a?&|H2DHIcK8BLq9C!rRIm>O}>bM6wLXsg@8+b zSkx$s7}(C~*2lQT1$?Vnl`q_COB);B@X>YDudBE3+8QgpG2}@}c#$)T+(9k>QbH#FRuVCM>7{Itv1^in>?jGq zK7ZB6FjDR|9<1qQh!Wb*^=I$ODnPcuj>Au;mRS9cn+3!+9FCAgKERHW6-Kywjyu1a z9!K7|KmrWq4dk5}jUc^`kq5N5A8NKiHCU~fuKA|k#Th{uDK+baoEuIlODqF>nBRtz zeU4YGWKmTa+APB;ZxcNZ)b);3lQN_%aeth8OnO3LL;B5?CYsunsee149s)vL2?+Q~ z(q&42&&`BeBiR(@!_fYQ#LExCH>Yh`?&99I#^f;ac8x(O~a8? z=P&x>(>+8uz`qZ~@K5An0}ANCc|M+gNpO}6TMSr^R(h0TOGZAZc!o2vH@4t7IDgI& z$$ynli~~KN)OouvUz}D6Cd}~=x#-Zbxq>0O1T3ecmsOhL6Be&MUhhWzOQ*W8U_hFK zXwUGt!4mO0(yuKMm-&r(T7cp} zDP$3-phiSlo_*}GNa-M<7vD=wZGX;wF z7yd=TVKa(}pFRr$x8v@mhY|@Preupgr^;;Z)!j4{hX1q0S%)t8#$lc4Hj47C&LNQUOVgNA!H~=jGGypgNFaS0HEdVqCIRG*MGyp9CVgNA!WB_CUEdXHvWB@b( zW&kq)W&mUWIRH2SVgO;2zXhCsR4_cIr>^aV0s;rnfPw=!zYT&uYI>UNV#hY;bc0aS zAi(Gs@c)czia)Yb&~0N<$)7l7CSOYi!#um7ZkTfrX+_stggJ=4wW4+KX-qaGjAN+p zI>Tq%q48CLehqG8V!5Vfghv&KEo@nF&DCbe5iH_v2$=Mou)fWsLPd#LL0j(E&4knvzIP_K`zcnkF6Xa`JUp?lB#wd38s6(J}WN*PdFCOm`k@4F$@d1isOib*wV4||T4W3O&OMs28v7GDOM8F@BSTMNZ7YQ$1Uq=Q+CN( z({6M0&ocMt6KgTx{-tZ_&Cu154M761o-raVho3R+LjCcY$015&y&&xA%b@HB#H)(K zJ0>vGUo|e*IBUX7Ekst_7moynWuMkt!Q>W`tQIutr9azl@{Y!TELWi2UNou{+fT>o zg^&4QI-K7Sx6i=uThWXdZ@15cr9S_*&Wv0j$Fs|Ua3hHlB?0x+>gmaDRB{{+70(Eg z`!&j|?Gl=+!!tO<`uhsx@m)7xj07(t{T5n9??eahzHlEs63+F#QYi%)3t!%kGIVOM z7%Q6CJMn3n-ocN5{xi)R05$Sk`Fj%c(kBv~xAdaV)B%z6`A-0i>ihUEO&)~?UdUQm zX3n_N%4tQ8fKzpQ*_>8F%q*^|6&+f?1lk;?A1ff%se2CX-Yo=hr0f^bdcG^!rQB!AsJUo+BuE?{Z$vG_i_eS78 z&EKatx}^nhu+tqJ4a=YWg5h(mogeiyhWWt`HPV=nIgFTW(AUwst|KNT=5OA& z$#gorFSC|J5Kt!{%-A*PT1Z%G^(gQ>s|^$F&X3Y@+1nLMm1*~q&=tIsWAa+adooLWCAVF`5^&G1IgChj?{;*WhM9mU z4KciVN`+*>&X~?Cy0qS0FNTSsmJ>h7IEx#PO(Knwqbo2wFdr}n1_dh)0|FWZ6wpcI vJg0v)g>Vn2aZR>4A{SlLS_BmBzKBp Date: Mon, 30 Aug 2021 16:02:51 -0700 Subject: [PATCH 175/200] Remove stale comment on attached shadow example --- .../SamplePages/Shadows/AttachedShadowWin2DXaml.bind | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind index 717d8a31515..84897111675 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Shadows/AttachedShadowWin2DXaml.bind @@ -7,7 +7,6 @@ xmlns:media="using:Microsoft.Toolkit.Uwp.UI.Media" mc:Ignorable="d"> - From cf4122c37030b6eb5e845cf5be3245e20977d990 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Sun, 29 Aug 2021 23:34:16 -0700 Subject: [PATCH 176/200] break main RichSuggestBox.cs into smaller files --- .../RichSuggestBox/RichSuggestBox.Document.cs | 244 ++++++++ .../RichSuggestBox.Suggestion.cs | 292 ++++++++++ .../RichSuggestBox/RichSuggestBox.cs | 521 +----------------- 3 files changed, 543 insertions(+), 514 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs new file mode 100644 index 00000000000..53b7b06c2fc --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs @@ -0,0 +1,244 @@ +// 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 Windows.UI.Input; +using Windows.UI.Text; +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 + { + private void ExpandSelectionOnPartialTokenSelect(ITextSelection selection, ITextRange tokenRange) + { + switch (selection.Type) + { + case SelectionType.InsertionPoint: + // Snap selection to token on click + if (tokenRange.StartPosition < selection.StartPosition && selection.EndPosition < tokenRange.EndPosition) + { + selection.Expand(TextRangeUnit.Link); + InvokeTokenSelected(selection); + } + + break; + + case SelectionType.Normal: + // We do not want user to partially select a token since pasting to a partial token can break + // the token tracking system, which can result in unwanted character formatting issues. + if ((tokenRange.StartPosition <= selection.StartPosition && selection.EndPosition < tokenRange.EndPosition) || + (tokenRange.StartPosition < selection.StartPosition && selection.EndPosition <= tokenRange.EndPosition)) + { + // TODO: Figure out how to expand selection without breaking selection flow (with Shift select or pointer sweep select) + selection.Expand(TextRangeUnit.Link); + InvokeTokenSelected(selection); + } + + break; + } + } + + private void InvokeTokenSelected(ITextSelection selection) + { + if (TokenSelected == null || !TryGetTokenFromRange(selection, out var token) || token.RangeEnd != selection.EndPosition) + { + return; + } + + TokenSelected.Invoke(this, new RichSuggestTokenSelectedEventArgs + { + Token = token, + Range = selection.GetClone() + }); + } + + private void InvokeTokenPointerOver(PointerPoint pointer) + { + var pointerPosition = TransformToVisual(_richEditBox).TransformPoint(pointer.Position); + var padding = _richEditBox.Padding; + pointerPosition.X += HorizontalOffset - padding.Left; + pointerPosition.Y += VerticalOffset - padding.Top; + var range = TextDocument.GetRangeFromPoint(pointerPosition, PointOptions.ClientCoordinates); + var linkRange = range.GetClone(); + range.Expand(TextRangeUnit.Character); + range.GetRect(PointOptions.None, out var hitTestRect, out _); + hitTestRect.X -= hitTestRect.Width; + hitTestRect.Width *= 2; + if (hitTestRect.Contains(pointerPosition) && linkRange.Expand(TextRangeUnit.Link) > 0 && + TryGetTokenFromRange(linkRange, out var token)) + { + this.TokenPointerOver.Invoke(this, new RichSuggestTokenPointerOverEventArgs + { + Token = token, + Range = linkRange, + CurrentPoint = pointer + }); + } + } + + private bool TryCommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, ITextCharacterFormat format, bool addTrailingSpace = true) + { + // We don't want to set text when the display text doesn't change since it may lead to unexpected caret move. + range.GetText(TextGetOptions.NoHidden, out var existingText); + if (existingText != displayText) + { + range.SetText(TextSetOptions.Unhide, displayText); + } + + var formatBefore = range.CharacterFormat.GetClone(); + range.CharacterFormat.SetClone(format); + PadRange(range, formatBefore); + range.Link = $"\"{id}\""; + + // In some rare case, setting Link can fail. Only observed when interacting with Undo/Redo feature. + if (range.Link != $"\"{id}\"") + { + range.Delete(TextRangeUnit.Story, -1); + return false; + } + + if (addTrailingSpace) + { + var clone = range.GetClone(); + clone.Collapse(false); + clone.SetText(TextSetOptions.Unhide, " "); + clone.Collapse(false); + TextDocument.Selection.SetRange(clone.EndPosition, clone.EndPosition); + } + + return true; + } + + private void ValidateTokensInDocument() + { + lock (_tokensLock) + { + foreach (var (_, token) in _tokens) + { + token.Active = false; + } + } + + ForEachLinkInDocument(TextDocument, ValidateTokenFromRange); + } + + private void ValidateTokenFromRange(ITextRange range) + { + if (range.Length == 0 || !TryGetTokenFromRange(range, out var token)) + { + return; + } + + // Check for duplicate tokens. This can happen if the user copies and pastes the token multiple times. + if (token.Active && token.RangeStart != range.StartPosition && token.RangeEnd != range.EndPosition) + { + lock (_tokensLock) + { + var guid = Guid.NewGuid(); + if (TryCommitSuggestionIntoDocument(range, token.DisplayText, guid, CreateTokenFormat(range), false)) + { + token = new RichSuggestToken(guid, token.DisplayText) { Active = true, Item = token.Item }; + token.UpdateTextRange(range); + _tokens.Add(range.Link, token); + } + + return; + } + } + + if (token.ToString() != range.Text) + { + range.Delete(TextRangeUnit.Story, 0); + token.Active = false; + return; + } + + token.UpdateTextRange(range); + token.Active = true; + } + + private bool TryExtractQueryFromSelection(out string prefix, out string query, out ITextRange range) + { + prefix = string.Empty; + query = string.Empty; + range = null; + if (TextDocument.Selection.Type != SelectionType.InsertionPoint) + { + return false; + } + + // Check if selection is on existing link (suggestion) + var expandCount = TextDocument.Selection.GetClone().Expand(TextRangeUnit.Link); + if (expandCount != 0) + { + return false; + } + + var selection = TextDocument.Selection.GetClone(); + selection.MoveStart(TextRangeUnit.Word, -1); + if (selection.Length == 0) + { + return false; + } + + range = selection; + if (TryExtractQueryFromRange(selection, out prefix, out query)) + { + return true; + } + + selection.MoveStart(TextRangeUnit.Word, -1); + if (TryExtractQueryFromRange(selection, out prefix, out query)) + { + return true; + } + + range = null; + return false; + } + + private bool TryExtractQueryFromRange(ITextRange range, out string prefix, out string query) + { + prefix = string.Empty; + query = string.Empty; + range.GetText(TextGetOptions.NoHidden, out var possibleQuery); + if (possibleQuery.Length > 0 && Prefixes.Contains(possibleQuery[0]) && + !possibleQuery.Any(char.IsWhiteSpace) && string.IsNullOrEmpty(range.Link)) + { + if (possibleQuery.Length == 1) + { + prefix = possibleQuery; + return true; + } + + prefix = possibleQuery[0].ToString(); + query = possibleQuery.Substring(1); + return true; + } + + return false; + } + + private ITextCharacterFormat CreateTokenFormat(ITextRange range) + { + var format = range.CharacterFormat.GetClone(); + if (this.TokenBackground != null) + { + format.BackgroundColor = this.TokenBackground.Color; + } + + if (this.TokenForeground != null) + { + format.ForegroundColor = this.TokenForeground.Color; + } + + return format; + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs new file mode 100644 index 00000000000..9574bfa7189 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs @@ -0,0 +1,292 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.Toolkit.Uwp.Deferred; +using Windows.Foundation.Metadata; +using Windows.UI.Text; +using Windows.UI.Xaml; +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 + { + private async Task RequestSuggestionsAsync(ITextRange range = null) + { + string prefix; + string query; + var currentQuery = _currentQuery; + var queryFound = range == null + ? TryExtractQueryFromSelection(out prefix, out query, out range) + : TryExtractQueryFromRange(range, out prefix, out query); + + if (queryFound && prefix == currentQuery?.Prefix && query == currentQuery?.QueryText && + range.EndPosition == currentQuery?.Range.EndPosition && _suggestionPopup.IsOpen) + { + return; + } + + var previousTokenSource = currentQuery?.CancellationTokenSource; + if (!(previousTokenSource?.IsCancellationRequested ?? true)) + { + previousTokenSource.Cancel(); + } + + if (queryFound) + { + using var tokenSource = new CancellationTokenSource(); + _currentQuery = new RichSuggestQuery + { + Prefix = prefix, QueryText = query, Range = range, CancellationTokenSource = tokenSource + }; + + var cancellationToken = tokenSource.Token; + var eventArgs = new SuggestionRequestedEventArgs { QueryText = query, Prefix = prefix }; + if (SuggestionRequested != null) + { + try + { + await SuggestionRequested.InvokeAsync(this, eventArgs, cancellationToken); + } + catch (OperationCanceledException) + { + return; + } + } + + if (!eventArgs.Cancel) + { + _suggestionChoice = 0; + ShowSuggestionsPopup(_suggestionsList?.Items?.Count > 0); + } + + tokenSource.Cancel(); + } + else + { + ShowSuggestionsPopup(false); + } + } + + internal async Task CommitSuggestionAsync(object selectedItem) + { + var currentQuery = _currentQuery; + var range = currentQuery?.Range.GetClone(); + var id = Guid.NewGuid(); + var prefix = currentQuery?.Prefix; + var query = currentQuery?.QueryText; + + // range has length of 0 at the end of the commit. + // Checking length == 0 to avoid committing twice. + if (prefix == null || query == null || range == null || range.Length == 0) + { + return; + } + + var textBefore = range.Text; + var format = CreateTokenFormat(range); + var eventArgs = new SuggestionChosenEventArgs + { + Id = id, + Prefix = prefix, + QueryText = query, + SelectedItem = selectedItem, + DisplayText = query, + Format = format + }; + + if (SuggestionChosen != null) + { + await SuggestionChosen.InvokeAsync(this, eventArgs); + } + + var text = eventArgs.DisplayText; + + // Since this operation is async, the document may have changed at this point. + // Double check if the range still has the expected query. + if (string.IsNullOrEmpty(text) || textBefore != range.Text || + !TryExtractQueryFromRange(range, out var testPrefix, out var testQuery) || + testPrefix != prefix || testQuery != query) + { + return; + } + + lock (_tokensLock) + { + var displayText = prefix + text; + + _ignoreChange = true; + var committed = TryCommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format ?? format); + TextDocument.EndUndoGroup(); + TextDocument.BeginUndoGroup(); + _ignoreChange = false; + + if (committed) + { + var token = new RichSuggestToken(id, displayText) { Active = true, Item = selectedItem }; + token.UpdateTextRange(range); + _tokens.TryAdd(range.Link, token); + } + } + } + + private void UpdateSuggestionsListSelectedItem(int choice) + { + var itemsList = _suggestionsList.Items; + if (itemsList == null) + { + return; + } + + _suggestionsList.SelectedItem = choice == 0 ? null : itemsList[choice - 1]; + _suggestionsList.ScrollIntoView(_suggestionsList.SelectedItem); + } + + private void ShowSuggestionsPopup(bool show) + { + if (_suggestionPopup == null) + { + return; + } + + this._suggestionPopup.IsOpen = show; + if (!show) + { + this._suggestionChoice = 0; + this._suggestionPopup.VerticalOffset = 0; + this._suggestionPopup.HorizontalOffset = 0; + this._suggestionsList.SelectedItem = null; + this._suggestionsList.ScrollIntoView(this._suggestionsList.Items?.FirstOrDefault()); + UpdateCornerRadii(); + } + } + + private void UpdatePopupWidth() + { + if (this._suggestionsContainer == null) + { + return; + } + + if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached) + { + this._suggestionsContainer.MaxWidth = double.PositiveInfinity; + this._suggestionsContainer.Width = this._richEditBox.ActualWidth; + } + else + { + this._suggestionsContainer.MaxWidth = this._richEditBox.ActualWidth; + this._suggestionsContainer.Width = double.NaN; + } + } + + /// + /// Calculate whether to open the suggestion list up or down depends on how much screen space is available + /// + private void UpdatePopupOffset() + { + if (this._suggestionsContainer == null || this._suggestionPopup == null || this._richEditBox == null) + { + return; + } + + this._richEditBox.TextDocument.Selection.GetRect(PointOptions.None, out var selectionRect, out _); + Thickness padding = this._richEditBox.Padding; + selectionRect.X -= HorizontalOffset; + selectionRect.Y -= VerticalOffset; + + // Update horizontal offset + if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached) + { + this._suggestionPopup.HorizontalOffset = 0; + } + else + { + double editBoxWidth = this._richEditBox.ActualWidth - padding.Left - padding.Right; + if (this._suggestionPopup.HorizontalOffset == 0 && editBoxWidth > 0) + { + var normalizedX = selectionRect.X / editBoxWidth; + this._suggestionPopup.HorizontalOffset = + (this._richEditBox.ActualWidth - this._suggestionsContainer.ActualWidth) * normalizedX; + } + } + + // Update vertical offset + double downOffset = this._richEditBox.ActualHeight; + double upOffset = -this._suggestionsContainer.ActualHeight; + if (this.PopupPlacement == SuggestionPopupPlacementMode.Floating) + { + downOffset = selectionRect.Bottom + padding.Top + padding.Bottom; + upOffset += selectionRect.Top; + } + + if (this._suggestionPopup.VerticalOffset == 0) + { + if (IsElementOnScreen(this._suggestionsContainer, offsetY: downOffset) && + (IsElementInsideWindow(this._suggestionsContainer, offsetY: downOffset) || + !IsElementInsideWindow(this._suggestionsContainer, offsetY: upOffset) || + !IsElementOnScreen(this._suggestionsContainer, offsetY: upOffset))) + { + this._suggestionPopup.VerticalOffset = downOffset; + this._popupOpenDown = true; + } + else + { + this._suggestionPopup.VerticalOffset = upOffset; + this._popupOpenDown = false; + } + + UpdateCornerRadii(); + } + else + { + this._suggestionPopup.VerticalOffset = this._popupOpenDown ? downOffset : upOffset; + } + } + + /// + /// Set corner radii so that inner corners, where suggestion list and text box connect, are square. + /// This only applies when is set to . + /// + /// https://docs.microsoft.com/en-us/windows/apps/design/style/rounded-corner#when-not-to-round + private void UpdateCornerRadii() + { + if (this._richEditBox == null || this._suggestionsContainer == null || + !ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7)) + { + return; + } + + this._richEditBox.CornerRadius = CornerRadius; + this._suggestionsContainer.CornerRadius = PopupCornerRadius; + + if (this._suggestionPopup.IsOpen && PopupPlacement == SuggestionPopupPlacementMode.Attached) + { + if (this._popupOpenDown) + { + var cornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0); + this._richEditBox.CornerRadius = cornerRadius; + var popupCornerRadius = + new CornerRadius(0, 0, PopupCornerRadius.BottomRight, PopupCornerRadius.BottomLeft); + this._suggestionsContainer.CornerRadius = popupCornerRadius; + } + else + { + var cornerRadius = new CornerRadius(0, 0, CornerRadius.BottomRight, CornerRadius.BottomLeft); + this._richEditBox.CornerRadius = cornerRadius; + var popupCornerRadius = + new CornerRadius(PopupCornerRadius.TopLeft, PopupCornerRadius.TopRight, 0, 0); + this._suggestionsContainer.CornerRadius = popupCornerRadius; + } + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs index bb1e1ee00ec..8a1a2ae3b4d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs @@ -6,14 +6,9 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Toolkit.Uwp.Deferred; using Windows.ApplicationModel.DataTransfer; using Windows.Foundation; -using Windows.Foundation.Metadata; using Windows.System; -using Windows.UI.Input; using Windows.UI.Text; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -277,6 +272,12 @@ private void OnLostFocus(object sender, RoutedEventArgs e) ShowSuggestionsPopup(false); } + private async void SuggestionsList_ItemClick(object sender, ItemClickEventArgs e) + { + var selectedItem = e.ClickedItem; + await CommitSuggestionAsync(selectedItem); + } + private void SuggestionsList_SizeChanged(object sender, SizeChangedEventArgs e) { if (this._suggestionPopup.IsOpen) @@ -287,10 +288,7 @@ private void SuggestionsList_SizeChanged(object sender, SizeChangedEventArgs e) private void SuggestionList_GotFocus(object sender, RoutedEventArgs e) { - if (_richEditBox != null) - { - _richEditBox.Focus(FocusState.Programmatic); - } + _richEditBox?.Focus(FocusState.Programmatic); } private void RichEditBox_OnPointerMoved(object sender, PointerRoutedEventArgs e) @@ -395,12 +393,6 @@ private async void RichEditBox_PreviewKeyDown(object sender, KeyRoutedEventArgs } } - private async void SuggestionsList_ItemClick(object sender, ItemClickEventArgs e) - { - var selectedItem = e.ClickedItem; - await CommitSuggestionAsync(selectedItem); - } - private void RichEditBox_TextChanging(RichEditBox sender, RichEditBoxTextChangingEventArgs args) { if (_ignoreChange || !args.IsContentChanging) @@ -462,276 +454,6 @@ private async void RichEditBox_Paste(object sender, TextControlPasteEventArgs e) } } - private void ExpandSelectionOnPartialTokenSelect(ITextSelection selection, ITextRange tokenRange) - { - switch (selection.Type) - { - case SelectionType.InsertionPoint: - // Snap selection to token on click - if (tokenRange.StartPosition < selection.StartPosition && selection.EndPosition < tokenRange.EndPosition) - { - selection.Expand(TextRangeUnit.Link); - InvokeTokenSelected(selection); - } - - break; - - case SelectionType.Normal: - // We do not want user to partially select a token since pasting to a partial token can break - // the token tracking system, which can result in unwanted character formatting issues. - if ((tokenRange.StartPosition <= selection.StartPosition && selection.EndPosition < tokenRange.EndPosition) || - (tokenRange.StartPosition < selection.StartPosition && selection.EndPosition <= tokenRange.EndPosition)) - { - // TODO: Figure out how to expand selection without breaking selection flow (with Shift select or pointer sweep select) - selection.Expand(TextRangeUnit.Link); - InvokeTokenSelected(selection); - } - - break; - } - } - - private void InvokeTokenSelected(ITextSelection selection) - { - if (TokenSelected == null || !TryGetTokenFromRange(selection, out var token) || token.RangeEnd != selection.EndPosition) - { - return; - } - - TokenSelected.Invoke(this, new RichSuggestTokenSelectedEventArgs - { - Token = token, - Range = selection.GetClone() - }); - } - - private void InvokeTokenPointerOver(PointerPoint pointer) - { - var pointerPosition = TransformToVisual(_richEditBox).TransformPoint(pointer.Position); - var padding = _richEditBox.Padding; - pointerPosition.X += HorizontalOffset - padding.Left; - pointerPosition.Y += VerticalOffset - padding.Top; - var range = TextDocument.GetRangeFromPoint(pointerPosition, PointOptions.ClientCoordinates); - var linkRange = range.GetClone(); - range.Expand(TextRangeUnit.Character); - range.GetRect(PointOptions.None, out var hitTestRect, out _); - hitTestRect.X -= hitTestRect.Width; - hitTestRect.Width *= 2; - if (hitTestRect.Contains(pointerPosition) && linkRange.Expand(TextRangeUnit.Link) > 0 && - TryGetTokenFromRange(linkRange, out var token)) - { - this.TokenPointerOver.Invoke(this, new RichSuggestTokenPointerOverEventArgs - { - Token = token, - Range = linkRange, - CurrentPoint = pointer - }); - } - } - - private async Task RequestSuggestionsAsync(ITextRange range = null) - { - string prefix; - string query; - var currentQuery = _currentQuery; - var queryFound = range == null - ? TryExtractQueryFromSelection(out prefix, out query, out range) - : TryExtractQueryFromRange(range, out prefix, out query); - - if (queryFound && prefix == currentQuery?.Prefix && query == currentQuery?.QueryText && - range.EndPosition == currentQuery?.Range.EndPosition && _suggestionPopup.IsOpen) - { - return; - } - - var previousTokenSource = currentQuery?.CancellationTokenSource; - if (!(previousTokenSource?.IsCancellationRequested ?? true)) - { - previousTokenSource.Cancel(); - } - - if (queryFound) - { - using var tokenSource = new CancellationTokenSource(); - _currentQuery = new RichSuggestQuery - { - Prefix = prefix, - QueryText = query, - Range = range, - CancellationTokenSource = tokenSource - }; - - var cancellationToken = tokenSource.Token; - var eventArgs = new SuggestionRequestedEventArgs { QueryText = query, Prefix = prefix }; - if (SuggestionRequested != null) - { - try - { - await SuggestionRequested.InvokeAsync(this, eventArgs, cancellationToken); - } - catch (OperationCanceledException) - { - return; - } - } - - if (!eventArgs.Cancel) - { - _suggestionChoice = 0; - ShowSuggestionsPopup(_suggestionsList?.Items?.Count > 0); - } - - tokenSource.Cancel(); - } - else - { - ShowSuggestionsPopup(false); - } - } - - internal async Task CommitSuggestionAsync(object selectedItem) - { - var currentQuery = _currentQuery; - var range = currentQuery?.Range.GetClone(); - var id = Guid.NewGuid(); - var prefix = currentQuery?.Prefix; - var query = currentQuery?.QueryText; - - // range has length of 0 at the end of the commit. - // Checking length == 0 to avoid committing twice. - if (prefix == null || query == null || range == null || range.Length == 0) - { - return; - } - - var textBefore = range.Text; - var format = CreateTokenFormat(range); - var eventArgs = new SuggestionChosenEventArgs - { - Id = id, - Prefix = prefix, - QueryText = query, - SelectedItem = selectedItem, - DisplayText = query, - Format = format - }; - - if (SuggestionChosen != null) - { - await SuggestionChosen.InvokeAsync(this, eventArgs); - } - - var text = eventArgs.DisplayText; - - // Since this operation is async, the document may have changed at this point. - // Double check if the range still has the expected query. - if (string.IsNullOrEmpty(text) || textBefore != range.Text || - !TryExtractQueryFromRange(range, out var testPrefix, out var testQuery) || - testPrefix != prefix || testQuery != query) - { - return; - } - - lock (_tokensLock) - { - var displayText = prefix + text; - - _ignoreChange = true; - var committed = TryCommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format ?? format); - TextDocument.EndUndoGroup(); - TextDocument.BeginUndoGroup(); - _ignoreChange = false; - - if (committed) - { - var token = new RichSuggestToken(id, displayText) { Active = true, Item = selectedItem }; - token.UpdateTextRange(range); - _tokens.TryAdd(range.Link, token); - } - } - } - - private bool TryCommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, ITextCharacterFormat format, bool addTrailingSpace = true) - { - // We don't want to set text when the display text doesn't change since it may lead to unexpected caret move. - range.GetText(TextGetOptions.NoHidden, out var existingText); - if (existingText != displayText) - { - range.SetText(TextSetOptions.Unhide, displayText); - } - - var formatBefore = range.CharacterFormat.GetClone(); - range.CharacterFormat.SetClone(format); - PadRange(range, formatBefore); - range.Link = $"\"{id}\""; - - // In some rare case, setting Link can fail. Only observed when interacting with Undo/Redo feature. - if (range.Link != $"\"{id}\"") - { - range.Delete(TextRangeUnit.Story, -1); - return false; - } - - if (addTrailingSpace) - { - var clone = range.GetClone(); - clone.Collapse(false); - clone.SetText(TextSetOptions.Unhide, " "); - clone.Collapse(false); - TextDocument.Selection.SetRange(clone.EndPosition, clone.EndPosition); - } - - return true; - } - - private void ValidateTokensInDocument() - { - lock (_tokensLock) - { - foreach (var (_, token) in _tokens) - { - token.Active = false; - } - } - - ForEachLinkInDocument(TextDocument, ValidateTokenFromRange); - } - - private void ValidateTokenFromRange(ITextRange range) - { - if (range.Length == 0 || !TryGetTokenFromRange(range, out var token)) - { - return; - } - - // Check for duplicate tokens. This can happen if the user copies and pastes the token multiple times. - if (token.Active && token.RangeStart != range.StartPosition && token.RangeEnd != range.EndPosition) - { - lock (_tokensLock) - { - var guid = Guid.NewGuid(); - if (TryCommitSuggestionIntoDocument(range, token.DisplayText, guid, CreateTokenFormat(range), false)) - { - token = new RichSuggestToken(guid, token.DisplayText) { Active = true, Item = token.Item }; - token.UpdateTextRange(range); - _tokens.Add(range.Link, token); - } - - return; - } - } - - if (token.ToString() != range.Text) - { - range.Delete(TextRangeUnit.Story, 0); - token.Active = false; - return; - } - - token.UpdateTextRange(range); - token.Active = true; - } - private void ConditionallyLoadElement(object property, string elementName) { if (property != null && GetTemplateChild(elementName) is UIElement presenter) @@ -740,235 +462,6 @@ private void ConditionallyLoadElement(object property, string elementName) } } - private void UpdateSuggestionsListSelectedItem(int choice) - { - var itemsList = _suggestionsList.Items; - if (itemsList == null) - { - return; - } - - _suggestionsList.SelectedItem = choice == 0 ? null : itemsList[choice - 1]; - _suggestionsList.ScrollIntoView(_suggestionsList.SelectedItem); - } - - private void ShowSuggestionsPopup(bool show) - { - if (_suggestionPopup == null) - { - return; - } - - this._suggestionPopup.IsOpen = show; - if (!show) - { - this._suggestionChoice = 0; - this._suggestionPopup.VerticalOffset = 0; - this._suggestionPopup.HorizontalOffset = 0; - this._suggestionsList.SelectedItem = null; - this._suggestionsList.ScrollIntoView(this._suggestionsList.Items?.FirstOrDefault()); - UpdateCornerRadii(); - } - } - - private void UpdatePopupWidth() - { - if (this._suggestionsContainer == null) - { - return; - } - - if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached) - { - this._suggestionsContainer.MaxWidth = double.PositiveInfinity; - this._suggestionsContainer.Width = this._richEditBox.ActualWidth; - } - else - { - this._suggestionsContainer.MaxWidth = this._richEditBox.ActualWidth; - this._suggestionsContainer.Width = double.NaN; - } - } - - /// - /// Calculate whether to open the suggestion list up or down depends on how much screen space is available - /// - private void UpdatePopupOffset() - { - if (this._suggestionsContainer == null || this._suggestionPopup == null || this._richEditBox == null) - { - return; - } - - this._richEditBox.TextDocument.Selection.GetRect(PointOptions.None, out var selectionRect, out _); - Thickness padding = this._richEditBox.Padding; - selectionRect.X -= HorizontalOffset; - selectionRect.Y -= VerticalOffset; - - // Update horizontal offset - if (this.PopupPlacement == SuggestionPopupPlacementMode.Attached) - { - this._suggestionPopup.HorizontalOffset = 0; - } - else - { - double editBoxWidth = this._richEditBox.ActualWidth - padding.Left - padding.Right; - if (this._suggestionPopup.HorizontalOffset == 0 && editBoxWidth > 0) - { - var normalizedX = selectionRect.X / editBoxWidth; - this._suggestionPopup.HorizontalOffset = - (this._richEditBox.ActualWidth - this._suggestionsContainer.ActualWidth) * normalizedX; - } - } - - // Update vertical offset - double downOffset = this._richEditBox.ActualHeight; - double upOffset = -this._suggestionsContainer.ActualHeight; - if (this.PopupPlacement == SuggestionPopupPlacementMode.Floating) - { - downOffset = selectionRect.Bottom + padding.Top + padding.Bottom; - upOffset += selectionRect.Top; - } - - if (this._suggestionPopup.VerticalOffset == 0) - { - if (IsElementOnScreen(this._suggestionsContainer, offsetY: downOffset) && - (IsElementInsideWindow(this._suggestionsContainer, offsetY: downOffset) || - !IsElementInsideWindow(this._suggestionsContainer, offsetY: upOffset) || - !IsElementOnScreen(this._suggestionsContainer, offsetY: upOffset))) - { - this._suggestionPopup.VerticalOffset = downOffset; - this._popupOpenDown = true; - } - else - { - this._suggestionPopup.VerticalOffset = upOffset; - this._popupOpenDown = false; - } - - UpdateCornerRadii(); - } - else - { - this._suggestionPopup.VerticalOffset = this._popupOpenDown ? downOffset : upOffset; - } - } - - /// - /// Set corner radii so that inner corners, where suggestion list and text box connect, are square. - /// This only applies when is set to . - /// - /// https://docs.microsoft.com/en-us/windows/apps/design/style/rounded-corner#when-not-to-round - private void UpdateCornerRadii() - { - if (this._richEditBox == null || this._suggestionsContainer == null || - !ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7)) - { - return; - } - - this._richEditBox.CornerRadius = CornerRadius; - this._suggestionsContainer.CornerRadius = PopupCornerRadius; - - if (this._suggestionPopup.IsOpen && PopupPlacement == SuggestionPopupPlacementMode.Attached) - { - if (this._popupOpenDown) - { - var cornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0); - this._richEditBox.CornerRadius = cornerRadius; - var popupCornerRadius = - new CornerRadius(0, 0, PopupCornerRadius.BottomRight, PopupCornerRadius.BottomLeft); - this._suggestionsContainer.CornerRadius = popupCornerRadius; - } - else - { - var cornerRadius = new CornerRadius(0, 0, CornerRadius.BottomRight, CornerRadius.BottomLeft); - this._richEditBox.CornerRadius = cornerRadius; - var popupCornerRadius = - new CornerRadius(PopupCornerRadius.TopLeft, PopupCornerRadius.TopRight, 0, 0); - this._suggestionsContainer.CornerRadius = popupCornerRadius; - } - } - } - - private bool TryExtractQueryFromSelection(out string prefix, out string query, out ITextRange range) - { - prefix = string.Empty; - query = string.Empty; - range = null; - if (TextDocument.Selection.Type != SelectionType.InsertionPoint) - { - return false; - } - - // Check if selection is on existing link (suggestion) - var expandCount = TextDocument.Selection.GetClone().Expand(TextRangeUnit.Link); - if (expandCount != 0) - { - return false; - } - - var selection = TextDocument.Selection.GetClone(); - selection.MoveStart(TextRangeUnit.Word, -1); - if (selection.Length == 0) - { - return false; - } - - range = selection; - if (TryExtractQueryFromRange(selection, out prefix, out query)) - { - return true; - } - - selection.MoveStart(TextRangeUnit.Word, -1); - if (TryExtractQueryFromRange(selection, out prefix, out query)) - { - return true; - } - - range = null; - return false; - } - - private bool TryExtractQueryFromRange(ITextRange range, out string prefix, out string query) - { - prefix = string.Empty; - query = string.Empty; - range.GetText(TextGetOptions.NoHidden, out var possibleQuery); - if (possibleQuery.Length > 0 && Prefixes.Contains(possibleQuery[0]) && - !possibleQuery.Any(char.IsWhiteSpace) && string.IsNullOrEmpty(range.Link)) - { - if (possibleQuery.Length == 1) - { - prefix = possibleQuery; - return true; - } - - prefix = possibleQuery[0].ToString(); - query = possibleQuery.Substring(1); - return true; - } - - return false; - } - - private ITextCharacterFormat CreateTokenFormat(ITextRange range) - { - var format = range.CharacterFormat.GetClone(); - if (this.TokenBackground != null) - { - format.BackgroundColor = this.TokenBackground.Color; - } - - if (this.TokenForeground != null) - { - format.ForegroundColor = this.TokenForeground.Color; - } - - return format; - } - private void UpdateVisibleTokenList() { lock (_tokensLock) From ede46a3e4e5a9fcd39d6eeecc37d8a5c9c63cac7 Mon Sep 17 00:00:00 2001 From: Tung Huynh Date: Mon, 30 Aug 2021 00:27:59 -0700 Subject: [PATCH 177/200] abstract internal document edit into CreateSingleEdit --- .../RichSuggestBox/RichSuggestBox.Document.cs | 99 ++++++++++--------- .../RichSuggestBox/RichSuggestBox.Helpers.cs | 1 - .../RichSuggestBox.Suggestion.cs | 21 ++-- .../RichSuggestBox/RichSuggestBox.cs | 9 +- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs index 53b7b06c2fc..944d4dae017 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Document.cs @@ -15,6 +15,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls /// public partial class RichSuggestBox { + private void CreateSingleEdit(Action editAction) + { + _ignoreChange = true; + editAction.Invoke(); + TextDocument.EndUndoGroup(); + TextDocument.BeginUndoGroup(); + _ignoreChange = false; + } + private void ExpandSelectionOnPartialTokenSelect(ITextSelection selection, ITextRange tokenRange) { switch (selection.Type) @@ -82,47 +91,11 @@ private void InvokeTokenPointerOver(PointerPoint pointer) } } - private bool TryCommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, ITextCharacterFormat format, bool addTrailingSpace = true) - { - // We don't want to set text when the display text doesn't change since it may lead to unexpected caret move. - range.GetText(TextGetOptions.NoHidden, out var existingText); - if (existingText != displayText) - { - range.SetText(TextSetOptions.Unhide, displayText); - } - - var formatBefore = range.CharacterFormat.GetClone(); - range.CharacterFormat.SetClone(format); - PadRange(range, formatBefore); - range.Link = $"\"{id}\""; - - // In some rare case, setting Link can fail. Only observed when interacting with Undo/Redo feature. - if (range.Link != $"\"{id}\"") - { - range.Delete(TextRangeUnit.Story, -1); - return false; - } - - if (addTrailingSpace) - { - var clone = range.GetClone(); - clone.Collapse(false); - clone.SetText(TextSetOptions.Unhide, " "); - clone.Collapse(false); - TextDocument.Selection.SetRange(clone.EndPosition, clone.EndPosition); - } - - return true; - } - private void ValidateTokensInDocument() { - lock (_tokensLock) + foreach (var (_, token) in _tokens) { - foreach (var (_, token) in _tokens) - { - token.Active = false; - } + token.Active = false; } ForEachLinkInDocument(TextDocument, ValidateTokenFromRange); @@ -138,18 +111,15 @@ private void ValidateTokenFromRange(ITextRange range) // Check for duplicate tokens. This can happen if the user copies and pastes the token multiple times. if (token.Active && token.RangeStart != range.StartPosition && token.RangeEnd != range.EndPosition) { - lock (_tokensLock) + var guid = Guid.NewGuid(); + if (TryCommitSuggestionIntoDocument(range, token.DisplayText, guid, CreateTokenFormat(range), false)) { - var guid = Guid.NewGuid(); - if (TryCommitSuggestionIntoDocument(range, token.DisplayText, guid, CreateTokenFormat(range), false)) - { - token = new RichSuggestToken(guid, token.DisplayText) { Active = true, Item = token.Item }; - token.UpdateTextRange(range); - _tokens.Add(range.Link, token); - } - - return; + token = new RichSuggestToken(guid, token.DisplayText) { Active = true, Item = token.Item }; + token.UpdateTextRange(range); + _tokens.Add(range.Link, token); } + + return; } if (token.ToString() != range.Text) @@ -163,6 +133,39 @@ private void ValidateTokenFromRange(ITextRange range) token.Active = true; } + private bool TryCommitSuggestionIntoDocument(ITextRange range, string displayText, Guid id, ITextCharacterFormat format, bool addTrailingSpace) + { + // We don't want to set text when the display text doesn't change since it may lead to unexpected caret move. + range.GetText(TextGetOptions.NoHidden, out var existingText); + if (existingText != displayText) + { + range.SetText(TextSetOptions.Unhide, displayText); + } + + var formatBefore = range.CharacterFormat.GetClone(); + range.CharacterFormat.SetClone(format); + PadRange(range, formatBefore); + range.Link = $"\"{id}\""; + + // In some rare case, setting Link can fail. Only observed when interacting with Undo/Redo feature. + if (range.Link != $"\"{id}\"") + { + range.Delete(TextRangeUnit.Story, -1); + return false; + } + + if (addTrailingSpace) + { + var clone = range.GetClone(); + clone.Collapse(false); + clone.SetText(TextSetOptions.Unhide, " "); + clone.Collapse(false); + TextDocument.Selection.SetRange(clone.EndPosition, clone.EndPosition); + } + + return true; + } + private bool TryExtractQueryFromSelection(out string prefix, out string query, out ITextRange range) { prefix = string.Empty; 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 0357e261b4b..0be8e37615b 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 Windows.Foundation; using Windows.Graphics.Display; using Windows.UI.Core; using Windows.UI.Text; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs index 9574bfa7189..d73f390d10a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.Suggestion.cs @@ -119,23 +119,22 @@ internal async Task CommitSuggestionAsync(object selectedItem) return; } - lock (_tokensLock) - { - var displayText = prefix + text; + var displayText = prefix + text; - _ignoreChange = true; - var committed = TryCommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format ?? format); - TextDocument.EndUndoGroup(); - TextDocument.BeginUndoGroup(); - _ignoreChange = false; - - if (committed) + void RealizeToken() + { + if (TryCommitSuggestionIntoDocument(range, displayText, id, eventArgs.Format ?? format, true)) { var token = new RichSuggestToken(id, displayText) { Active = true, Item = selectedItem }; token.UpdateTextRange(range); - _tokens.TryAdd(range.Link, token); + _tokens.Add(range.Link, token); } } + + lock (_tokensLock) + { + this.CreateSingleEdit(RealizeToken); + } } private void UpdateSuggestionsListSelectedItem(int choice) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs index 8a1a2ae3b4d..856216412ad 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RichSuggestBox/RichSuggestBox.cs @@ -400,11 +400,10 @@ private void RichEditBox_TextChanging(RichEditBox sender, RichEditBoxTextChangin return; } - _ignoreChange = true; - ValidateTokensInDocument(); - TextDocument.EndUndoGroup(); - TextDocument.BeginUndoGroup(); - _ignoreChange = false; + lock (_tokensLock) + { + this.CreateSingleEdit(ValidateTokensInDocument); + } } private void RichEditBox_TextChanged(object sender, RoutedEventArgs e) From 8c9890bd23bb889b0ad93e9b5f2bddfee4debe69 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Mon, 30 Aug 2021 17:29:37 -0700 Subject: [PATCH 178/200] Apply WeakReference --- ...rameworkElementExtensions.RelativeAncestor.cs | 16 +--------------- .../Test_FrameworkElementExtensions.cs | 2 ++ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs index e17fe1fb893..f981e75d6ca 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs @@ -25,10 +25,8 @@ public static object GetAncestor(DependencyObject obj) /// /// Parent object /// FrameworkElement - //public static void SetAncestor(DependencyObject obj, WeakReference value) public static void SetAncestor(DependencyObject obj, object value) { - //obj.SetValue(AncestorProperty, value); obj.SetValue(AncestorProperty, new WeakReference(value)); } @@ -38,7 +36,6 @@ public static void SetAncestor(DependencyObject obj, object value) public static readonly DependencyProperty AncestorProperty = DependencyProperty.RegisterAttached("Ancestor", typeof(WeakReference), - //typeof(object), typeof(FrameworkElementExtensions), new PropertyMetadata(null)); @@ -73,13 +70,11 @@ private static void AncestorType_PropertyChanged(DependencyObject obj, Dependenc if (obj is FrameworkElement fe) { fe.Loaded -= FrameworkElement_Loaded; - //fe.Unloaded -= FrameworkElement_Unloaded; if (args.NewValue != null) { fe.Loaded += FrameworkElement_Loaded; - //fe.Unloaded += FrameworkElement_Unloaded; - + if (fe.Parent != null) { FrameworkElement_Loaded(fe, null); @@ -92,17 +87,8 @@ private static void FrameworkElement_Loaded(object sender, RoutedEventArgs e) { if (sender is FrameworkElement fe) { - //SetAncestor(fe, new WeakReference(fe.FindAscendant(GetAncestorType(fe)))); SetAncestor(fe, fe.FindAscendant(GetAncestorType(fe))); } } - - private static void FrameworkElement_Unloaded(object sender, RoutedEventArgs e) - { - if(sender is FrameworkElement fe) - { - SetAncestor(fe, null); - } - } } } \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs index 52eadeb27c1..6c6ab6da263 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs @@ -39,6 +39,8 @@ public async Task Test_Ancestor_WeakReference() var btn = treeRoot.FindChild("InnerButton") as Button; + Assert.IsNull(btn.Parent); + btn.SetValue(FrameworkElementExtensions.AncestorProperty, new object()); Assert.IsNull(btn.Parent); From cefaeeb258b5f48f8a2b436802b71a28b306d599 Mon Sep 17 00:00:00 2001 From: brianboly Date: Tue, 31 Aug 2021 16:39:15 -0400 Subject: [PATCH 179/200] Improving ComboBoxColumn behavior to prevent need to click a ComboBox 3 times for DropDown expansion --- .../DataGrid/DataGridComboBoxColumn.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs index 42f67a40b51..d18d106aa2f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs @@ -383,7 +383,13 @@ protected override void CancelCellEdit(FrameworkElement editingElement, object u /// The unedited value. protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs) { - return (editingElement as ComboBox)?.SelectedItem; + var comboBox = editingElement as ComboBox; + if (comboBox != null) + { + comboBox.IsDropDownOpen = true; + } + + return comboBox?.SelectedItem; } /// From 81504cec0848bbbb5382dc188ffe07905bd9e589 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Tue, 31 Aug 2021 16:13:47 -0700 Subject: [PATCH 180/200] Add extra requirements for controls to checklist --- .github/PULL_REQUEST_TEMPLATE.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 14fe6178110..ee7279502fa 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -38,7 +38,10 @@ What kind of change does this PR introduce? Please check if your PR fulfills the following requirements: - [ ] Tested code with current [supported SDKs](../#supported) -- [ ] Pull Request has been submitted to the documentation repository [instructions](../blob/main/Contributing.md#docs). Link: +- [ ] New component + - [ ] Pull Request has been submitted to the documentation repository [instructions](../blob/main/Contributing.md#docs). Link: + - [ ] Added description of major feature to project description for NuGet package (4000 total character limit, so don't push entire description over that) + - [ ] If control, added to Visual Studio Design project - [ ] Sample in sample app has been added / updated (for bug fixes / features) - [ ] Icon has been created (if new sample) following the [Thumbnail Style Guide and templates](https://github.com/CommunityToolkit/WindowsCommunityToolkit-design-assets) - [ ] New major technical changes in the toolkit have or will be added to the [Wiki](https://github.com/CommunityToolkit/WindowsCommunityToolkit/wiki) e.g. build changes, source generators, testing infrastructure, sample creation changes, etc... @@ -51,4 +54,4 @@ Please note that breaking changes are likely to be rejected within minor release ## Other information - \ No newline at end of file + From 2f9a2668c9750c8cf21b0d098fdfe07dd08f33e9 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Tue, 31 Aug 2021 17:08:59 -0700 Subject: [PATCH 181/200] Added missing header --- .../Extensions/Test_FrameworkElementExtensions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs index 6c6ab6da263..4766eea9dd7 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.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; using Microsoft.Toolkit.Uwp.UI; using Microsoft.VisualStudio.TestTools.UnitTesting; From 20c61f9393d8208fcaf692c2b8343c7c807f60a9 Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Sun, 14 Feb 2021 14:02:41 +0530 Subject: [PATCH 182/200] Convert Design projects to Sdk-style - Update Solution to Use CPS for the Design projects. - Move 'MetadataRegistration.cs' file into Common folder. - Remove 'AssemblyInfo.cs' file as it's generated by .NET SDK. --- .../{readme.md => ReadMe.md} | 0 .../{ => Common}/MetadataRegistration.cs | 10 +- ...it.Uwp.UI.Controls.Core.DesignTools.csproj | 141 +--------- .../Properties/AssemblyInfo.cs | 32 --- ...wp.UI.Controls.DataGrid.DesignTools.csproj | 112 +------- .../Properties/AssemblyInfo.cs | 32 --- ...t.Uwp.UI.Controls.Input.DesignTools.csproj | 117 +------- .../Properties/AssemblyInfo.cs | 32 --- ....Uwp.UI.Controls.Layout.DesignTools.csproj | 127 +-------- .../Properties/AssemblyInfo.cs | 32 --- ...wp.UI.Controls.Markdown.DesignTools.csproj | 112 +------- .../Properties/AssemblyInfo.cs | 32 --- ....UI.Controls.Primitives.DesignTools.csproj | 114 +------- .../Properties/AssemblyInfo.cs | 32 --- Windows Community Toolkit.sln | 256 +++++++++--------- .../Windows.Toolkit.VisualStudio.Design.props | 35 ++- ...indows.Toolkit.VisualStudio.Design.targets | 13 + 17 files changed, 238 insertions(+), 991 deletions(-) rename Microsoft.Toolkit.Uwp.Notifications/{readme.md => ReadMe.md} (100%) rename Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/{ => Common}/MetadataRegistration.cs (77%) delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/AssemblyInfo.cs delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Properties/AssemblyInfo.cs delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Properties/AssemblyInfo.cs delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Properties/AssemblyInfo.cs delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Properties/AssemblyInfo.cs delete mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Properties/AssemblyInfo.cs create mode 100644 build/Windows.Toolkit.VisualStudio.Design.targets diff --git a/Microsoft.Toolkit.Uwp.Notifications/readme.md b/Microsoft.Toolkit.Uwp.Notifications/ReadMe.md similarity index 100% rename from Microsoft.Toolkit.Uwp.Notifications/readme.md rename to Microsoft.Toolkit.Uwp.Notifications/ReadMe.md diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/MetadataRegistration.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Common/MetadataRegistration.cs similarity index 77% rename from Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/MetadataRegistration.cs rename to Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Common/MetadataRegistration.cs index 2b755e8893f..c6f6e697283 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/MetadataRegistration.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Common/MetadataRegistration.cs @@ -20,9 +20,11 @@ public MetadataRegistration() : base() // Note: // The default constructor sets value of 'AssemblyFullName' and // 'XmlResourceName' used by 'MetadataRegistrationBase.AddDescriptions()'. - // The convention here is that the in '.DesignTools.csproj' - // (or Default namespace in Project -> Properties -> Application tab) - // must be the same as runtime assembly's main namespace plus ".Design". + // The convention here is that the root namespace plus the Controls category. + // Example: + // + "." + + ".xml" + // "Microsoft.Toolkit.Uwp.UI.Controls" + "." + "Primitives" + ".xml" + Type thisType = this.GetType(); AssemblyName designLib = thisType.Assembly.GetName(); @@ -31,7 +33,7 @@ public MetadataRegistration() : base() string controlLibName = designLib.Name.Remove(annexStart, annexString.Length); AssemblyFullName = designLib.FullName; - XmlResourceName = $"{thisType.Namespace}{controlLibName}.xml"; + XmlResourceName = $"{controlLibName}.xml"; } } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj index 203fa4c3487..2db5364050f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj @@ -1,136 +1,15 @@ - - + + - Debug - x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.Core\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.Core\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(NoWarn);0618 - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - Common Controls (DesignTools) + Design time support for Windows Community Toolkit Common Controls - - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - True - True - Resources.resx - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Microsoft.Toolkit.Uwp.UI.Controls.Core.xml - False - - - - - - - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index f73fd4f9e89..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Controls (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Controls")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj index 2a21fa70bd4..e54f0629e60 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj @@ -1,107 +1,19 @@ - - + + - Debug - x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - DataGrid Control (Design) + Design time support for Windows Community Toolkit DataGrid Control + - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - Code - - - True - True - Resources.resx - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.xml - False - + - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index 4307a45aee6..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Controls DataGrid (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Controls DataGrid")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj index 2d2510304dd..3d09eb7086b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj @@ -1,112 +1,19 @@ - - + + - Debug - x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.Input\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.Input\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(NoWarn);0618 - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - Input Controls (Design) + Design time support for Windows Community Toolkit Input Controls + - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - - - - - Code - - - True - True - Resources.resx - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Microsoft.Toolkit.Uwp.UI.Controls.Input.xml - False - + - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index f73fd4f9e89..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Controls (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Controls")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj index 8a2b8b57ae4..d394a24fab3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj @@ -1,122 +1,19 @@ - - + + - Debug - x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.Layout\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.Layout\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(NoWarn);0618 - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - Layout Controls (Design) + Design time support for Windows Community Toolkit Layout Controls + - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - True - True - Resources.resx - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Microsoft.Toolkit.Uwp.UI.Controls.Layout.xml - False - + - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index f73fd4f9e89..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Controls (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Controls")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj index 1402810bf0b..01340cfae86 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj @@ -1,107 +1,19 @@ - - + + - Debug - x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.Markdown\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.Markdown\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - Markdown Control (Design) + Design time support for Windows Community Toolkit Markdown Control + - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - Code - - - True - True - Resources.resx - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Microsoft.Toolkit.Uwp.UI.Controls.Markdown.xml - False - + - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index 155da46d428..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Controls Markdown (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Markdown Controls")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj index 281073e59c6..51ff1a1233e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj @@ -1,109 +1,19 @@ - - + + - Debug - x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A} - Library - Properties - Microsoft.Toolkit.Uwp.UI.Controls.Design - Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools - 512 - 8.1 - v4.7.2 - false - - - true - ..\Microsoft.Toolkit.Uwp.UI.Controls.Primitives\bin\Debug\uap10.0.17763\Design\ - full - false - TRACE;DEBUG - x86 - - - ..\Microsoft.Toolkit.Uwp.UI.Controls.Primitives\bin\Release\uap10.0.17763\Design\ - pdbonly - x86 - true - TRACE + net472 + Microsoft.Toolkit.Uwp.UI.Controls + - $(AssetTargetFallback);uap10.0.17763 + Windows Community Toolkit - Basic Controls (Design) + Design time support for Windows Community Toolkit Basic Controls + - - - - - - False - False - - - False - False - - - - - - - - - - False - False - $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd - - - - - - - - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd - WindowsRuntime - False - - - $(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.17763.0\Windows.Foundation.UniversalApiContract\7.0.0.0\Windows.Foundation.UniversalApiContract.winmd - WindowsRuntime - False - - - - - - - - - - - - - Code - - - - Resources.resx - True - True - - - - - Microsoft.Toolkit.Uwp.UI.Controls.Primitives.xml - False - - - ResXFileCodeGenerator - Resources.Designer.cs - + - - - + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Properties/AssemblyInfo.cs deleted file mode 100644 index eefe23842bb..00000000000 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +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.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Windows Community Toolkit Primitive Controls (Design)")] -[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Primitive Controls")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Windows Community Toolkit")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US English -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] \ No newline at end of file diff --git a/Windows Community Toolkit.sln b/Windows Community Toolkit.sln index 1b9704c9604..56a778d4b97 100644 --- a/Windows Community Toolkit.sln +++ b/Windows Community Toolkit.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29230.61 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31521.260 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9AD30620-667D-433C-9961-8D885EE7B762}" EndProject @@ -52,12 +52,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution settings.xamlstyler = settings.xamlstyler EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Core.Design\Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj", "{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Core.Design\Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj", "{7AEFC959-ED7C-4D96-9E92-72609B40FBE0}" ProjectSection(ProjectDependencies) = postProject {E9FAABFB-D726-42C1-83C1-CB46A29FEA81} = {E9FAABFB-D726-42C1-83C1-CB46A29FEA81} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj", "{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.Design\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.DesignTools.csproj", "{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}" ProjectSection(ProjectDependencies) = postProject {DAEB9CEC-C817-33B2-74B2-BC379380DB72} = {DAEB9CEC-C817-33B2-74B2-BC379380DB72} EndProjectSection @@ -105,7 +105,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{88C6FFBE-3 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Markdown", "Microsoft.Toolkit.Uwp.UI.Controls.Markdown\Microsoft.Toolkit.Uwp.UI.Controls.Markdown.csproj", "{6FEDF199-B052-49DD-8F3E-2A9224998E0F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design\Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj", "{67FE47A0-CA93-4680-B770-A0A48C1DBC40}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Design\Microsoft.Toolkit.Uwp.UI.Controls.Markdown.DesignTools.csproj", "{67FE47A0-CA93-4680-B770-A0A48C1DBC40}" ProjectSection(ProjectDependencies) = postProject {6FEDF199-B052-49DD-8F3E-2A9224998E0F} = {6FEDF199-B052-49DD-8F3E-2A9224998E0F} EndProjectSection @@ -138,19 +138,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Co EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Primitives", "Microsoft.Toolkit.Uwp.UI.Controls.Primitives\Microsoft.Toolkit.Uwp.UI.Controls.Primitives.csproj", "{84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design\Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj", "{AF5045DE-0D13-45C2-AC33-50CF5FDB333A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design\Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj", "{AF5045DE-0D13-45C2-AC33-50CF5FDB333A}" ProjectSection(ProjectDependencies) = postProject {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A} = {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design\Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj", "{1B55A026-5BF8-4D04-B7C0-A82AB49BA017}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Layout.Design\Microsoft.Toolkit.Uwp.UI.Controls.Layout.DesignTools.csproj", "{1B55A026-5BF8-4D04-B7C0-A82AB49BA017}" ProjectSection(ProjectDependencies) = postProject {CB444381-18BA-4A51-BB32-3A498BCC1E99} = {CB444381-18BA-4A51-BB32-3A498BCC1E99} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Input", "Microsoft.Toolkit.Uwp.UI.Controls.Input\Microsoft.Toolkit.Uwp.UI.Controls.Input.csproj", "{AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Input.Design\Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj", "{3307BC1D-5D71-41C6-A1B3-B113B8242D08}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Input.Design\Microsoft.Toolkit.Uwp.UI.Controls.Input.DesignTools.csproj", "{3307BC1D-5D71-41C6-A1B3-B113B8242D08}" ProjectSection(ProjectDependencies) = postProject {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE} = {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE} EndProjectSection @@ -492,46 +492,46 @@ Global {94994424-5F60-4CD8-ABA2-101779066208}.Release|x64.Build.0 = Release|Any CPU {94994424-5F60-4CD8-ABA2-101779066208}.Release|x86.ActiveCfg = Release|Any CPU {94994424-5F60-4CD8-ABA2-101779066208}.Release|x86.Build.0 = Release|Any CPU - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|Any CPU.ActiveCfg = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|Any CPU.Build.0 = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM.ActiveCfg = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM.Build.0 = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM64.ActiveCfg = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM64.Build.0 = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x64.ActiveCfg = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x64.Build.0 = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.ActiveCfg = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.Build.0 = Debug|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.ActiveCfg = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.Build.0 = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM.ActiveCfg = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM.Build.0 = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM64.ActiveCfg = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM64.Build.0 = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x64.ActiveCfg = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x64.Build.0 = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x86.ActiveCfg = Release|x86 - {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x86.Build.0 = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|Any CPU.ActiveCfg = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|Any CPU.Build.0 = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM.ActiveCfg = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM.Build.0 = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM64.ActiveCfg = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM64.Build.0 = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x64.ActiveCfg = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x64.Build.0 = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.ActiveCfg = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.Build.0 = Debug|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.ActiveCfg = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.Build.0 = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM.ActiveCfg = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM.Build.0 = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM64.ActiveCfg = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM64.Build.0 = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x64.ActiveCfg = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x64.Build.0 = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x86.ActiveCfg = Release|x86 - {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x86.Build.0 = Release|x86 + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM.Build.0 = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|ARM64.Build.0 = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x64.ActiveCfg = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x64.Build.0 = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.ActiveCfg = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Debug|x86.Build.0 = Debug|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|Any CPU.Build.0 = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM.ActiveCfg = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM.Build.0 = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM64.ActiveCfg = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|ARM64.Build.0 = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x64.ActiveCfg = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x64.Build.0 = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x86.ActiveCfg = Release|Any CPU + {7AEFC959-ED7C-4D96-9E92-72609B40FBE0}.Release|x86.Build.0 = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM.ActiveCfg = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM.Build.0 = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|ARM64.Build.0 = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x64.ActiveCfg = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x64.Build.0 = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.ActiveCfg = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Debug|x86.Build.0 = Debug|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|Any CPU.Build.0 = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM.ActiveCfg = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM.Build.0 = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM64.ActiveCfg = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|ARM64.Build.0 = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x64.ActiveCfg = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x64.Build.0 = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x86.ActiveCfg = Release|Any CPU + {6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1}.Release|x86.Build.0 = Release|Any CPU {5BF75694-798A-43A0-8150-415DE195359C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5BF75694-798A-43A0-8150-415DE195359C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5BF75694-798A-43A0-8150-415DE195359C}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -804,26 +804,26 @@ Global {6FEDF199-B052-49DD-8F3E-2A9224998E0F}.Release|x64.Build.0 = Release|Any CPU {6FEDF199-B052-49DD-8F3E-2A9224998E0F}.Release|x86.ActiveCfg = Release|Any CPU {6FEDF199-B052-49DD-8F3E-2A9224998E0F}.Release|x86.Build.0 = Release|Any CPU - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|Any CPU.ActiveCfg = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|Any CPU.Build.0 = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM.ActiveCfg = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM.Build.0 = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM64.ActiveCfg = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM64.Build.0 = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x64.ActiveCfg = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x64.Build.0 = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x86.ActiveCfg = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x86.Build.0 = Debug|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|Any CPU.ActiveCfg = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|Any CPU.Build.0 = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM.ActiveCfg = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM.Build.0 = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM64.ActiveCfg = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM64.Build.0 = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x64.ActiveCfg = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x64.Build.0 = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x86.ActiveCfg = Release|x86 - {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x86.Build.0 = Release|x86 + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM.ActiveCfg = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM.Build.0 = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|ARM64.Build.0 = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x64.ActiveCfg = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x64.Build.0 = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x86.ActiveCfg = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Debug|x86.Build.0 = Debug|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|Any CPU.Build.0 = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM.ActiveCfg = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM.Build.0 = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM64.ActiveCfg = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|ARM64.Build.0 = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x64.ActiveCfg = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x64.Build.0 = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x86.ActiveCfg = Release|Any CPU + {67FE47A0-CA93-4680-B770-A0A48C1DBC40}.Release|x86.Build.0 = Release|Any CPU {0037E4C9-7AF3-4ADD-8156-5AEFA6C36405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0037E4C9-7AF3-4ADD-8156-5AEFA6C36405}.Debug|ARM.ActiveCfg = Debug|Any CPU {0037E4C9-7AF3-4ADD-8156-5AEFA6C36405}.Debug|ARM.Build.0 = Debug|Any CPU @@ -1016,46 +1016,46 @@ Global {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Release|x64.Build.0 = Release|Any CPU {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Release|x86.ActiveCfg = Release|Any CPU {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Release|x86.Build.0 = Release|Any CPU - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|Any CPU.ActiveCfg = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|Any CPU.Build.0 = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM.ActiveCfg = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM.Build.0 = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM64.ActiveCfg = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM64.Build.0 = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x64.ActiveCfg = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x64.Build.0 = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x86.ActiveCfg = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x86.Build.0 = Debug|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|Any CPU.ActiveCfg = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|Any CPU.Build.0 = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM.ActiveCfg = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM.Build.0 = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM64.ActiveCfg = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM64.Build.0 = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x64.ActiveCfg = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x64.Build.0 = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x86.ActiveCfg = Release|x86 - {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x86.Build.0 = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|Any CPU.ActiveCfg = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|Any CPU.Build.0 = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM.ActiveCfg = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM.Build.0 = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM64.ActiveCfg = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM64.Build.0 = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x64.ActiveCfg = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x64.Build.0 = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x86.ActiveCfg = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x86.Build.0 = Debug|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|Any CPU.ActiveCfg = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|Any CPU.Build.0 = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM.ActiveCfg = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM.Build.0 = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM64.ActiveCfg = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM64.Build.0 = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x64.ActiveCfg = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x64.Build.0 = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x86.ActiveCfg = Release|x86 - {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x86.Build.0 = Release|x86 + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM.Build.0 = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|ARM64.Build.0 = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x64.ActiveCfg = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x64.Build.0 = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x86.ActiveCfg = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Debug|x86.Build.0 = Debug|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|Any CPU.Build.0 = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM.ActiveCfg = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM.Build.0 = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM64.ActiveCfg = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|ARM64.Build.0 = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x64.ActiveCfg = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x64.Build.0 = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x86.ActiveCfg = Release|Any CPU + {AF5045DE-0D13-45C2-AC33-50CF5FDB333A}.Release|x86.Build.0 = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM.ActiveCfg = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM.Build.0 = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|ARM64.Build.0 = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x64.ActiveCfg = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x64.Build.0 = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x86.ActiveCfg = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Debug|x86.Build.0 = Debug|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|Any CPU.Build.0 = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM.ActiveCfg = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM.Build.0 = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM64.ActiveCfg = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|ARM64.Build.0 = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x64.ActiveCfg = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x64.Build.0 = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x86.ActiveCfg = Release|Any CPU + {1B55A026-5BF8-4D04-B7C0-A82AB49BA017}.Release|x86.Build.0 = Release|Any CPU {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1076,26 +1076,26 @@ Global {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Release|x64.Build.0 = Release|Any CPU {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Release|x86.ActiveCfg = Release|Any CPU {AF1BE4E9-E2E1-4729-B076-B3725D8E21EE}.Release|x86.Build.0 = Release|Any CPU - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|Any CPU.ActiveCfg = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|Any CPU.Build.0 = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM.ActiveCfg = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM.Build.0 = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM64.ActiveCfg = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM64.Build.0 = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x64.ActiveCfg = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x64.Build.0 = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x86.ActiveCfg = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x86.Build.0 = Debug|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|Any CPU.ActiveCfg = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|Any CPU.Build.0 = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM.ActiveCfg = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM.Build.0 = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM64.ActiveCfg = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM64.Build.0 = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x64.ActiveCfg = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x64.Build.0 = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x86.ActiveCfg = Release|x86 - {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x86.Build.0 = Release|x86 + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM.Build.0 = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|ARM64.Build.0 = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x64.ActiveCfg = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x64.Build.0 = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x86.ActiveCfg = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Debug|x86.Build.0 = Debug|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|Any CPU.Build.0 = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM.ActiveCfg = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM.Build.0 = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM64.ActiveCfg = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|ARM64.Build.0 = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x64.ActiveCfg = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x64.Build.0 = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x86.ActiveCfg = Release|Any CPU + {3307BC1D-5D71-41C6-A1B3-B113B8242D08}.Release|x86.Build.0 = Release|Any CPU {099B60FD-DAD6-4648-9DE2-8DBF9DCD9557}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {099B60FD-DAD6-4648-9DE2-8DBF9DCD9557}.Debug|Any CPU.Build.0 = Debug|Any CPU {099B60FD-DAD6-4648-9DE2-8DBF9DCD9557}.Debug|ARM.ActiveCfg = Debug|Any CPU diff --git a/build/Windows.Toolkit.VisualStudio.Design.props b/build/Windows.Toolkit.VisualStudio.Design.props index de91a09dfad..be20674e59f 100644 --- a/build/Windows.Toolkit.VisualStudio.Design.props +++ b/build/Windows.Toolkit.VisualStudio.Design.props @@ -1,21 +1,28 @@ - $([MSBuild]::EnsureTrailingSlash($([MSBuild]::ValueOrDefault('$(BaseIntermediateOutputPath)', 'obj')))) + True + False + CS0618;$(NoWarn) - - - - $(BaseIntermediateOutputPath)Design\ - - - - - $(BaseIntermediateOutputPath)DesignTools\ - - - - + + False + False + True + uap$(TargetPlatformBaseVersion).$(TargetPlatformMinRevision) + $(MSBuildProjectName.Remove($(MSBuildProjectName.LastIndexOf('.')))) + + $(ParentProjectName.Remove(0, $([MSBuild]::Add($(ParentProjectName.LastIndexOf('.')), '1')))) + + + + + + + $(ParentProjectType).xml + False + + \ No newline at end of file diff --git a/build/Windows.Toolkit.VisualStudio.Design.targets b/build/Windows.Toolkit.VisualStudio.Design.targets new file mode 100644 index 00000000000..842a7ad3c30 --- /dev/null +++ b/build/Windows.Toolkit.VisualStudio.Design.targets @@ -0,0 +1,13 @@ + + + + ..\$(ParentProjectName)\bin\$(Configuration)\$(ParentTargetFramework)\ + $(ParentProjectOutputPath)Design\ + + + + + + + + \ No newline at end of file From ea41b480b4f00fe6ddb2e87e910cc4625ee97a46 Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Sun, 14 Feb 2021 15:04:09 +0530 Subject: [PATCH 183/200] Build Design project's parent when not under solution build The reason we build the Controls library first is that we have a dependency on the XML Doc file it generates. The Doc file is embedded within the Design library and it uses it's type-info to populate metadata automatically. --- build/Windows.Toolkit.VisualStudio.Design.targets | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/Windows.Toolkit.VisualStudio.Design.targets b/build/Windows.Toolkit.VisualStudio.Design.targets index 842a7ad3c30..4c389fa9cd8 100644 --- a/build/Windows.Toolkit.VisualStudio.Design.targets +++ b/build/Windows.Toolkit.VisualStudio.Design.targets @@ -5,6 +5,14 @@ $(ParentProjectOutputPath)Design\ + + + False + False + True + + + From 67f3906eea3b8d951025668d0fd1dc7420da536f Mon Sep 17 00:00:00 2001 From: Nirmal Guru Date: Sun, 14 Feb 2021 15:04:21 +0530 Subject: [PATCH 184/200] Add Design project for the UWP Media controls Basic DesignTime support on-par with other controls in the toolkit. Reverts the following commit - Commit Message: `Media Controls does not have a "DesignTools" project` - Commit Hash : `1781e7f4291061c642fedfa5307aa80879838b56` --- ...t.Uwp.UI.Controls.Media.DesignTools.csproj | 19 +++ .../Properties/Resources.Designer.cs | 81 +++++++++++ .../Properties/Resources.resx | 126 ++++++++++++++++++ ...osoft.Toolkit.Uwp.UI.Controls.Media.csproj | 4 +- Windows Community Toolkit.sln | 28 +++- 5 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools.csproj create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.Designer.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.resx diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools.csproj new file mode 100644 index 00000000000..a2a931cea64 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools.csproj @@ -0,0 +1,19 @@ + + + + net472 + Microsoft.Toolkit.Uwp.UI.Controls + + + + Windows Community Toolkit - Media Controls (Design) + Design time support for Windows Community Toolkit Media Controls + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.Designer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.Designer.cs new file mode 100644 index 00000000000..35acb0b25a0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Common. + /// + internal static string CategoryCommon { + get { + return ResourceManager.GetString("CategoryCommon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Layout. + /// + internal static string CategoryLayout { + get { + return ResourceManager.GetString("CategoryLayout", resourceCulture); + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.resx b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.resx new file mode 100644 index 00000000000..6d2e88846dd --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Media.Design/Properties/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Common + + + Layout + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Media/Microsoft.Toolkit.Uwp.UI.Controls.Media.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Media/Microsoft.Toolkit.Uwp.UI.Controls.Media.csproj index df160b65ffe..e2a245b7046 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Media/Microsoft.Toolkit.Uwp.UI.Controls.Media.csproj +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Media/Microsoft.Toolkit.Uwp.UI.Controls.Media.csproj @@ -25,7 +25,6 @@ - @@ -33,9 +32,10 @@ - + + \ No newline at end of file diff --git a/Windows Community Toolkit.sln b/Windows Community Toolkit.sln index 56a778d4b97..4dae01f1765 100644 --- a/Windows Community Toolkit.sln +++ b/Windows Community Toolkit.sln @@ -136,6 +136,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Diagnosti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Media", "Microsoft.Toolkit.Uwp.UI.Controls.Media\Microsoft.Toolkit.Uwp.UI.Controls.Media.csproj", "{43BD2A36-9E12-4788-80AE-2377E41E6D05}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Media.Design\Microsoft.Toolkit.Uwp.UI.Controls.Media.DesignTools.csproj", "{B9940334-BD06-44E2-B9E9-A65DA791CE32}" + ProjectSection(ProjectDependencies) = postProject + {43BD2A36-9E12-4788-80AE-2377E41E6D05} = {43BD2A36-9E12-4788-80AE-2377E41E6D05} + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Primitives", "Microsoft.Toolkit.Uwp.UI.Controls.Primitives\Microsoft.Toolkit.Uwp.UI.Controls.Primitives.csproj", "{84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools", "Microsoft.Toolkit.Uwp.UI.Controls.Primitives.Design\Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DesignTools.csproj", "{AF5045DE-0D13-45C2-AC33-50CF5FDB333A}" @@ -161,7 +166,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Mvvm.Sour EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.SourceGenerators", "UnitTests\UnitTests.SourceGenerators\UnitTests.SourceGenerators.csproj", "{338C3BE4-2E71-4F21-AD30-03FDBB47A272}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.NetStandard", "UnitTests\UnitTests.NetStandard\UnitTests.NetStandard.csproj", "{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.NetStandard", "UnitTests\UnitTests.NetStandard\UnitTests.NetStandard.csproj", "{D9C82C0D-31D7-4888-B024-3CF3A4F54FE1}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -996,6 +1001,26 @@ Global {43BD2A36-9E12-4788-80AE-2377E41E6D05}.Release|x64.Build.0 = Release|Any CPU {43BD2A36-9E12-4788-80AE-2377E41E6D05}.Release|x86.ActiveCfg = Release|Any CPU {43BD2A36-9E12-4788-80AE-2377E41E6D05}.Release|x86.Build.0 = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|ARM.Build.0 = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|ARM64.Build.0 = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|x64.Build.0 = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Debug|x86.Build.0 = Debug|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|Any CPU.Build.0 = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|ARM.ActiveCfg = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|ARM.Build.0 = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|ARM64.ActiveCfg = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|ARM64.Build.0 = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|x64.ActiveCfg = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|x64.Build.0 = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|x86.ActiveCfg = Release|Any CPU + {B9940334-BD06-44E2-B9E9-A65DA791CE32}.Release|x86.Build.0 = Release|Any CPU {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1219,6 +1244,7 @@ Global {1D8B0260-5C17-41DA-9C38-1E37441B3925} = {6FAA1CFE-3368-4FD2-9DBD-F4700F69174C} {D4FF799D-0DF2-495A-ADC9-3BBC4AEF8971} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} {43BD2A36-9E12-4788-80AE-2377E41E6D05} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} + {B9940334-BD06-44E2-B9E9-A65DA791CE32} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} {84AB7DC5-95C9-4CF8-A370-D077E9E9EF1A} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} {AF5045DE-0D13-45C2-AC33-50CF5FDB333A} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} {1B55A026-5BF8-4D04-B7C0-A82AB49BA017} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC} From 8d0d47346363228874d8b038adbd97efc2ebc161 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 2 Sep 2021 12:30:39 +0200 Subject: [PATCH 185/200] Fixed [ObservableProperty] for fields named "value" --- .../ObservablePropertyGenerator.cs | 20 +++++--- .../Mvvm/Test_ObservablePropertyAttribute.cs | 50 +++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs index e5b9402e042..037dc98fa78 100644 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs +++ b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs @@ -240,6 +240,14 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( } } + // In case the backing field is exactly named "value", we need to add the "this." prefix to ensure that comparisons and assignments + // with it in the generated setter body are executed correctly and without conflicts with the implicit value parameter. + ExpressionSyntax fieldExpression = fieldSymbol.Name switch + { + "value" => MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("value")), + string name => IdentifierName(name) + }; + BlockSyntax setterBlock; if (validationAttributes.Count > 0) @@ -263,10 +271,10 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( // Generate the inner setter block as follows: // - // if (!global::System.Collections.Generic.EqualityComparer<>.Default.Equals(, value)) + // if (!global::System.Collections.Generic.EqualityComparer<>.Default.Equals(this., value)) // { // OnPropertyChanging(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangingEventArgs); // Optional - // = value; + // this. = value; // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangedEventArgs); // ValidateProperty(value, ); // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property1PropertyChangedEventArgs); // Optional @@ -291,7 +299,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( IdentifierName("Default")), IdentifierName("Equals"))) .AddArgumentListArguments( - Argument(IdentifierName(fieldSymbol.Name)), + Argument(fieldExpression), Argument(IdentifierName("value")))), Block( ExpressionStatement( @@ -303,7 +311,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - IdentifierName(fieldSymbol.Name), + fieldExpression, IdentifierName("value"))), ExpressionStatement( InvocationExpression(IdentifierName("OnPropertyChanged")) @@ -346,7 +354,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, - IdentifierName(fieldSymbol.Name), + fieldExpression, IdentifierName("value"))), ExpressionStatement( InvocationExpression(IdentifierName("OnPropertyChanged")) @@ -384,7 +392,7 @@ private static PropertyDeclarationSyntax CreatePropertyDeclaration( IdentifierName("Default")), IdentifierName("Equals"))) .AddArgumentListArguments( - Argument(IdentifierName(fieldSymbol.Name)), + Argument(fieldExpression), Argument(IdentifierName("value")))), updateAndNotificationBlock)); } diff --git a/UnitTests/UnitTests.NetCore/Mvvm/Test_ObservablePropertyAttribute.cs b/UnitTests/UnitTests.NetCore/Mvvm/Test_ObservablePropertyAttribute.cs index 8b8005d15c9..f7c596ba438 100644 --- a/UnitTests/UnitTests.NetCore/Mvvm/Test_ObservablePropertyAttribute.cs +++ b/UnitTests/UnitTests.NetCore/Mvvm/Test_ObservablePropertyAttribute.cs @@ -119,6 +119,42 @@ public void Test_ValidationAttributes() Assert.AreEqual(testAttribute.Animal, Animal.Llama); } + // See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216 + [TestCategory("Mvvm")] + [TestMethod] + public void Test_ObservablePropertyWithValueNamedField() + { + var model = new ModelWithValueProperty(); + + List propertyNames = new(); + + model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName); + + model.Value = "Hello world"; + + Assert.AreEqual(model.Value, "Hello world"); + + CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames); + } + + // See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/4216 + [TestCategory("Mvvm")] + [TestMethod] + public void Test_ObservablePropertyWithValueNamedField_WithValidationAttributes() + { + var model = new ModelWithValuePropertyWithValidation(); + + List propertyNames = new(); + + model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName); + + model.Value = "Hello world"; + + Assert.AreEqual(model.Value, "Hello world"); + + CollectionAssert.AreEqual(new[] { nameof(model.Value) }, propertyNames); + } + public partial class SampleModel : ObservableObject { /// @@ -195,5 +231,19 @@ public enum Animal Dog, Llama } + + public partial class ModelWithValueProperty : ObservableObject + { + [ObservableProperty] + private string value; + } + + public partial class ModelWithValuePropertyWithValidation : ObservableValidator + { + [ObservableProperty] + [Required] + [MinLength(5)] + private string value; + } } } From 8273bca898fa4ae8e380d560fbaf7cea3eb955f5 Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 2 Sep 2021 15:23:11 -0700 Subject: [PATCH 186/200] Update Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs Co-authored-by: Rosario Pulella --- .../DataGrid/DataGridComboBoxColumn.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs index d18d106aa2f..d70e56d0986 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridComboBoxColumn.cs @@ -383,13 +383,13 @@ protected override void CancelCellEdit(FrameworkElement editingElement, object u /// The unedited value. protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs) { - var comboBox = editingElement as ComboBox; - if (comboBox != null) + if(editingElement is ComboBox comboBox) { comboBox.IsDropDownOpen = true; + return comboBox.SelectedItem; } - return comboBox?.SelectedItem; + return null; } /// From cc2a51eaa963707e66fb01c7466764646c2b1783 Mon Sep 17 00:00:00 2001 From: Luke Blevins Date: Fri, 30 Jul 2021 14:26:33 -0400 Subject: [PATCH 187/200] fix: Prevent focus reset for DataGridTemplateColumn --- .../DataGrid/DataGrid.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGrid.cs b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGrid.cs index 561e69e4182..35814fff6fb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGrid.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGrid.cs @@ -5729,6 +5729,7 @@ private void DataGrid_LostFocus(object sender, RoutedEventArgs e) { bool focusLeftDataGrid = true; bool dataGridWillReceiveRoutedEvent = true; + DataGridColumn editingColumn = null; // Walk up the visual tree of the newly focused element // to determine if focus is still within DataGrid. @@ -5768,7 +5769,17 @@ private void DataGrid_LostFocus(object sender, RoutedEventArgs e) focusedDependencyObject = parent; } - if (focusLeftDataGrid) + if (this.EditingRow != null && this.EditingColumnIndex != -1) + { + editingColumn = this.ColumnsItemsInternal[this.EditingColumnIndex]; + + if (focusLeftDataGrid && editingColumn is DataGridTemplateColumn) + { + dataGridWillReceiveRoutedEvent = false; + } + } + + if (focusLeftDataGrid && !(editingColumn is DataGridTemplateColumn)) { this.ContainsFocus = false; if (this.EditingRow != null) From 99d587f939226e3413009acf688486ebf0522d23 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 2 Sep 2021 15:55:09 -0700 Subject: [PATCH 188/200] Enhance DataGrid Sample --- .../Assets/mtns.csv | 234 +++++++++--------- .../Data/DataGridDataItem.cs | 2 +- .../Data/DataGridDataSource.cs | 4 +- .../SamplePages/DataGrid/DataGridCode.bind | 16 +- .../SamplePages/DataGrid/DataGridPage.xaml | 10 +- .../SamplePages/DataGrid/DataGridPage.xaml.cs | 2 +- .../SamplePages/XamlOnlyPage.xaml | 10 +- 7 files changed, 146 insertions(+), 132 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Assets/mtns.csv b/Microsoft.Toolkit.Uwp.SampleApp/Assets/mtns.csv index 7b7568567d7..2f7eb6fee3d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Assets/mtns.csv +++ b/Microsoft.Toolkit.Uwp.SampleApp/Assets/mtns.csv @@ -1,117 +1,117 @@ -1,Mount Everest,8848,Mahalangur Himalaya,27d59m17sN 86d55m31sE,8848,none,1953,>>145 (121) -2,K2/Qogir,8611,Baltoro Karakoram,35d52m53sN 76d30m48sE,4017,Mount Everest,1954,45 (44) -3,Kangchenjunga,8586,Kangchenjunga Himalaya,27d42m12sN 88d08m51sE *,3922,Mount Everest,1955,38 (24) -4,Lhotse,8516,Mahalangur Himalaya,27d57m42sN 86d55m59sE,610,Mount Everest,1956,26 (26) -5,Makalu,8485,Mahalangur Himalaya,27d53m23sN 87d5m20sE,2386,Mount Everest,1955,45 (52) -6,Cho Oyu,8188,Mahalangur Himalaya,28d05m39sN 86d39m39sE,2340,Mount Everest,1954,79 (28) -7,Dhaulagiri I,8167,Dhaulagiri Himalaya,28d41m48sN 83d29m35sE,3357,K2,1960,51 (39) -8,Manaslu,8163,Manaslu Himalaya,28d33m00sN 84d33m35sE,3092,Cho Oyu,1956,49 (45) -9,Nanga Parbat,8126,Nanga Parbat Himalaya,35d14m14sN 74d35m21sE,4608,Dhaulagiri,1953,52 (67) -10,Annapurna I,8091,Annapurna Himalaya,28d35m44sN 83d49m13sE,2984,Cho Oyu,1950,36 (47) -11,Gasherbrum I,8080,Baltoro Karakoram,35d43m28sN 76d41m47sE,2155,K2,1958,31 (16) -12,Broad Peak/K3,8051,Baltoro Karakoram,35d48m38sN 76d34m06sE,1701,Gasherbrum I,1957,39 (19) -13,Gasherbrum II/K4,8034,Baltoro Karakoram,35d45m28sN 76d39m12sE,1523,Gasherbrum I,1956,54 (12) -14,Shishapangma,8027,Jugal Himalaya,28d21m12sN 85d46m43sE,2897,Cho Oyu,1964,43 (19) -15,Gyachung Kang,7952,Mahalangur Himalaya,28d05m53sN 86d44m42sE,700,Cho Oyu,1964,5 (3) -15,Gasherbrum III,7946,Baltoro Karakoram,35d45m33sN 76d38m30sE,355,Gasherbrum II,1975,2 (2) -16,Annapurna II,7937,Annapurna Himalaya,28d32m05sN 84d07m19sE,2437,Annapurna I,1960,6 (19) -17,Gasherbrum IV,7932,Baltoro Karakoram,35d45m38sN 76d36m58sE,715,Gasherbrum III,1958,4 (11) -18,Himalchuli,7893,Manaslu Himalaya,28d26m12sN 84d38m23sE *,1633,Manaslu,1960,6 (12) -19,Distaghil Sar,7884,Hispar Karakoram,36d19m33sN 75d11m16sE,2525,K2,1960,3 (5) -20,Ngadi Chuli,7871,Manaslu Himalaya,28d30m12sN 84d34m00sE,1020,Manaslu,1970,2 (6) -20,Nuptse,7864,Mahalangur Himalaya,27d58m03sN 86d53m13sE,319,Lhotse,1961,5 (12) -21,Khunyang Chhish,7823,Hispar Karakoram,36d12m19sN 75d12m28sE *,1765,Distaghil Sar,1971,2 (6) -22,Masherbrum/K1,7821,Masherbrum Karakoram,35d38m28sN 76d18m21sE,2457,Gasherbrum I,1960,4 (9) -23,Nanda Devi,7816,Garhwal Himalaya,30d22m33sN 79d58m15sE,3139,Dhaulagiri,1936,14 (12) -24,Chomo Lonzo,7804,Mahalangur Himalaya,27d55m50sN 87d06m28sE,590,Makalu,1954,3 (1) -25,Batura Sar,7795,Batura Karakoram,36d30m37sN 74d31m21sE,3118,Distaghil Sar,1976,4 (6) -26,Kanjut Sar,7790,Hispar Karakoram,36d12m20sN 75d25m01sE,1690,Khunyang Chhish,1959,2 (1) -27,Rakaposhi,7788,Rakaposhi-Haramosh Karakoram,36d08m33sN 74d29m22sE,2818,Khunyang Chhish,1958,8 (13) -28,Namcha Barwa,7782,Assam Himalaya,29d37m52sN 95d03m19sE,4106,Kangchenjunga,1992,1 (2) -29,Kamet,7756,Garhwal Himalaya,30d55m12sN 79d35m30sE *,2825,Nanda Devi,1931,23 (14) -30,Dhaulagiri II,7751,Dhaulagiri Himalaya,28d45m46sN 83d23m18sE,2396,Dhaulagiri,1971,4 (11) -31,Saltoro Kangri/K10,7742,Saltoro Karakoram,35d23m57sN 76d50m53sE *,2160,Gasherbrum I,1962,2 (1) -32,Jannu,7711,Kangchenjunga Himalaya,27d40m56sN 88d02m40sE *,1036,Kangchenjunga,1962,17 (12) -33,Tirich Mir,7708,Hindu Kush,36d15m19sN 71d50m30sE *,3910,Batura Sar,1950,20 (11) -33,Molamenqing,7703,Langtang Himalaya,28d21m18sN 85d48m35sE,430,Shishapangma,1981,1 (0) -34,Gurla Mandhata,7694,Nalakankar Himalaya,30d26m19sN 81d17m48sE,2788,Dhaulagiri,1985,6 (4) -35,Saser Kangri I/K22,7672,Saser Karakoram,34d52m00sN 77d45m09sE,2304,Gasherbrum I,1973,6 (4) -36,Chogolisa,7665,Masherbrum Karakoram,35d36m47sN 76d34m29sE,1624,Masherbrum,1975,4 (2) -36,Dhaulagiri IV,7661,Dhaulagiri Himalaya,28d44m09sN 83d18m55sE,469,Dhaulagiri II,1975,2 (10) -37,Kongur Tagh,7649,Kongur Shan Kunlun,38d35m36sN 75d18m48sE,3585,Distaghil Sar,1981,2 (4) -37,Dhaulagiri V,7618,Dhaulagiri Himalaya,28d44m02sN 83d21m41sE *,340,Dhaulagiri IV,1975,2 (3) -38,Shispare,7611,Batura Karakoram,36d26m26sN 74d40m51sE,1240,Batura Sar,1974,3 (1) -39,Trivor,7577,Hispar Karakoram,36d17m15sN 75d05m06sE *,980,Distaghil Sar,1960,2 (5) -40,Gangkhar Puensum,7570,Kula Kangri Himalaya,28d02m50sN 90d27m19sE *,2995,Kangchenjunga,0,0 (3) -41,Gongga Shan,7556,Daxue Shan,29d35m43sN 101d52m47sE,3642,Mount Everest,1932,6 (7) -42,Annapurna III,7555,Annapurna Himalaya,28d35m06sN 83d59m24sE,703,Annapurna I,1961,10 (17) -43,Muztagh Ata,7546,Muztagata Kunlun,38d16m33sN 75d06m58sE,2735,Kongur Tagh,1956,Many -44,Skyang Kangri,7545,Baltoro Karakoram,35d55m35sN 76d34m03sE,1085,K2,1976,1 (2) -45,Changtse,7543,Mahalangur Himalaya,28d01m29sN 86d54m51sE,520,Mount Everest,1982,9 (9) -46,Kula Kangri,7538,Kula Kangri Himalaya,28d13m37sN 90d36m59sE,1650,Gangkhar Puensum,1986,3 (2) -47,Kongur Tiube,7530,Kongur Shan Kunlun,38d36m57sN 75d11m45sE,840,Kongur Tagh,1956,2 (3) -48,Mamostong Kangri,7516,Rimo Karakoram,35d08m31sN 77d34m39sE,1803,Gasherbrum I,1984,5 (0) -49,Saser Kangri II E,7513,Saser Karakoram,34d48m17sN 77d48m24sE,1450,Saser Kangri I,2011,0 (0)[10] -50,Ismoil Somoni Peak,7495,Pamir (Akademiya Nauk Range),38d56m35sN 72d00m57sE,3402,Muztagh Ata,1933, -51,Saser Kangri III,7495,Saser Karakoram,34d50m44sN 77d47m06sE,850,Saser Kangri I,1986,1 (0) -52,Noshaq,7492,Hindu Kush,36d25m56sN 71d49m43sE,2024,Tirich Mir,1960,33 (3) -53,Pumari Chhish,7492,Hispar Karakoram,36d12m41sN 75d15m01sE,890,Khunyang Chhish,1979,1 (2) -54,Pasu Sar,7476,Batura Karakoram,36d29m16sN 74d35m16sE,645,Batura Sar,1994,1 (0) -55,Yukshin Gardan Sar,7469,Hispar Karakoram,36d15m04sN 75d22m29sE,1313,Pumari Chhish,1984,4 (1) -56,Teram Kangri I,7462,Siachen Karakoram,35d34m48sN 77d04m42sE,1702,Gasherbrum I,1975,2 (0) -57,Jongsong Peak,7462,Kangchenjunga Himalaya,27d52m54sN 88d08m09sE,1298,Kangchenjunga,1930,2 (3) -58,Malubiting,7458,Rakaposhi-Haramosh Karakoram,36d00m12sN 74d52m31sE,2193,Rakaposhi,1971,2 (6) -59,Gangapurna,7455,Annapurna Himalaya,28d36m18sN 83d57m49sE,563,Annapurna III,1965,8 (13) -60,Jengish Chokusu,7439,Tian Shan,42d02m05sN 80d07m47sE,4148,Ismail Samani Peak,1938, -61,K12,7428,Saltoro Karakoram,35d17m45sN 77d01m20sE,1978,Saltoro Kangri,1974,4 (2) -62,Yangra,7422,Ganesh Himalaya,28d23m29sN 85d07m38sE,2352,Manaslu,1955,1 (6) -63,Sia Kangri,7422,Siachen Karakoram,35d39m48sN 76d45m42sE,640,Gasherbrum I,1934,6 (0) -64,Momhil Sar,7414,Hispar Karakoram,36d19m04sN 75d02m11sE *,980,Trivor,1964,2 (6) -65,Kabru N,7412,Kangchenjunga Himalaya,27d38m02sN 88d07m00sE,780,Kangchenjunga,1994,1 (2)[11] -66,Skil Brum,7410,Baltoro Karakoram,35d51m03sN 76d25m43sE,1152,K2,1957,2 (1) -67,Haramosh,7409,Rakaposhi Karakoram,35d50m24sN 74d53m51sE,2277,Malubiting,1958,4 (3) -68,Istor-o-Nal,7403,Hindu Kush,36d22m32sN 71d53m54sE,1040,Noshaq,1969,4 (5) -69,Ghent Kangri,7401,Saltoro Karakoram,35d31m04sN 76d48m02sE,1493,Saltoro Kangri,1961,4 (0) -70,Ultar Sar,7388,Batura Karakoram,36d23m27sN 74d43m00sE,700,Shispare,1996,2 (5) -71,Rimo I,7385,Rimo Karakoram,35d21m18sN 77d22m08sE,1438,Teram Kangri I,1988,1 (3) -72,Churen Himal,7385,Dhaulagiri Himalaya,28d44m05sN 83d13m03sE,600,Dhaulagiri IV,1970,3 (0) -73,Teram Kangri III,7382,Siachen Karakoram,35d35m59sN 77d02m53sE,520,Teram Kangri I,1979,1 (0) -74,Sherpi Kangri,7380,Saltoro Karakoram,35d27m58sN 76d46m53sE *,1000,Ghent Kangri,1976,1 (1) -75,Labuche Kang,7367,Labuche Himalaya,28d18m15sN 86d21m03sE,1957,Cho Oyu,1987,1 (0) -76,Kirat Chuli,7362,Kangchenjunga Himalaya,27d47m16sN 88d11m43sE,1168,Kangchenjunga,1939,1 (6) -76,Abi Gamin,7355,Garhwal Himalaya,30d55m57sN 79d36m09sE,217,Kamet,1950,17 (2) -77,Nangpai Gosum,7350,Mahalangur Himalaya,28d04m24sN 86d36m51sE,500,Cho Oyu,1996,3 (1) -77,Gimmigela,7350,Kangchenjunga Himalaya,27d44m27sN 88d09m31sE,432,Kangchenjunga,1994,3 (1) -78,Saraghrar,7349,Hindu Kush,36d32m51sN 72d06m54sE,1979,Noshaq,1959,2 (3) -79,Jomolhari,7326,Jomolhari Himalaya,27d49m36sN 89d16m04sE *,2077,Gangkhar Puensum,1937,4 (0) -80,Chamlang,7321,Mahalangur Himalaya,27d46m30sN 86d58m47sE,1240,Lhotse,1961,7 (1) -81,Chongtar,7315,Baltoro Karakoram,35d54m55sN 76d25m45sE,1300,Skil Brum,1994,1 (1) -82,Baltoro Kangri,7312,Masherbrum Karakoram,35d38m21sN 76d40m24sE,1200,Chogolisa,1976,1 (0) -83,Siguang Ri,7309,Mahalangur Himalaya,28d08m50sN 86d41m06sE,650,Cho Oyu,1989,2 (1) -84,The Crown,7295,Yengisogat Karakoram,36d06m24sN 76d12m21sE,1919,Skil Brum (K2),1993,1 (3) -85,Gyala Peri,7294,Assam Himalaya,29d48m52sN 94d58m07sE,2942,Mount Everest,1986,1 (0) -86,Porong Ri,7292,Langtang Himalaya,28d23m22sN 85d43m12sE,520,Shisha Pangma,1982,5 (0) -87,Baintha Brakk,7285,Panmah Karakoram,35d56m51sN 75d45m12sE *,1891,Kanjut Sar,1977,3 (13) -88,Yutmaru Sar,7283,Hispar Karakoram,36d13m35sN 75d22m02sE,620,Yukshin Gardan Sar,1980,1 (1) -89,Baltistan Peak/K6,7282,Masherbrum Karakoram,35d25m06sN 76d33m06sE,1962,Chogolisa,1970,1 (3) -90,Kangpenqing,7281,Baiku Himalaya,28d33m03sN 85d32m44sE,1340,Shisha Pangma,1982,1 (1) -91,Muztagh Tower,7276,Baltoro Karakoram,35d49m40sN 76d21m40sE,1710,Skil Brum,1956,4 (2) -92,Mana,7272,Garhwal Himalaya,30d52m50sN 79d36m55sE,730,Kamet,1937,7 (3) -92,Dhaulagiri VI,7268,Dhaulagiri Himalaya,28d42m31sN 83d16m27sE,485,Dhaulagiri IV,1970,5 (0) -93,Diran,7266,Rakaposhi-Haramosh Karakoram,36d07m13sN 74d39m42sE,1325,Malubiting,1968,12 (8) -94,Labuche Kang III/East[12],7250,Labuche Himalaya,28d18m05sN 86d23m02sE,570,Labuche Kang,0,0 (0) -95,Putha Hiunchuli,7246,Dhaulagiri Himalaya,28d44m52sN 83d08m46sE,1151,Churen Himal,1954,11 (5) -96,Apsarasas Kangri,7245,Siachen Karakoram,35d32m19sN 77d08m55sE,635,Teram Kangri I,1976,2 (0) -97,Mukut Parbat,7242,Garhwal Himalaya,30d56m57sN 79d34m12sE,840,Kamet,1951,2 (1) -98,Rimo III,7233,Rimo Karakoram,35d22m31sN 77d21m42sE,615,Rimo I,1985,1 (0) -99,Langtang Lirung,7227,Langtang Himalaya,28d15m22sN 85d31m01sE,1525,Shisha Pangma,1978,14 (13) -100,Karjiang,7221,Kula Kangri Himalaya,28d15m27sN 90d38m49sE,880,Kula Kangri,0,0 (2) -101,Annapurna Dakshin,7219,Annapurna Himalaya,28d31m06sN 83d48m22sE,775,Annapurna,1964,10 (16) -102,Khartaphu,7213,Mahalangur Himalaya,28d03m49sN 86d58m39sE,712,Mount Everest,1935,1 (0) -103,Tongshanjiabu,7207,Lunana Himalaya,28d11m12sN 89d57m27sE,1757,Gangkar Puensum,0,0 (0) -104,Malangutti Sar,7207,Hispar Karakoram,36d21m47sN 75d08m57sE,515,Distaghil Sar,1985,1 (0) -105,Noijin Kangsang,7206,Nagarze Himalaya,28d56m48sN 90d10m42sE,2160,Tongshanjiabu,1986,4 (1) -106,Langtang Ri,7205,Langtang Himalaya,28d22m53sN 85d41m01sE,650,Porong Ri,1981,4 (0) -107,Kangphu Kang,7204,Lunana Himalaya,28d09m20sN 90d03m48sE,1200,Tongshanjiabu,2002,1 (0) -108,Singhi Kangri,7202,Siachen Karakoram,35d35m59sN 76d59m01sE,790,Teram Kangri III,1976,2 (0) -109,Lupghar Sar,7200,Hispar Karakoram,36d21m01sN 75d02m13sE *,730,Momhil Sar,1979,1 (0) +1,Mount Everest,8848,Mahalangur Himalaya,27d59m17sN 86d55m31sE,8848,none,05/29/1953,>>145 (121) +2,K2/Qogir,8611,Baltoro Karakoram,35d52m53sN 76d30m48sE,4017,Mount Everest,07/31/1954,45 (44) +3,Kangchenjunga,8586,Kangchenjunga Himalaya,27d42m12sN 88d08m51sE *,3922,Mount Everest,05/25/1955,38 (24) +4,Lhotse,8516,Mahalangur Himalaya,27d57m42sN 86d55m59sE,610,Mount Everest,05/18/1956,26 (26) +5,Makalu,8485,Mahalangur Himalaya,27d53m23sN 87d5m20sE,2386,Mount Everest,05/15/1955,45 (52) +6,Cho Oyu,8188,Mahalangur Himalaya,28d05m39sN 86d39m39sE,2340,Mount Everest,10/19/1954,79 (28) +7,Dhaulagiri I,8167,Dhaulagiri Himalaya,28d41m48sN 83d29m35sE,3357,K2,05/13/1960,51 (39) +8,Manaslu,8163,Manaslu Himalaya,28d33m00sN 84d33m35sE,3092,Cho Oyu,05/9/1956,49 (45) +9,Nanga Parbat,8126,Nanga Parbat Himalaya,35d14m14sN 74d35m21sE,4608,Dhaulagiri,07/03/1953,52 (67) +10,Annapurna I,8091,Annapurna Himalaya,28d35m44sN 83d49m13sE,2984,Cho Oyu,06/03/1950,36 (47) +11,Gasherbrum I,8080,Baltoro Karakoram,35d43m28sN 76d41m47sE,2155,K2,01/01/1958,31 (16) +12,Broad Peak/K3,8051,Baltoro Karakoram,35d48m38sN 76d34m06sE,1701,Gasherbrum I,01/01/1957,39 (19) +13,Gasherbrum II/K4,8034,Baltoro Karakoram,35d45m28sN 76d39m12sE,1523,Gasherbrum I,01/01/1956,54 (12) +14,Shishapangma,8027,Jugal Himalaya,28d21m12sN 85d46m43sE,2897,Cho Oyu,01/01/1964,43 (19) +15,Gyachung Kang,7952,Mahalangur Himalaya,28d05m53sN 86d44m42sE,700,Cho Oyu,01/01/1964,5 (3) +15,Gasherbrum III,7946,Baltoro Karakoram,35d45m33sN 76d38m30sE,355,Gasherbrum II,01/01/1975,2 (2) +16,Annapurna II,7937,Annapurna Himalaya,28d32m05sN 84d07m19sE,2437,Annapurna I,01/01/1960,6 (19) +17,Gasherbrum IV,7932,Baltoro Karakoram,35d45m38sN 76d36m58sE,715,Gasherbrum III,01/01/1958,4 (11) +18,Himalchuli,7893,Manaslu Himalaya,28d26m12sN 84d38m23sE *,1633,Manaslu,01/01/1960,6 (12) +19,Distaghil Sar,7884,Hispar Karakoram,36d19m33sN 75d11m16sE,2525,K2,01/01/1960,3 (5) +20,Ngadi Chuli,7871,Manaslu Himalaya,28d30m12sN 84d34m00sE,1020,Manaslu,01/01/1970,2 (6) +20,Nuptse,7864,Mahalangur Himalaya,27d58m03sN 86d53m13sE,319,Lhotse,01/01/1961,5 (12) +21,Khunyang Chhish,7823,Hispar Karakoram,36d12m19sN 75d12m28sE *,1765,Distaghil Sar,01/01/1971,2 (6) +22,Masherbrum/K1,7821,Masherbrum Karakoram,35d38m28sN 76d18m21sE,2457,Gasherbrum I,01/01/1960,4 (9) +23,Nanda Devi,7816,Garhwal Himalaya,30d22m33sN 79d58m15sE,3139,Dhaulagiri,01/01/1936,14 (12) +24,Chomo Lonzo,7804,Mahalangur Himalaya,27d55m50sN 87d06m28sE,590,Makalu,01/01/1954,3 (1) +25,Batura Sar,7795,Batura Karakoram,36d30m37sN 74d31m21sE,3118,Distaghil Sar,01/01/1976,4 (6) +26,Kanjut Sar,7790,Hispar Karakoram,36d12m20sN 75d25m01sE,1690,Khunyang Chhish,01/01/1959,2 (1) +27,Rakaposhi,7788,Rakaposhi-Haramosh Karakoram,36d08m33sN 74d29m22sE,2818,Khunyang Chhish,01/01/1958,8 (13) +28,Namcha Barwa,7782,Assam Himalaya,29d37m52sN 95d03m19sE,4106,Kangchenjunga,01/01/1992,1 (2) +29,Kamet,7756,Garhwal Himalaya,30d55m12sN 79d35m30sE *,2825,Nanda Devi,01/01/1931,23 (14) +30,Dhaulagiri II,7751,Dhaulagiri Himalaya,28d45m46sN 83d23m18sE,2396,Dhaulagiri,01/01/1971,4 (11) +31,Saltoro Kangri/K10,7742,Saltoro Karakoram,35d23m57sN 76d50m53sE *,2160,Gasherbrum I,01/01/1962,2 (1) +32,Jannu,7711,Kangchenjunga Himalaya,27d40m56sN 88d02m40sE *,1036,Kangchenjunga,01/01/1962,17 (12) +33,Tirich Mir,7708,Hindu Kush,36d15m19sN 71d50m30sE *,3910,Batura Sar,01/01/1950,20 (11) +33,Molamenqing,7703,Langtang Himalaya,28d21m18sN 85d48m35sE,430,Shishapangma,01/01/1981,1 (0) +34,Gurla Mandhata,7694,Nalakankar Himalaya,30d26m19sN 81d17m48sE,2788,Dhaulagiri,01/01/1985,6 (4) +35,Saser Kangri I/K22,7672,Saser Karakoram,34d52m00sN 77d45m09sE,2304,Gasherbrum I,01/01/1973,6 (4) +36,Chogolisa,7665,Masherbrum Karakoram,35d36m47sN 76d34m29sE,1624,Masherbrum,01/01/1975,4 (2) +36,Dhaulagiri IV,7661,Dhaulagiri Himalaya,28d44m09sN 83d18m55sE,469,Dhaulagiri II,01/01/1975,2 (10) +37,Kongur Tagh,7649,Kongur Shan Kunlun,38d35m36sN 75d18m48sE,3585,Distaghil Sar,01/01/1981,2 (4) +37,Dhaulagiri V,7618,Dhaulagiri Himalaya,28d44m02sN 83d21m41sE *,340,Dhaulagiri IV,01/01/1975,2 (3) +38,Shispare,7611,Batura Karakoram,36d26m26sN 74d40m51sE,1240,Batura Sar,01/01/1974,3 (1) +39,Trivor,7577,Hispar Karakoram,36d17m15sN 75d05m06sE *,980,Distaghil Sar,01/01/1960,2 (5) +40,Gangkhar Puensum,7570,Kula Kangri Himalaya,28d02m50sN 90d27m19sE *,2995,Kangchenjunga,01/01/1000,0 (3) +41,Gongga Shan,7556,Daxue Shan,29d35m43sN 101d52m47sE,3642,Mount Everest,01/01/1932,6 (7) +42,Annapurna III,7555,Annapurna Himalaya,28d35m06sN 83d59m24sE,703,Annapurna I,01/01/1961,10 (17) +43,Muztagh Ata,7546,Muztagata Kunlun,38d16m33sN 75d06m58sE,2735,Kongur Tagh,01/01/1956,Many +44,Skyang Kangri,7545,Baltoro Karakoram,35d55m35sN 76d34m03sE,1085,K2,01/01/1976,1 (2) +45,Changtse,7543,Mahalangur Himalaya,28d01m29sN 86d54m51sE,520,Mount Everest,01/01/1982,9 (9) +46,Kula Kangri,7538,Kula Kangri Himalaya,28d13m37sN 90d36m59sE,1650,Gangkhar Puensum,01/01/1986,3 (2) +47,Kongur Tiube,7530,Kongur Shan Kunlun,38d36m57sN 75d11m45sE,840,Kongur Tagh,01/01/1956,2 (3) +48,Mamostong Kangri,7516,Rimo Karakoram,35d08m31sN 77d34m39sE,1803,Gasherbrum I,01/01/1984,5 (0) +49,Saser Kangri II E,7513,Saser Karakoram,34d48m17sN 77d48m24sE,1450,Saser Kangri I,01/01/2011,0 (0)[10] +50,Ismoil Somoni Peak,7495,Pamir (Akademiya Nauk Range),38d56m35sN 72d00m57sE,3402,Muztagh Ata,01/01/1933, +51,Saser Kangri III,7495,Saser Karakoram,34d50m44sN 77d47m06sE,850,Saser Kangri I,01/01/1986,1 (0) +52,Noshaq,7492,Hindu Kush,36d25m56sN 71d49m43sE,2024,Tirich Mir,01/01/1960,33 (3) +53,Pumari Chhish,7492,Hispar Karakoram,36d12m41sN 75d15m01sE,890,Khunyang Chhish,01/01/1979,1 (2) +54,Pasu Sar,7476,Batura Karakoram,36d29m16sN 74d35m16sE,645,Batura Sar,01/01/1994,1 (0) +55,Yukshin Gardan Sar,7469,Hispar Karakoram,36d15m04sN 75d22m29sE,1313,Pumari Chhish,01/01/1984,4 (1) +56,Teram Kangri I,7462,Siachen Karakoram,35d34m48sN 77d04m42sE,1702,Gasherbrum I,01/01/1975,2 (0) +57,Jongsong Peak,7462,Kangchenjunga Himalaya,27d52m54sN 88d08m09sE,1298,Kangchenjunga,01/01/1930,2 (3) +58,Malubiting,7458,Rakaposhi-Haramosh Karakoram,36d00m12sN 74d52m31sE,2193,Rakaposhi,01/01/1971,2 (6) +59,Gangapurna,7455,Annapurna Himalaya,28d36m18sN 83d57m49sE,563,Annapurna III,01/01/1965,8 (13) +60,Jengish Chokusu,7439,Tian Shan,42d02m05sN 80d07m47sE,4148,Ismail Samani Peak,01/01/1938, +61,K12,7428,Saltoro Karakoram,35d17m45sN 77d01m20sE,1978,Saltoro Kangri,01/01/1974,4 (2) +62,Yangra,7422,Ganesh Himalaya,28d23m29sN 85d07m38sE,2352,Manaslu,01/01/1955,1 (6) +63,Sia Kangri,7422,Siachen Karakoram,35d39m48sN 76d45m42sE,640,Gasherbrum I,01/01/1934,6 (0) +64,Momhil Sar,7414,Hispar Karakoram,36d19m04sN 75d02m11sE *,980,Trivor,01/01/1964,2 (6) +65,Kabru N,7412,Kangchenjunga Himalaya,27d38m02sN 88d07m00sE,780,Kangchenjunga,01/01/1994,1 (2)[11] +66,Skil Brum,7410,Baltoro Karakoram,35d51m03sN 76d25m43sE,1152,K2,01/01/1957,2 (1) +67,Haramosh,7409,Rakaposhi Karakoram,35d50m24sN 74d53m51sE,2277,Malubiting,01/01/1958,4 (3) +68,Istor-o-Nal,7403,Hindu Kush,36d22m32sN 71d53m54sE,1040,Noshaq,01/01/1969,4 (5) +69,Ghent Kangri,7401,Saltoro Karakoram,35d31m04sN 76d48m02sE,1493,Saltoro Kangri,01/01/1961,4 (0) +70,Ultar Sar,7388,Batura Karakoram,36d23m27sN 74d43m00sE,700,Shispare,01/01/1996,2 (5) +71,Rimo I,7385,Rimo Karakoram,35d21m18sN 77d22m08sE,1438,Teram Kangri I,01/01/1988,1 (3) +72,Churen Himal,7385,Dhaulagiri Himalaya,28d44m05sN 83d13m03sE,600,Dhaulagiri IV,01/01/1970,3 (0) +73,Teram Kangri III,7382,Siachen Karakoram,35d35m59sN 77d02m53sE,520,Teram Kangri I,01/01/1979,1 (0) +74,Sherpi Kangri,7380,Saltoro Karakoram,35d27m58sN 76d46m53sE *,1000,Ghent Kangri,01/01/1976,1 (1) +75,Labuche Kang,7367,Labuche Himalaya,28d18m15sN 86d21m03sE,1957,Cho Oyu,01/01/1987,1 (0) +76,Kirat Chuli,7362,Kangchenjunga Himalaya,27d47m16sN 88d11m43sE,1168,Kangchenjunga,01/01/1939,1 (6) +76,Abi Gamin,7355,Garhwal Himalaya,30d55m57sN 79d36m09sE,217,Kamet,01/01/1950,17 (2) +77,Nangpai Gosum,7350,Mahalangur Himalaya,28d04m24sN 86d36m51sE,500,Cho Oyu,01/01/1996,3 (1) +77,Gimmigela,7350,Kangchenjunga Himalaya,27d44m27sN 88d09m31sE,432,Kangchenjunga,01/01/1994,3 (1) +78,Saraghrar,7349,Hindu Kush,36d32m51sN 72d06m54sE,1979,Noshaq,01/01/1959,2 (3) +79,Jomolhari,7326,Jomolhari Himalaya,27d49m36sN 89d16m04sE *,2077,Gangkhar Puensum,01/01/1937,4 (0) +80,Chamlang,7321,Mahalangur Himalaya,27d46m30sN 86d58m47sE,1240,Lhotse,01/01/1961,7 (1) +81,Chongtar,7315,Baltoro Karakoram,35d54m55sN 76d25m45sE,1300,Skil Brum,01/01/1994,1 (1) +82,Baltoro Kangri,7312,Masherbrum Karakoram,35d38m21sN 76d40m24sE,1200,Chogolisa,01/01/1976,1 (0) +83,Siguang Ri,7309,Mahalangur Himalaya,28d08m50sN 86d41m06sE,650,Cho Oyu,01/01/1989,2 (1) +84,The Crown,7295,Yengisogat Karakoram,36d06m24sN 76d12m21sE,1919,Skil Brum (K2),01/01/1993,1 (3) +85,Gyala Peri,7294,Assam Himalaya,29d48m52sN 94d58m07sE,2942,Mount Everest,01/01/1986,1 (0) +86,Porong Ri,7292,Langtang Himalaya,28d23m22sN 85d43m12sE,520,Shisha Pangma,01/01/1982,5 (0) +87,Baintha Brakk,7285,Panmah Karakoram,35d56m51sN 75d45m12sE *,1891,Kanjut Sar,01/01/1977,3 (13) +88,Yutmaru Sar,7283,Hispar Karakoram,36d13m35sN 75d22m02sE,620,Yukshin Gardan Sar,01/01/1980,1 (1) +89,Baltistan Peak/K6,7282,Masherbrum Karakoram,35d25m06sN 76d33m06sE,1962,Chogolisa,01/01/1970,1 (3) +90,Kangpenqing,7281,Baiku Himalaya,28d33m03sN 85d32m44sE,1340,Shisha Pangma,01/01/1982,1 (1) +91,Muztagh Tower,7276,Baltoro Karakoram,35d49m40sN 76d21m40sE,1710,Skil Brum,01/01/1956,4 (2) +92,Mana,7272,Garhwal Himalaya,30d52m50sN 79d36m55sE,730,Kamet,01/01/1937,7 (3) +92,Dhaulagiri VI,7268,Dhaulagiri Himalaya,28d42m31sN 83d16m27sE,485,Dhaulagiri IV,01/01/1970,5 (0) +93,Diran,7266,Rakaposhi-Haramosh Karakoram,36d07m13sN 74d39m42sE,1325,Malubiting,01/01/1968,12 (8) +94,Labuche Kang III/East[12],7250,Labuche Himalaya,28d18m05sN 86d23m02sE,570,Labuche Kang,01/01/1000,0 (0) +95,Putha Hiunchuli,7246,Dhaulagiri Himalaya,28d44m52sN 83d08m46sE,1151,Churen Himal,01/01/1954,11 (5) +96,Apsarasas Kangri,7245,Siachen Karakoram,35d32m19sN 77d08m55sE,635,Teram Kangri I,01/01/1976,2 (0) +97,Mukut Parbat,7242,Garhwal Himalaya,30d56m57sN 79d34m12sE,840,Kamet,01/01/1951,2 (1) +98,Rimo III,7233,Rimo Karakoram,35d22m31sN 77d21m42sE,615,Rimo I,01/01/1985,1 (0) +99,Langtang Lirung,7227,Langtang Himalaya,28d15m22sN 85d31m01sE,1525,Shisha Pangma,01/01/1978,14 (13) +100,Karjiang,7221,Kula Kangri Himalaya,28d15m27sN 90d38m49sE,880,Kula Kangri,01/01/1000,0 (2) +101,Annapurna Dakshin,7219,Annapurna Himalaya,28d31m06sN 83d48m22sE,775,Annapurna,01/01/1964,10 (16) +102,Khartaphu,7213,Mahalangur Himalaya,28d03m49sN 86d58m39sE,712,Mount Everest,01/01/1935,1 (0) +103,Tongshanjiabu,7207,Lunana Himalaya,28d11m12sN 89d57m27sE,1757,Gangkar Puensum,01/01/1000,0 (0) +104,Malangutti Sar,7207,Hispar Karakoram,36d21m47sN 75d08m57sE,515,Distaghil Sar,01/01/1985,1 (0) +105,Noijin Kangsang,7206,Nagarze Himalaya,28d56m48sN 90d10m42sE,2160,Tongshanjiabu,01/01/1986,4 (1) +106,Langtang Ri,7205,Langtang Himalaya,28d22m53sN 85d41m01sE,650,Porong Ri,01/01/1981,4 (0) +107,Kangphu Kang,7204,Lunana Himalaya,28d09m20sN 90d03m48sE,1200,Tongshanjiabu,01/01/2002,1 (0) +108,Singhi Kangri,7202,Siachen Karakoram,35d35m59sN 76d59m01sE,790,Teram Kangri III,01/01/1976,2 (0) +109,Lupghar Sar,7200,Hispar Karakoram,36d21m01sN 75d02m13sE *,730,Momhil Sar,01/01/1979,1 (0) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs index f942f1f1fe5..1ddf86ddb90 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs @@ -149,7 +149,7 @@ public string Parent_mountain public uint Prominence { get; set; } - public uint First_ascent { get; set; } + public DateTimeOffset First_ascent { get; set; } public string Ascents { get; set; } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataSource.cs b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataSource.cs index 01cc02cb71e..688b172925f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataSource.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataSource.cs @@ -48,8 +48,8 @@ public async Task> GetDataAsync() Coordinates = values[4], Prominence = uint.Parse(values[5]), Parent_mountain = values[6], - First_ascent = uint.Parse(values[7]), - Ascents = values[8] + First_ascent = DateTimeOffset.Parse(values[7]), + Ascents = values[8], }); } } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridCode.bind index 588589bf48d..874762b3798 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridCode.bind @@ -1,12 +1,14 @@ + @@ -85,7 +87,19 @@ + + + + + + + + + + + + - \ No newline at end of file + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml index 807dd5214e6..486f886a39f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml @@ -6,13 +6,5 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - - - - - - - + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs index 5dfc2ff19d8..122526b478c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs @@ -42,7 +42,7 @@ public async void OnXamlRendered(FrameworkElement control) dataGrid.LoadingRowGroup += DataGrid_LoadingRowGroup; dataGrid.ItemsSource = await viewModel.GetDataAsync(); - var comboBoxColumn = dataGrid.Columns.FirstOrDefault(x => x.Tag.Equals("Mountain")) as DataGridComboBoxColumn; + var comboBoxColumn = dataGrid.Columns.FirstOrDefault(x => x.Tag?.Equals("Mountain") == true) as DataGridComboBoxColumn; if (comboBoxColumn != null) { comboBoxColumn.ItemsSource = await viewModel.GetMountains(); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml index 84cacab3aaa..a3a001645f7 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml @@ -54,6 +54,14 @@ + + + + + + + + @@ -90,4 +98,4 @@ - \ No newline at end of file + From e89cac670b11d8dbbabcadb83642b18c16315646 Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:27:14 -0700 Subject: [PATCH 189/200] Add PreparingCellForEdit example to open CalendarDropDown on 2nd click --- .../SamplePages/DataGrid/DataGridPage.xaml.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs index 122526b478c..1b2c55a4829 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs @@ -41,6 +41,7 @@ public async void OnXamlRendered(FrameworkElement control) dataGrid.Sorting += DataGrid_Sorting; dataGrid.LoadingRowGroup += DataGrid_LoadingRowGroup; dataGrid.ItemsSource = await viewModel.GetDataAsync(); + dataGrid.PreparingCellForEdit += DataGrid_PreparingCellForEdit; var comboBoxColumn = dataGrid.Columns.FirstOrDefault(x => x.Tag?.Equals("Mountain") == true) as DataGridComboBoxColumn; if (comboBoxColumn != null) @@ -111,6 +112,15 @@ public async void OnXamlRendered(FrameworkElement control) } } + private void DataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e) + { + if (e.Column is DataGridTemplateColumn column && (string)column?.Tag == "First_ascent" && + e.EditingElement is CalendarDatePicker calendar) + { + calendar.IsCalendarOpen = true; + } + } + private void DataGrid_LoadingRowGroup(object sender, DataGridRowGroupHeaderEventArgs e) { ICollectionViewGroup group = e.RowGroupHeader.CollectionViewGroup; From 9cf163043ee630db3b8f583e50ccb0e2dc8beddb Mon Sep 17 00:00:00 2001 From: "Michael Hawker MSFT (XAML Llama)" <24302614+michael-hawker@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:00:03 -0700 Subject: [PATCH 190/200] Add comment about DateTimeOffset binding from #2493 comment https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/2493#issuecomment-424917060 --- Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs index 1ddf86ddb90..f3d0bae93f2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Data/DataGridDataItem.cs @@ -149,6 +149,7 @@ public string Parent_mountain public uint Prominence { get; set; } + // You need to use DateTimeOffset to get proper binding to the CalendarDatePicker control, DateTime won't work. public DateTimeOffset First_ascent { get; set; } public string Ascents { get; set; } From c5b2bb75b99bb164faf9464212c0c4b509ea8c67 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Fri, 3 Sep 2021 11:28:24 -0700 Subject: [PATCH 191/200] Place DP props on one line --- .../FrameworkElementExtensions.RelativeAncestor.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs index f981e75d6ca..ed44ae0f777 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs @@ -34,10 +34,7 @@ public static void SetAncestor(DependencyObject obj, object value) /// Attached for retrieving a parent for the /// public static readonly DependencyProperty AncestorProperty = - DependencyProperty.RegisterAttached("Ancestor", - typeof(WeakReference), - typeof(FrameworkElementExtensions), - new PropertyMetadata(null)); + DependencyProperty.RegisterAttached("Ancestor", typeof(WeakReference), typeof(FrameworkElementExtensions), new PropertyMetadata(null)); /// /// Gets the Type of Ancestor to look for from this element. @@ -60,10 +57,7 @@ public static void SetAncestorType(DependencyObject obj, Type value) /// Attached for retrieving a parent for the based on the provided in the . /// public static readonly DependencyProperty AncestorTypeProperty = - DependencyProperty.RegisterAttached("AncestorType", - typeof(Type), - typeof(FrameworkElementExtensions), - new PropertyMetadata(null, AncestorType_PropertyChanged)); + DependencyProperty.RegisterAttached("AncestorType", typeof(Type), typeof(FrameworkElementExtensions), new PropertyMetadata(null, AncestorType_PropertyChanged)); private static void AncestorType_PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { From 9f8527c00421c8c99a66f029b79358ad34c094a8 Mon Sep 17 00:00:00 2001 From: Rosario Pulella Date: Fri, 3 Sep 2021 15:15:17 -0400 Subject: [PATCH 192/200] UITests: Remove ref to incorrect version of winui --- UITests/UITests.App/UITests.App.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/UITests/UITests.App/UITests.App.csproj b/UITests/UITests.App/UITests.App.csproj index 52c7d1b9801..696475fbbae 100644 --- a/UITests/UITests.App/UITests.App.csproj +++ b/UITests/UITests.App/UITests.App.csproj @@ -168,9 +168,6 @@ 6.2.12 - - 2.6.1 - 0.0.4 From 55316d49ba36aee11ac9c94542a7046a2a889698 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Tue, 7 Sep 2021 11:05:23 -0700 Subject: [PATCH 193/200] Remove blank line --- .../FrameworkElementExtensions.RelativeAncestor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs index ed44ae0f777..3d06f444e59 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/FrameworkElement/FrameworkElementExtensions.RelativeAncestor.cs @@ -68,7 +68,6 @@ private static void AncestorType_PropertyChanged(DependencyObject obj, Dependenc if (args.NewValue != null) { fe.Loaded += FrameworkElement_Loaded; - if (fe.Parent != null) { FrameworkElement_Loaded(fe, null); From 762818cf7ef94d14f8ec000e8f2fb2489226ea98 Mon Sep 17 00:00:00 2001 From: Darren Batchelor Date: Tue, 7 Sep 2021 17:30:00 -0700 Subject: [PATCH 194/200] Add test for grid unload --- .../Test_FrameworkElementExtensions.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs index 4766eea9dd7..57d0e2d4f04 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_FrameworkElementExtensions.cs @@ -11,6 +11,7 @@ namespace UnitTests.Extensions { + [TestClass] public class Test_FrameworkElementExtensions : VisualUITestBase { [TestCategory("FrameworkElementExtensions")] @@ -50,5 +51,45 @@ public async Task Test_Ancestor_WeakReference() Assert.IsNull(btn.Parent); }); } + + [TestCategory("FrameworkElementExtensions")] + [TestMethod] + public async Task Test_Ancestor_WeakRef_UnloadGrid() + { + // Need to sim loading the control, and not just load the XAML via XamlReader + var view = App.DispatcherQueue.EnqueueAsync(async () => + { + var treeRoot = XamlReader.Load( + @" + + +