From 40b615ca6205a7e8cf2299059389cf5c5b1711c6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 13:19:50 +0100 Subject: [PATCH 01/29] Restructured the FindDescendant extensions --- .../Pages/SampleController.xaml.cs | 2 +- .../AdaptiveGridViewPage.xaml.cs | 2 +- .../CameraPreview/CameraPreviewPage.xaml.cs | 4 +- .../SamplePages/Carousel/CarouselPage.xaml.cs | 2 +- .../SamplePages/DataGrid/DataGridPage.xaml.cs | 4 +- .../SamplePages/Loading/LoadingPage.xaml.cs | 2 +- .../StaggeredLayoutPage.xaml.cs | 2 +- .../IsNullOrEmptyStateTriggerPage.xaml.cs | 6 +- .../WrapLayout/WrapLayoutPage.xaml.cs | 4 +- .../TokenizingTextBox/TokenizingTextBox.cs | 2 +- .../Extensions/Tree/VisualTree.cs | 128 +++++++++++------- .../Microsoft.Toolkit.Uwp.UI.csproj | 2 +- 12 files changed, 95 insertions(+), 65 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml.cs index 02d03672b3a..d2fcb96fee7 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Pages/SampleController.xaml.cs @@ -517,7 +517,7 @@ private void UpdateXamlRender(string text) if (CurrentSample.HasType) { - root = SamplePage?.FindDescendantByName("XamlRoot"); + root = SamplePage?.FindDescendant("XamlRoot"); if (root is Panel) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/AdaptiveGridView/AdaptiveGridViewPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/AdaptiveGridView/AdaptiveGridViewPage.xaml.cs index 45e6f47a270..0407bb6ac1f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/AdaptiveGridView/AdaptiveGridViewPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/AdaptiveGridView/AdaptiveGridViewPage.xaml.cs @@ -27,7 +27,7 @@ public AdaptiveGridViewPage() public async void OnXamlRendered(FrameworkElement control) { - _adaptiveGridViewControl = control.FindDescendantByName("AdaptiveGridViewControl") as AdaptiveGridView; + _adaptiveGridViewControl = control.FindDescendant("AdaptiveGridViewControl") as AdaptiveGridView; if (_adaptiveGridViewControl != null) { var allPhotos = await new Data.PhotosDataSource().GetItemsAsync(); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CameraPreview/CameraPreviewPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CameraPreview/CameraPreviewPage.xaml.cs index 00d2685e00b..454f16d815d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CameraPreview/CameraPreviewPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CameraPreview/CameraPreviewPage.xaml.cs @@ -54,14 +54,14 @@ public async void OnXamlRendered(FrameworkElement control) _cameraPreviewControl.CameraHelper.FrameArrived += CameraPreviewControl_FrameArrived; } - _imageControl = control.FindDescendantByName("CurrentFrameImage") as Image; + _imageControl = control.FindDescendant("CurrentFrameImage") as Image; if (_imageControl != null) { _softwareBitmapSource = new SoftwareBitmapSource(); _imageControl.Source = _softwareBitmapSource; } - _errorMessageText = control.FindDescendantByName("ErrorMessage") as TextBlock; + _errorMessageText = control.FindDescendant("ErrorMessage") as TextBlock; semaphoreSlim.Release(); } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Carousel/CarouselPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Carousel/CarouselPage.xaml.cs index 99b59038717..83408598b20 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Carousel/CarouselPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Carousel/CarouselPage.xaml.cs @@ -29,7 +29,7 @@ public CarouselPage() public async void OnXamlRendered(FrameworkElement control) { - carouselControl = control.FindDescendantByName("CarouselControl") as Carousel; + carouselControl = control.FindDescendant("CarouselControl") as Carousel; carouselControl.ItemsSource = await new Data.PhotosDataSource().GetItemsAsync(); } } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs index e3e480fdabe..27650a982f8 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs @@ -35,7 +35,7 @@ public async void OnXamlRendered(FrameworkElement control) dataGrid.LoadingRowGroup -= DataGrid_LoadingRowGroup; } - dataGrid = control.FindDescendantByName("dataGrid") as DataGrid; + dataGrid = control.FindDescendant("dataGrid") as DataGrid; if (dataGrid != null) { dataGrid.Sorting += DataGrid_Sorting; @@ -54,7 +54,7 @@ public async void OnXamlRendered(FrameworkElement control) groupButton.Click -= GroupButton_Click; } - groupButton = control.FindDescendantByName("groupButton") as AppBarButton; + groupButton = control.FindDescendant("groupButton") as AppBarButton; if (groupButton != null) { groupButton.Click += GroupButton_Click; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs index a6b3b57aa53..5e480990bc4 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs @@ -24,7 +24,7 @@ public LoadingPage() public async void OnXamlRendered(FrameworkElement control) { - loadingControl = control.FindDescendantByName("LoadingControl") as Loading; + loadingControl = control.FindDescendant("LoadingControl") as Loading; loadingContentControl = control.FindChildByName("LoadingContentControl") as ContentControl; resources = control.Resources; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs index b5f03018e43..4d57aa7f183 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredLayout/StaggeredLayoutPage.xaml.cs @@ -34,7 +34,7 @@ public StaggeredLayoutPage() public void OnXamlRendered(FrameworkElement control) { - var repeater = control.FindDescendantByName("StaggeredRepeater") as ItemsRepeater; + var repeater = control.FindDescendant("StaggeredRepeater") as ItemsRepeater; if (repeater != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/IsNullOrEmptyStateTriggerPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/IsNullOrEmptyStateTriggerPage.xaml.cs index e9198b6dbd9..6590400b48e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/IsNullOrEmptyStateTriggerPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Triggers/IsNullOrEmptyStateTriggerPage.xaml.cs @@ -32,7 +32,7 @@ public void OnXamlRendered(FrameworkElement control) _addButton.Click -= this.AddButton_Click; } - if (control.FindDescendantByName("AddButton") is Button btn) + if (control.FindDescendant("AddButton") is Button btn) { _addButton = btn; @@ -44,14 +44,14 @@ public void OnXamlRendered(FrameworkElement control) _removeButton.Click -= this.RemoveButton_Click; } - if (control.FindDescendantByName("RemoveButton") is Button btn2) + if (control.FindDescendant("RemoveButton") is Button btn2) { _removeButton = btn2; _removeButton.Click += this.RemoveButton_Click; } - _listBox = control.FindDescendantByName("OurList") as ListBox; + _listBox = control.FindDescendant("OurList") as ListBox; } private void AddButton_Click(object sender, RoutedEventArgs e) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs index 72a4518c71b..9cd7382b4c5 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapLayout/WrapLayoutPage.xaml.cs @@ -39,7 +39,7 @@ public WrapLayoutPage() public void OnXamlRendered(FrameworkElement control) { - var repeater = control.FindDescendantByName("WrapRepeater") as ItemsRepeater; + var repeater = control.FindDescendant("WrapRepeater") as ItemsRepeater; if (repeater != null) { @@ -48,7 +48,7 @@ public void OnXamlRendered(FrameworkElement control) _wrapLayout = repeater.Layout as WrapLayout; } - _wrapScrollParent = control.FindDescendantByName("WrapScrollParent") as ScrollViewer; + _wrapScrollParent = control.FindDescendant("WrapScrollParent") as ScrollViewer; } private class Item diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs index 2b47f16b2f6..be8920094bf 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs @@ -532,7 +532,7 @@ private void GuardAgainstPlaceholderTextLayoutIssue() // // To combat this issue: // We toggle the visibility of the Placeholder ContentControl in order to force it's layout to update properly - var placeholder = ContainerFromItem(_lastTextEdit).FindDescendantByName("PlaceholderTextContentPresenter"); + var placeholder = ContainerFromItem(_lastTextEdit).FindDescendant("PlaceholderTextContentPresenter"); if (placeholder?.Visibility == Visibility.Visible) { diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 73177af5c27..664e90dc184 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -7,6 +7,8 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Media; +#nullable enable + namespace Microsoft.Toolkit.Uwp.UI.Extensions { /// @@ -15,98 +17,126 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions public static class VisualTree { /// - /// Find descendant control using its name. + /// Find the first descendant of type with a given name, using a depth-first search. /// - /// Parent element. - /// Name of the control to find - /// Descendant control or null if not found. - public static FrameworkElement FindDescendantByName(this DependencyObject element, string name) + /// The root element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The descendant that was found, or . + public static FrameworkElement? FindDescendant(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - if (element == null || string.IsNullOrWhiteSpace(name)) - { - return null; - } + return FindDescendant( + element, + (name, comparisonType), + static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + } - if (name.Equals((element as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) - { - return element as FrameworkElement; - } + /// + /// Find the first descendant element of a given type, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The descendant that was found, or . + public static T? FindDescendant(this DependencyObject element) + where T : notnull, DependencyObject + { + int childrenCount = VisualTreeHelper.GetChildrenCount(element); - var childCount = VisualTreeHelper.GetChildrenCount(element); - for (int i = 0; i < childCount; i++) + for (var i = 0; i < childrenCount; i++) { - var result = VisualTreeHelper.GetChild(element, i).FindDescendantByName(name); - if (result != null) + DependencyObject child = VisualTreeHelper.GetChild(element, i); + + if (child is T result) { return result; } + + T? descendant = FindDescendant(child); + + if (descendant is not null) + { + return descendant; + } } return null; } /// - /// Find first descendant control of a specified type. + /// Find the first descendant element of a given type, using a depth-first search. /// - /// Type to search for. - /// Parent element. - /// Descendant control or null if not found. - public static T FindDescendant(this DependencyObject element) - where T : DependencyObject + /// The root element. + /// The type of element to match. + /// The descendant that was found, or . + public static DependencyObject? FindDescendant(this DependencyObject element, Type type) { - T retValue = null; - var childrenCount = VisualTreeHelper.GetChildrenCount(element); + return FindDescendant(element, type, static (e, t) => e.GetType() == t); + } + + /// + /// Find the first descendant element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The predicatee to use to match the descendant nodes. + /// The descendant that was found, or . + public static T? FindDescendant(this DependencyObject element, Func predicate) + where T : notnull, DependencyObject + { + int childrenCount = VisualTreeHelper.GetChildrenCount(element); for (var i = 0; i < childrenCount; i++) { - var child = VisualTreeHelper.GetChild(element, i); - var type = child as T; - if (type != null) + DependencyObject child = VisualTreeHelper.GetChild(element, i); + + if (child is T result && predicate(result)) { - retValue = type; - break; + return result; } - retValue = FindDescendant(child); + T? descendant = FindDescendant(child, predicate); - if (retValue != null) + if (descendant is not null && predicate(descendant)) { - break; + return descendant; } } - return retValue; + return null; } /// - /// Find first descendant control of a specified type. + /// Find the first descendant element matching a given predicate, using a depth-first search. /// - /// Parent element. - /// Type of descendant. - /// Descendant control or null if not found. - public static object FindDescendant(this DependencyObject element, Type type) + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The root element. + /// The state to give as input to . + /// The predicatee to use to match the descendant nodes. + /// The descendant that was found, or . + public static T? FindDescendant(this DependencyObject element, TState state, Func predicate) + where T : notnull, DependencyObject { - object retValue = null; - var childrenCount = VisualTreeHelper.GetChildrenCount(element); + int childrenCount = VisualTreeHelper.GetChildrenCount(element); for (var i = 0; i < childrenCount; i++) { - var child = VisualTreeHelper.GetChild(element, i); - if (child.GetType() == type) + DependencyObject child = VisualTreeHelper.GetChild(element, i); + + if (child is T result && predicate(result, state)) { - retValue = child; - break; + return result; } - retValue = FindDescendant(child, type); + T? descendant = FindDescendant(child, state, predicate); - if (retValue != null) + if (descendant is not null && predicate(descendant, state)) { - break; + return descendant; } } - return retValue; + return null; } /// diff --git a/Microsoft.Toolkit.Uwp.UI/Microsoft.Toolkit.Uwp.UI.csproj b/Microsoft.Toolkit.Uwp.UI/Microsoft.Toolkit.Uwp.UI.csproj index e7358bf1939..d2db457a3d5 100644 --- a/Microsoft.Toolkit.Uwp.UI/Microsoft.Toolkit.Uwp.UI.csproj +++ b/Microsoft.Toolkit.Uwp.UI/Microsoft.Toolkit.Uwp.UI.csproj @@ -2,7 +2,7 @@ uap10.0.17763 - 8.0 + 9.0 Windows Community Toolkit UI This library provides UI components, such as XAML extensions, helpers, converters and more. It is part of the Windows Community Toolkit. From df3055f725280bbc79ccc5ddd4a3f74d31c4e891 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 13:28:58 +0100 Subject: [PATCH 02/29] Restructured the FindAscendant extensions --- .../Extensions/Tree/VisualTree.cs | 132 +++++++++++------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 664e90dc184..679495fedd9 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -167,78 +167,114 @@ public static IEnumerable FindDescendants(this DependencyObject element) } /// - /// Find visual ascendant control using its name. + /// Find the first ascendant of type with a given name. /// - /// Parent element. - /// Name of the control to find - /// Descendant control or null if not found. - public static FrameworkElement FindAscendantByName(this DependencyObject element, string name) + /// The starting element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The ascendant that was found, or . + public static FrameworkElement? FindAscendant(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + return FindAscendant( + element, + (name, comparisonType), + static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + } + + /// + /// Find the first ascendant element of a given type. + /// + /// The type of elements to match. + /// The starting element. + /// The ascendant that was found, or . + public static T? FindAscendant(this DependencyObject element) + where T : notnull, DependencyObject { - if (element == null || string.IsNullOrWhiteSpace(name)) + while (true) { - return null; - } + DependencyObject? parent = VisualTreeHelper.GetParent(element); - var parent = VisualTreeHelper.GetParent(element); + if (parent is null) + { + return null; + } - if (parent == null) - { - return null; - } + if (parent is T result) + { + return result; + } - if (name.Equals((parent as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) - { - return parent as FrameworkElement; + element = parent; } - - return parent.FindAscendantByName(name); } /// - /// Find first visual ascendant control of a specified type. + /// Find the first ascendant element of a given type. /// - /// Type to search for. - /// Child element. - /// Ascendant control or null if not found. - public static T FindAscendant(this DependencyObject element) - where T : DependencyObject + /// The starting element. + /// The type of element to match. + /// The ascendant that was found, or . + public static DependencyObject? FindAscendant(this DependencyObject element, Type type) { - var parent = VisualTreeHelper.GetParent(element); + return FindAscendant(element, type, static (e, t) => e.GetType() == t); + } - if (parent == null) + /// + /// Find the first ascendant element matching a given predicate. + /// + /// The type of elements to match. + /// The starting element. + /// The predicatee to use to match the ascendant nodes. + /// The ascendant that was found, or . + public static T? FindAscendant(this DependencyObject element, Func predicate) + where T : notnull, DependencyObject + { + while (true) { - return null; - } + DependencyObject? parent = VisualTreeHelper.GetParent(element); - if (parent is T) - { - return parent as T; - } + if (parent is null) + { + return null; + } + + if (parent is T result && predicate(result)) + { + return result; + } - return parent.FindAscendant(); + element = parent; + } } /// - /// Find first visual ascendant control of a specified type. + /// Find the first ascendant element matching a given predicate. /// - /// Child element. - /// Type of ascendant to look for. - /// Ascendant control or null if not found. - public static object FindAscendant(this DependencyObject element, Type type) + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The starting element. + /// The state to give as input to . + /// The predicatee to use to match the ascendant nodes. + /// The ascendant that was found, or . + public static T? FindAscendant(this DependencyObject element, TState state, Func predicate) + where T : notnull, DependencyObject { - var parent = VisualTreeHelper.GetParent(element); - - if (parent == null) + while (true) { - return null; - } + DependencyObject? parent = VisualTreeHelper.GetParent(element); - if (parent.GetType() == type) - { - return parent; - } + if (parent is null) + { + return null; + } - return parent.FindAscendant(type); + if (parent is T result && predicate(result, state)) + { + return result; + } + + element = parent; + } } /// From 4eb9ee59ba909ffa0383135a40ef9e4a65c728b3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 13:41:52 +0100 Subject: [PATCH 03/29] Added "___OrSelf" overloads --- .../Extensions/Tree/VisualTree.cs | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 679495fedd9..6acf6c2b9da 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -139,6 +139,94 @@ public static class VisualTree return null; } + /// + /// Find the first descendant (or self) of type with a given name, using a depth-first search. + /// + /// The root element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The descendant (or self) that was found, or . + public static FrameworkElement? FindDescendantOrSelf(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + if (element is FrameworkElement result && name.Equals(result.Name, comparisonType)) + { + return result; + } + + return FindDescendant(element, name, comparisonType); + } + + /// + /// Find the first descendant (or self) element of a given type, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The descendant (or self) that was found, or . + public static T? FindDescendantOrSelf(this DependencyObject element) + where T : notnull, DependencyObject + { + if (element is T result) + { + return result; + } + + return FindDescendant(element); + } + + /// + /// Find the first descendant (or self) element of a given type, using a depth-first search. + /// + /// The root element. + /// The type of element to match. + /// The descendant (or self) that was found, or . + public static DependencyObject? FindDescendantOrSelf(this DependencyObject element, Type type) + { + if (element.GetType() == type) + { + return element; + } + + return FindDescendant(element, type); + } + + /// + /// Find the first descendant (or self) element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The predicatee to use to match the descendant nodes. + /// The descendant (or self) that was found, or . + public static T? FindDescendantOrSelf(this DependencyObject element, Func predicate) + where T : notnull, DependencyObject + { + if (element is T result && predicate(result)) + { + return result; + } + + return FindDescendant(element, predicate); + } + + /// + /// Find the first descendant (or self) element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The root element. + /// The state to give as input to . + /// The predicatee to use to match the descendant nodes. + /// The descendant (or self) that was found, or . + public static T? FindDescendantOrSelf(this DependencyObject element, TState state, Func predicate) + where T : notnull, DependencyObject + { + if (element is T result && predicate(result, state)) + { + return result; + } + + return FindDescendant(element, state, predicate); + } + /// /// Find all descendant controls of the specified type. /// @@ -277,6 +365,94 @@ public static IEnumerable FindDescendants(this DependencyObject element) } } + /// + /// Find the first ascendant (or self) of type with a given name. + /// + /// The starting element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The ascendant (or self) that was found, or . + public static FrameworkElement? FindAscendantOrSelf(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + if (element is FrameworkElement result && name.Equals(result.Name, comparisonType)) + { + return result; + } + + return FindAscendant(element, name, comparisonType); + } + + /// + /// Find the first ascendant (or self) element of a given type. + /// + /// The type of elements to match. + /// The starting element. + /// The ascendant (or self) that was found, or . + public static T? FindAscendantOrSelf(this DependencyObject element) + where T : notnull, DependencyObject + { + if (element is T result) + { + return result; + } + + return FindAscendant(element); + } + + /// + /// Find the first ascendant (or self) element of a given type. + /// + /// The starting element. + /// The type of element to match. + /// The ascendant (or self) that was found, or . + public static DependencyObject? FindAscendantOrSelf(this DependencyObject element, Type type) + { + if (element.GetType() == type) + { + return element; + } + + return FindAscendant(element, type); + } + + /// + /// Find the first ascendant (or self) element matching a given predicate. + /// + /// The type of elements to match. + /// The starting element. + /// The predicatee to use to match the ascendant nodes. + /// The ascendant (or self) that was found, or . + public static T? FindAscendantOrSelf(this DependencyObject element, Func predicate) + where T : notnull, DependencyObject + { + if (element is T result && predicate(result)) + { + return result; + } + + return FindAscendant(element, predicate); + } + + /// + /// Find the first ascendant (or self) element matching a given predicate. + /// + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The starting element. + /// The state to give as input to . + /// The predicatee to use to match the ascendant nodes. + /// The ascendant (or self) that was found, or . + public static T? FindAscendantOrSelf(this DependencyObject element, TState state, Func predicate) + where T : notnull, DependencyObject + { + if (element is T result && predicate(result, state)) + { + return result; + } + + return FindAscendant(element, state, predicate); + } + /// /// Find all visual ascendants for the element. /// From 3ce4d793b24803979a1700dd5df05e8792f20b62 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 14:45:45 +0100 Subject: [PATCH 04/29] Minor code refactoring --- .../Extensions/Tree/LogicalTree.cs | 34 ++++--------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index a09471a4edb..31ebb142134 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -256,37 +255,16 @@ public static T FindParent(this FrameworkElement element) /// Child Content control or null if not available. public static UIElement GetContentControl(this FrameworkElement element) { - var contentpropname = ContentPropertySearch(element.GetType()); - if (contentpropname != null) - { - return element.GetType()?.GetProperty(contentpropname)?.GetValue(element) as UIElement; - } + Type type = element.GetType(); - return null; - } - - /// - /// Retrieves the Content Property's Name for the given type. - /// - /// UIElement based type to search for ContentProperty. - /// String name of ContentProperty for control. - private static string ContentPropertySearch(Type type) - { - if (type == null) - { - return null; - } - - // Using GetCustomAttribute directly isn't working for some reason, so we'll dig in ourselves: https://aka.ms/Rkzseg - ////var attr = type.GetTypeInfo().GetCustomAttribute(typeof(ContentPropertyAttribute), true); - var attr = type.GetTypeInfo().CustomAttributes.FirstOrDefault((element) => element.AttributeType == typeof(ContentPropertyAttribute)); - if (attr != null) + if (type.GetCustomAttribute(true) is ContentPropertyAttribute attribute && + type.GetProperty(attribute.Name) is PropertyInfo propertyInfo && + propertyInfo.GetValue(element) is UIElement content) { - ////return attr as ContentPropertyAttribute; - return attr.NamedArguments.First().TypedValue.Value as string; + return content; } - return ContentPropertySearch(type.GetTypeInfo().BaseType); + return null; } /// From b564ef7ba6f181c0b0fc841c09455b3861245a1c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 14:52:01 +0100 Subject: [PATCH 05/29] Restructured the FindParent extensions --- .../Extensions/Tree/LogicalTree.cs | 157 ++++++++++++------ 1 file changed, 107 insertions(+), 50 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 31ebb142134..9e79acb6873 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -9,6 +9,8 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; +#nullable enable + namespace Microsoft.Toolkit.Uwp.UI.Extensions { /// @@ -198,56 +200,6 @@ public static IEnumerable FindChildren(this FrameworkElement element) } } - /// - /// Finds the logical parent element with the given name or returns null. Note: Parent may only be set when the control is added to the VisualTree. - /// - /// - /// Child element. - /// Name of the control to find. - /// Parent control or null if not found. - public static FrameworkElement FindParentByName(this FrameworkElement element, string name) - { - if (element == null || string.IsNullOrWhiteSpace(name)) - { - return null; - } - - if (name.Equals(element.Name, StringComparison.OrdinalIgnoreCase)) - { - return element; - } - - if (element.Parent == null) - { - return null; - } - - return (element.Parent as FrameworkElement).FindParentByName(name); - } - - /// - /// Find first logical parent control of a specified type. Note: Parent may only be set when the control is added to the VisualTree. - /// - /// - /// Type to search for. - /// Child element. - /// Parent control or null if not found. - public static T FindParent(this FrameworkElement element) - where T : FrameworkElement - { - if (element.Parent == null) - { - return null; - } - - if (element.Parent is T) - { - return element.Parent as T; - } - - return (element.Parent as FrameworkElement).FindParent(); - } - /// /// Retrieves the Content control of this element as defined by the ContentPropertyAttribute. /// @@ -298,5 +250,110 @@ public static object TryFindResource(this FrameworkElement start, object resourc return value; } + + /// + /// Find the first parent of type with a given name. + /// + /// The starting element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The parent that was found, or . + public static FrameworkElement? FindParent(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + return FindParent( + element, + (name, comparisonType), + static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + } + + /// + /// Find the first parent element of a given type. + /// + /// The type of elements to match. + /// The starting element. + /// The parent that was found, or . + public static T? FindParent(this FrameworkElement element) + where T : notnull, FrameworkElement + { + while (true) + { + if (element.Parent is not FrameworkElement parent) + { + return null; + } + + if (parent is T result) + { + return result; + } + + element = parent; + } + } + + /// + /// Find the first parent element of a given type. + /// + /// The starting element. + /// The type of element to match. + /// The parent that was found, or . + public static FrameworkElement? FindParent(this FrameworkElement element, Type type) + { + return FindParent(element, type, static (e, t) => e.GetType() == t); + } + + /// + /// Find the first parent element matching a given predicate. + /// + /// The type of elements to match. + /// The starting element. + /// The predicatee to use to match the parent nodes. + /// The parent that was found, or . + public static T? FindParent(this FrameworkElement element, Func predicate) + where T : notnull, FrameworkElement + { + while (true) + { + if (element.Parent is not FrameworkElement parent) + { + return null; + } + + if (parent is T result && predicate(result)) + { + return result; + } + + element = parent; + } + } + + /// + /// Find the first parent element matching a given predicate. + /// + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The starting element. + /// The state to give as input to . + /// The predicatee to use to match the parent nodes. + /// The parent that was found, or . + public static T? FindParent(this FrameworkElement element, TState state, Func predicate) + where T : notnull, FrameworkElement + { + while (true) + { + if (element.Parent is not FrameworkElement parent) + { + return null; + } + + if (parent is T result && predicate(result, state)) + { + return result; + } + + element = parent; + } + } } } From eeb9e879de29ae25e466d72b880f0fa91e4b98fa Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 14:55:59 +0100 Subject: [PATCH 06/29] Added FindParentOrSelf overloads --- .../Extensions/Tree/LogicalTree.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 9e79acb6873..892d52c279e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -355,5 +355,93 @@ public static object TryFindResource(this FrameworkElement start, object resourc element = parent; } } + + /// + /// Find the first parent (or self) of type with a given name. + /// + /// The starting element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The parent (or self) that was found, or . + public static FrameworkElement? FindParentOrSelf(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + if (name.Equals(element.Name, comparisonType)) + { + return element; + } + + return FindParent(element, name, comparisonType); + } + + /// + /// Find the first parent (or self) element of a given type. + /// + /// The type of elements to match. + /// The starting element. + /// The parent (or self) that was found, or . + public static T? FindParentOrSelf(this FrameworkElement element) + where T : notnull, FrameworkElement + { + if (element is T result) + { + return result; + } + + return FindParent(element); + } + + /// + /// Find the first parent (or self) element of a given type. + /// + /// The starting element. + /// The type of element to match. + /// The parent (or self) that was found, or . + public static FrameworkElement? FindParentOrSelf(this FrameworkElement element, Type type) + { + if (element.GetType() == type) + { + return element; + } + + return FindParent(element, type); + } + + /// + /// Find the first parent (or self) element matching a given predicate. + /// + /// The type of elements to match. + /// The starting element. + /// The predicatee to use to match the parent nodes. + /// The parent (or self) that was found, or . + public static T? FindParentOrSelf(this FrameworkElement element, Func predicate) + where T : notnull, FrameworkElement + { + if (element is T result && predicate(result)) + { + return result; + } + + return FindParent(element, predicate); + } + + /// + /// Find the first parent (or self) element matching a given predicate. + /// + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The starting element. + /// The state to give as input to . + /// The predicatee to use to match the parent nodes. + /// The parent (or self) that was found, or . + public static T? FindParentOrSelf(this FrameworkElement element, TState state, Func predicate) + where T : notnull, FrameworkElement + { + if (element is T result && predicate(result, state)) + { + return result; + } + + return FindParent(element, state, predicate); + } } } From 9d2d5b5fd058601f34a06a31f1b901a3a8f7a172 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 16:01:03 +0100 Subject: [PATCH 07/29] Minor code tweaks --- .../Extensions/Tree/LogicalTree.cs | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 892d52c279e..e4b38b3675d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -205,7 +205,7 @@ public static IEnumerable FindChildren(this FrameworkElement element) /// /// Parent element. /// Child Content control or null if not available. - public static UIElement GetContentControl(this FrameworkElement element) + public static UIElement? GetContentControl(this FrameworkElement element) { Type type = element.GetType(); @@ -219,38 +219,6 @@ public static UIElement GetContentControl(this FrameworkElement element) return null; } - /// - /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. - /// If the key is not found in the current element's resources, the logical tree is then searched element-by-element to look for the resource in each element's resources. - /// If none of the elements contain the resource, the Application's resources are then searched. - /// - /// - /// - /// to start searching for Resource. - /// Key to search for. - /// Requested resource or null. - public static object TryFindResource(this FrameworkElement start, object resourceKey) - { - object value = null; - var current = start; - - // Look in our dictionary and then walk-up parents - while (current != null) - { - if (current.Resources?.TryGetValue(resourceKey, out value) == true) - { - return value; - } - - current = current.Parent as FrameworkElement; - } - - // Finally try application resources. - Application.Current?.Resources?.TryGetValue(resourceKey, out value); - - return value; - } - /// /// Find the first parent of type with a given name. /// @@ -443,5 +411,41 @@ public static object TryFindResource(this FrameworkElement start, object resourc return FindParent(element, state, predicate); } + + /// + /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. + /// If the key is not found in the current element's resources, the logical tree is then + /// searched element-by-element to look for the resource in each element's resources. + /// If none of the elements contain the resource, the Application's resources are then searched. + /// See: + /// And also: + /// + /// The to start searching for the target resource. + /// The resource key to search for. + /// The requested resource, or . + public static object? TryFindResource(this FrameworkElement element, object resourceKey) + { + object? value = null; + FrameworkElement? current = element; + + // Look in our dictionary and then walk-up parents. We use a do-while loop here + // so that an implicit NRE will be thrown at the first iteration in case the + // input element is null. This is consistent with the other extensions. + do + { + if (current.Resources?.TryGetValue(resourceKey, out value) == true) + { + return value!; + } + + current = current.Parent as FrameworkElement; + } + while (current is not null); + + // Finally try application resources + Application.Current?.Resources?.TryGetValue(resourceKey, out value); + + return value; + } } } From 2803ef03662c24040c729ea1d2aef5f9563483d4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 16:21:52 +0100 Subject: [PATCH 08/29] Removed unnecessary predicate calls --- Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 6acf6c2b9da..82906fd81aa 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -96,7 +96,7 @@ public static class VisualTree T? descendant = FindDescendant(child, predicate); - if (descendant is not null && predicate(descendant)) + if (descendant is not null) { return descendant; } @@ -130,7 +130,7 @@ public static class VisualTree T? descendant = FindDescendant(child, state, predicate); - if (descendant is not null && predicate(descendant, state)) + if (descendant is not null) { return descendant; } From 73ab43ef8f23da9e1a351bb2a10020b696c3218a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 16:32:31 +0100 Subject: [PATCH 09/29] Restructured the FindChild extensions --- .../Extensions/Tree/LogicalTree.cs | 211 ++++++++++++------ 1 file changed, 137 insertions(+), 74 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index e4b38b3675d..989daff06e7 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -19,112 +19,175 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions public static class LogicalTree { /// - /// Find logical child control using its name. + /// Find the first child of type with a given name, using a depth-first search. /// - /// Parent element. - /// Name of the control to find. - /// Child control or null if not found. - public static FrameworkElement FindChildByName(this FrameworkElement element, string name) + /// The root element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The child that was found, or . + public static FrameworkElement? FindChild(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - if (element == null || string.IsNullOrWhiteSpace(name)) - { - return null; - } + return FindChild( + element, + (name, comparisonType), + static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + } - if (name.Equals(element.Name, StringComparison.OrdinalIgnoreCase)) - { - return element; - } + /// + /// Find the first child element of a given type, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The child that was found, or . + public static T? FindChild(this FrameworkElement element) + where T : notnull, FrameworkElement + { + return FindChild(element, static _ => true); + } - if (element is Panel) + /// + /// Find the first child element of a given type, using a depth-first search. + /// + /// The root element. + /// The type of element to match. + /// The child that was found, or . + public static FrameworkElement? FindChild(this FrameworkElement element, Type type) + { + return FindChild(element, type, static (e, t) => e.GetType() == t); + } + + /// + /// Find the first child element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The predicatee to use to match the child nodes. + /// The child that was found, or . + public static T? FindChild(this FrameworkElement element, Func predicate) + where T : notnull, FrameworkElement + { + if (element is Panel panel) { - foreach (var child in (element as Panel).Children) + foreach (UIElement child in panel.Children) { - var result = (child as FrameworkElement)?.FindChildByName(name); - if (result != null) + if (child is not FrameworkElement current) + { + continue; + } + + if (child is T result && predicate(result)) { return result; } + + T? descendant = FindChild(current, predicate); + + if (descendant is not null) + { + return descendant; + } } } - else if (element is ItemsControl) + else if (element is ItemsControl itemsControl) { - foreach (var item in (element as ItemsControl).Items) + foreach (object item in itemsControl.Items) { - var result = (item as FrameworkElement)?.FindChildByName(name); - if (result != null) + if (item is not FrameworkElement current) + { + continue; + } + + if (item is T result && predicate(result)) { return result; } + + T? descendant = FindChild(current, predicate); + + if (descendant is not null) + { + return descendant; + } } } - else + else if (element.TryGetContentControl() is FrameworkElement contentControl) { - var result = (element.GetContentControl() as FrameworkElement)?.FindChildByName(name); - if (result != null) + if (contentControl is T result && predicate(result)) { return result; } + + return FindChild(contentControl, predicate); } return null; } /// - /// Find first logical child control of a specified type. + /// Find the first child element matching a given predicate, using a depth-first search. /// - /// Type to search for. - /// Parent element. - /// Child control or null if not found. - public static T FindChild(this FrameworkElement element) - where T : FrameworkElement + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The root element. + /// The state to give as input to . + /// The predicatee to use to match the child nodes. + /// The child that was found, or . + public static T? FindChild(this FrameworkElement element, TState state, Func predicate) + where T : notnull, FrameworkElement { - if (element == null) - { - return null; - } - if (element is Panel) + if (element is Panel panel) { - foreach (var child in (element as Panel).Children) + foreach (UIElement child in panel.Children) { - if (child is T) + if (child is not FrameworkElement current) { - return child as T; + continue; } - var result = (child as FrameworkElement)?.FindChild(); - if (result != null) + if (child is T result && predicate(result, state)) { return result; } + + T? descendant = FindChild(current, state, predicate); + + if (descendant is not null) + { + return descendant; + } } } - else if (element is ItemsControl) + else if (element is ItemsControl itemsControl) { - foreach (var item in (element as ItemsControl).Items) + foreach (object item in itemsControl.Items) { - var result = (item as FrameworkElement)?.FindChild(); - if (result != null) + if (item is not FrameworkElement current) + { + continue; + } + + if (item is T result && predicate(result, state)) { return result; } + + T? descendant = FindChild(current, state, predicate); + + if (descendant is not null) + { + return descendant; + } } } - else + else if (element.TryGetContentControl() is FrameworkElement contentControl) { - var content = element.GetContentControl(); - - if (content is T) - { - return content as T; - } - - var result = (content as FrameworkElement)?.FindChild(); - if (result != null) + if (contentControl is T result && predicate(result, state)) { return result; } + + return FindChild(contentControl, state, predicate); } return null; @@ -181,7 +244,7 @@ public static IEnumerable FindChildren(this FrameworkElement element) } else { - var content = element.GetContentControl(); + var content = element.TryGetContentControl(); if (content is T) { @@ -200,25 +263,6 @@ public static IEnumerable FindChildren(this FrameworkElement element) } } - /// - /// Retrieves the Content control of this element as defined by the ContentPropertyAttribute. - /// - /// Parent element. - /// Child Content control or null if not available. - public static UIElement? GetContentControl(this FrameworkElement element) - { - Type type = element.GetType(); - - if (type.GetCustomAttribute(true) is ContentPropertyAttribute attribute && - type.GetProperty(attribute.Name) is PropertyInfo propertyInfo && - propertyInfo.GetValue(element) is UIElement content) - { - return content; - } - - return null; - } - /// /// Find the first parent of type with a given name. /// @@ -412,6 +456,25 @@ public static IEnumerable FindChildren(this FrameworkElement element) return FindParent(element, state, predicate); } + /// + /// Tries to retrieve the content property of this element as defined by . + /// + /// The parent element. + /// The retrieved content control, or if not available. + public static UIElement? TryGetContentControl(this FrameworkElement element) + { + Type type = element.GetType(); + + if (type.GetCustomAttribute(true) is ContentPropertyAttribute attribute && + type.GetProperty(attribute.Name) is PropertyInfo propertyInfo && + propertyInfo.GetValue(element) is UIElement content) + { + return content; + } + + return null; + } + /// /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. /// If the key is not found in the current element's resources, the logical tree is then From 81a5ca4691187af6d51f810c845f54876337c429 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 16:36:02 +0100 Subject: [PATCH 10/29] Added FindChildOrSelf overloads --- .../Extensions/Tree/LogicalTree.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 989daff06e7..3b604c528a7 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -193,6 +193,94 @@ public static class LogicalTree return null; } + /// + /// Find the first child (or self) of type with a given name, using a depth-first search. + /// + /// The root element. + /// The name of the element to look for. + /// The comparison type to use to match . + /// The child (or self) that was found, or . + public static FrameworkElement? FindChildOrSelf(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) + { + if (name.Equals(element.Name, comparisonType)) + { + return element; + } + + return FindChild(element, name, comparisonType); + } + + /// + /// Find the first child (or self) element of a given type, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The child (or self) that was found, or . + public static T? FindChildOrSelf(this FrameworkElement element) + where T : notnull, FrameworkElement + { + if (element is T result) + { + return result; + } + + return FindChild(element); + } + + /// + /// Find the first child (or self) element of a given type, using a depth-first search. + /// + /// The root element. + /// The type of element to match. + /// The child (or self) that was found, or . + public static FrameworkElement? FindChildOrSelf(this FrameworkElement element, Type type) + { + if (element.GetType() == type) + { + return element; + } + + return FindChild(element, type); + } + + /// + /// Find the first child (or self) element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The root element. + /// The predicatee to use to match the child nodes. + /// The child (or self) that was found, or . + public static T? FindChildOrSelf(this FrameworkElement element, Func predicate) + where T : notnull, FrameworkElement + { + if (element is T result && predicate(result)) + { + return result; + } + + return FindChild(element, predicate); + } + + /// + /// Find the first child (or self) element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The type of state to use when matching nodes. + /// The root element. + /// The state to give as input to . + /// The predicatee to use to match the child nodes. + /// The child (or self) that was found, or . + public static T? FindChildOrSelf(this FrameworkElement element, TState state, Func predicate) + where T : notnull, FrameworkElement + { + if (element is T result && predicate(result, state)) + { + return result; + } + + return FindChild(element, state, predicate); + } + /// /// Find all logical child controls of the specified type. /// From b1dfbdd65d19be93ad3e7491ed6416be3cd80aff Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 16:59:23 +0100 Subject: [PATCH 11/29] Refactored enumerable parent/child visual tree extensions --- .../Extensions/Tree/LogicalTree.cs | 103 ++++++++++-------- .../Extensions/Tree/VisualTree.cs | 59 ++++++---- 2 files changed, 96 insertions(+), 66 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 3b604c528a7..c6d7fa6da77 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -135,7 +135,6 @@ public static class LogicalTree public static T? FindChild(this FrameworkElement element, TState state, Func predicate) where T : notnull, FrameworkElement { - if (element is Panel panel) { foreach (UIElement child in panel.Children) @@ -282,71 +281,60 @@ public static class LogicalTree } /// - /// Find all logical child controls of the specified type. + /// Find all logical child elements of the specified element. This method can be chained with + /// LINQ calls to add additional filters or projections on top of the returned results. + /// + /// This method is meant to provide extra flexibility in specific scenarios and it should not + /// be used when only the first item is being looked for. In those cases, use one of the + /// available overloads instead, which will + /// offer a more compact syntax as well as better performance in those cases. + /// /// - /// Type to search for. - /// Parent element. - /// Child controls or empty if not found. - public static IEnumerable FindChildren(this FrameworkElement element) - where T : FrameworkElement + /// The root element. + /// All the child instance from . + public static IEnumerable FindChildren(this FrameworkElement element) { - if (element == null) - { - yield break; - } - - if (element is Panel) + if (element is Panel panel) { - foreach (var child in (element as Panel).Children) + foreach (UIElement child in panel.Children) { - if (child is T) + if (child is not FrameworkElement current) { - yield return child as T; + continue; } - var childFrameworkElement = child as FrameworkElement; + yield return current; - if (childFrameworkElement != null) + foreach (FrameworkElement childOfChild in FindChildren(current)) { - foreach (T childOfChild in childFrameworkElement.FindChildren()) - { - yield return childOfChild; - } + yield return childOfChild; } } } - else if (element is ItemsControl) + else if (element is ItemsControl itemsControl) { - foreach (var item in (element as ItemsControl).Items) + foreach (object item in itemsControl.Items) { - var childFrameworkElement = item as FrameworkElement; + if (item is not FrameworkElement current) + { + continue; + } - if (childFrameworkElement != null) + yield return current; + + foreach (FrameworkElement childOfChild in FindChildren(current)) { - foreach (T childOfChild in childFrameworkElement.FindChildren()) - { - yield return childOfChild; - } + yield return childOfChild; } } } - else + else if (element.TryGetContentControl() is FrameworkElement contentControl) { - var content = element.TryGetContentControl(); + yield return contentControl; - if (content is T) + foreach (FrameworkElement childOfChild in FindChildren(contentControl)) { - yield return content as T; - } - - var childFrameworkElement = content as FrameworkElement; - - if (childFrameworkElement != null) - { - foreach (T childOfChild in childFrameworkElement.FindChildren()) - { - yield return childOfChild; - } + yield return childOfChild; } } } @@ -544,6 +532,33 @@ public static IEnumerable FindChildren(this FrameworkElement element) return FindParent(element, state, predicate); } + /// + /// Find all parent elements of the specified element. This method can be chained with + /// LINQ calls to add additional filters or projections on top of the returned results. + /// + /// This method is meant to provide extra flexibility in specific scenarios and it should not + /// be used when only the first item is being looked for. In those cases, use one of the + /// available overloads instead, which will + /// offer a more compact syntax as well as better performance in those cases. + /// + /// + /// The root element. + /// All the parent instance from . + public static IEnumerable FindParents(this FrameworkElement element) + { + while (true) + { + if (element.Parent is not FrameworkElement parent) + { + yield break; + } + + yield return parent; + + element = parent; + } + } + /// /// Tries to retrieve the content property of this element as defined by . /// diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 82906fd81aa..5a2fa1f70d8 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -228,28 +228,30 @@ public static class VisualTree } /// - /// Find all descendant controls of the specified type. + /// Find all descendant elements of the specified element. This method can be chained with + /// LINQ calls to add additional filters or projections on top of the returned results. + /// + /// This method is meant to provide extra flexibility in specific scenarios and it should not + /// be used when only the first item is being looked for. In those cases, use one of the + /// available overloads instead, which will + /// offer a more compact syntax as well as better performance in those cases. + /// /// - /// Type to search for. - /// Parent element. - /// Descendant controls or empty if not found. - public static IEnumerable FindDescendants(this DependencyObject element) - where T : DependencyObject + /// The root element. + /// All the descendant instance from . + public static IEnumerable FindDescendants(this DependencyObject element) { - var childrenCount = VisualTreeHelper.GetChildrenCount(element); + int childrenCount = VisualTreeHelper.GetChildrenCount(element); for (var i = 0; i < childrenCount; i++) { - var child = VisualTreeHelper.GetChild(element, i); - var type = child as T; - if (type != null) - { - yield return type; - } + DependencyObject child = VisualTreeHelper.GetChild(element, i); - foreach (T childofChild in child.FindDescendants()) + yield return child; + + foreach (DependencyObject childOfChild in FindDescendants(child)) { - yield return childofChild; + yield return childOfChild; } } } @@ -454,18 +456,31 @@ public static IEnumerable FindDescendants(this DependencyObject element) } /// - /// Find all visual ascendants for the element. + /// Find all ascendant elements of the specified element. This method can be chained with + /// LINQ calls to add additional filters or projections on top of the returned results. + /// + /// This method is meant to provide extra flexibility in specific scenarios and it should not + /// be used when only the first item is being looked for. In those cases, use one of the + /// available overloads instead, which will + /// offer a more compact syntax as well as better performance in those cases. + /// /// - /// Child element. - /// A collection of parent elements or null if none found. + /// The root element. + /// All the descendant instance from . public static IEnumerable FindAscendants(this DependencyObject element) { - var parent = VisualTreeHelper.GetParent(element); - - while (parent != null) + while (true) { + DependencyObject? parent = VisualTreeHelper.GetParent(element); + + if (parent is null) + { + yield break; + } + yield return parent; - parent = VisualTreeHelper.GetParent(parent); + + element = parent; } } } From b50f3ed2a24a76ff9bb0d7441f17cd6ed3ae8cd1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 17:08:04 +0100 Subject: [PATCH 12/29] Updated leftover references in the codebase --- .../SamplePages/BladeView/BladePage.xaml.cs | 4 ++-- .../SamplePages/DockPanel/DockPanelPage.xaml.cs | 3 +-- .../SamplePages/FadeHeader/FadeHeaderBehaviorPage.xaml.cs | 2 +- .../GazeInteraction/GazeInteractionPage.xaml.cs | 4 +--- .../SamplePages/GazeTracing/GazeTracingPage.xaml.cs | 2 +- .../HeaderedItemsControl/HeaderedItemsControlPage.xaml.cs | 3 ++- .../SamplePages/ImageCache/ImageCachePage.xaml.cs | 2 +- .../SamplePages/ImageCropper/ImageCropperPage.xaml.cs | 5 +---- .../SamplePages/ImageEx/ImageExPage.xaml.cs | 4 ++-- .../Implicit Animations/ImplicitAnimationsPage.xaml.cs | 2 +- .../InAppNotification/InAppNotificationPage.xaml.cs | 8 ++++---- .../SamplePages/InfiniteCanvas/InfiniteCanvasPage.xaml.cs | 2 +- .../ItemsReorderAnimationPage.xaml.cs | 2 +- .../ListViewExtensions/ListViewExtensionsPage.xaml.cs | 2 +- .../SamplePages/Loading/LoadingPage.xaml.cs | 4 ++-- .../MarkdownTextBlock/MarkdownTextBlockPage.xaml.cs | 4 ++-- .../SamplePages/Menu/MenuPage.xaml.cs | 2 +- .../SamplePages/OrbitView/OrbitViewPage.xaml.cs | 4 ++-- .../SamplePages/PrintHelper/PrintHelperPage.xaml.cs | 3 +-- .../SamplePages/RotatorTile/RotatorTilePage.xaml.cs | 8 ++++---- .../SamplePages/ScrollHeader/ScrollHeaderPage.xaml.cs | 2 +- .../ScrollViewerExtensionsPage.xaml.cs | 4 ++-- .../SamplePages/StaggeredPanel/StaggeredPanelPage.xaml.cs | 2 +- .../SamplePages/TextBoxMask/TextBoxMaskPage.xaml.cs | 2 +- .../SamplePages/TextToolbar/TextToolbarPage.xaml.cs | 6 +++--- .../TokenizingTextBox/TokenizingTextBoxPage.xaml.cs | 6 +++--- .../ViewportBehavior/ViewportBehaviorPage.xaml.cs | 4 ++-- .../SamplePages/WrapPanel/WrapPanelPage.xaml.cs | 2 +- .../Extensions/ScrollViewer/ScrollViewerExtensions.cs | 4 ++-- 29 files changed, 48 insertions(+), 54 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/BladeView/BladePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/BladeView/BladePage.xaml.cs index 302543fd4b0..93c45ce8376 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/BladeView/BladePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/BladeView/BladePage.xaml.cs @@ -25,8 +25,8 @@ public BladePage() public void OnXamlRendered(FrameworkElement control) { - bladeView = control.FindChildByName("BladeView") as BladeView; - addBlade = control.FindChildByName("AddBlade") as Button; + bladeView = control.FindChild("BladeView") as BladeView; + addBlade = control.FindChild("AddBlade") as Button; if (addBlade != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DockPanel/DockPanelPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DockPanel/DockPanelPage.xaml.cs index f85da6df97a..2861de1cd75 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DockPanel/DockPanelPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DockPanel/DockPanelPage.xaml.cs @@ -9,7 +9,6 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { @@ -29,7 +28,7 @@ public DockPanelPage() public void OnXamlRendered(FrameworkElement control) { - _sampleDockPanel = control.FindChildByName("SampleDockPanel") as DockPanel; + _sampleDockPanel = control.FindChild("SampleDockPanel") as DockPanel; } private void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FadeHeader/FadeHeaderBehaviorPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FadeHeader/FadeHeaderBehaviorPage.xaml.cs index 3f0636c40b7..c7ced9c6e0d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FadeHeader/FadeHeaderBehaviorPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/FadeHeader/FadeHeaderBehaviorPage.xaml.cs @@ -26,7 +26,7 @@ public FadeHeaderBehaviorPage() public void OnXamlRendered(FrameworkElement control) { - myListView = control.FindChildByName("MyListView") as ListView; + myListView = control.FindChild("MyListView") as ListView; // Load the ListView with Sample Data if (myListView != null) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeInteraction/GazeInteractionPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeInteraction/GazeInteractionPage.xaml.cs index 5f8af7e016b..b702e89949f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeInteraction/GazeInteractionPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeInteraction/GazeInteractionPage.xaml.cs @@ -2,13 +2,11 @@ // 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.Input.GazeInteraction; using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Shapes; namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { @@ -33,7 +31,7 @@ public void OnXamlRendered(FrameworkElement control) WarnUserToPlugInDevice(); - var buttonControl = control.FindChildByName("TargetButton") as Button; + var buttonControl = control.FindChild("TargetButton") as Button; if (buttonControl != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeTracing/GazeTracingPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeTracing/GazeTracingPage.xaml.cs index c8ab234d297..4293ec3594e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeTracing/GazeTracingPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeTracing/GazeTracingPage.xaml.cs @@ -42,7 +42,7 @@ public GazeTracingPage() public void OnXamlRendered(FrameworkElement control) { - if (control.FindChildByName("Points") is ItemsControl itemsControl) + if (control.FindChild("Points") is ItemsControl itemsControl) { itemsControl.ItemsSource = GazeHistory; } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/HeaderedItemsControl/HeaderedItemsControlPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/HeaderedItemsControl/HeaderedItemsControlPage.xaml.cs index ecc9a3042b0..3054d3c935c 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/HeaderedItemsControl/HeaderedItemsControlPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/HeaderedItemsControl/HeaderedItemsControlPage.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Linq; using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.UI.Xaml; @@ -26,7 +27,7 @@ public HeaderedItemsControlPage() public void OnXamlRendered(FrameworkElement element) { - foreach (var control in element.FindChildren()) + foreach (var control in element.FindChildren().OfType()) { control.DataContext = this; } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCache/ImageCachePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCache/ImageCachePage.xaml.cs index 63bd02f289a..ab0ed961847 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCache/ImageCachePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCache/ImageCachePage.xaml.cs @@ -29,7 +29,7 @@ public ImageCachePage() public void OnXamlRendered(FrameworkElement control) { - photoList = control.FindChildByName("PhotoList") as ListView; + photoList = control.FindChild("PhotoList") as ListView; } private async void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCropper/ImageCropperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCropper/ImageCropperPage.xaml.cs index 3799c7f9a39..7dcc30bfb55 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCropper/ImageCropperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageCropper/ImageCropperPage.xaml.cs @@ -4,12 +4,9 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.Toolkit.Uwp.UI.Extensions; -using Windows.Graphics.Imaging; using Windows.Storage; using Windows.Storage.Pickers; using Windows.UI.Xaml; @@ -35,7 +32,7 @@ public ImageCropperPage() public async void OnXamlRendered(FrameworkElement control) { - _imageCropper = control.FindChildByName("ImageCropper") as ImageCropper; + _imageCropper = control.FindChild("ImageCropper") as ImageCropper; if (_imageCropper != null) { var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Photos/Owl.jpg")); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageEx/ImageExPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageEx/ImageExPage.xaml.cs index 5d9eebfa33a..97b4412fa95 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageEx/ImageExPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageEx/ImageExPage.xaml.cs @@ -32,9 +32,9 @@ public ImageExPage() public void OnXamlRendered(FrameworkElement control) { // Need to use logical tree here as scrollviewer hasn't initialized yet even with dispatch. - container = control.FindChildByName("Container") as StackPanel; + container = control.FindChild("Container") as StackPanel; resources = control.Resources; - lazyLoadingControlHost = control.FindChildByName("LazyLoadingControlHost") as Border; + lazyLoadingControlHost = control.FindChild("LazyLoadingControlHost") as Border; } private async void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Implicit Animations/ImplicitAnimationsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Implicit Animations/ImplicitAnimationsPage.xaml.cs index d108285fdf7..b967f0c0767 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Implicit Animations/ImplicitAnimationsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Implicit Animations/ImplicitAnimationsPage.xaml.cs @@ -27,7 +27,7 @@ public ImplicitAnimationsPage() public void OnXamlRendered(FrameworkElement control) { - _element = control.FindChildByName("Element"); + _element = control.FindChild("Element"); } private void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InAppNotification/InAppNotificationPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InAppNotification/InAppNotificationPage.xaml.cs index 49a219e3d5d..bed188ffb46 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InAppNotification/InAppNotificationPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InAppNotification/InAppNotificationPage.xaml.cs @@ -35,14 +35,14 @@ public void OnXamlRendered(FrameworkElement control) { NotificationDuration = 0; - _exampleInAppNotification = control.FindChildByName("ExampleInAppNotification") as InAppNotification; + _exampleInAppNotification = control.FindChild("ExampleInAppNotification") as InAppNotification; _defaultInAppNotificationControlTemplate = _exampleInAppNotification?.Template; - _exampleCustomInAppNotification = control.FindChildByName("ExampleCustomInAppNotification") as InAppNotification; + _exampleCustomInAppNotification = control.FindChild("ExampleCustomInAppNotification") as InAppNotification; _customInAppNotificationControlTemplate = _exampleCustomInAppNotification?.Template; - _exampleVSCodeInAppNotification = control.FindChildByName("ExampleVSCodeInAppNotification") as InAppNotification; + _exampleVSCodeInAppNotification = control.FindChild("ExampleVSCodeInAppNotification") as InAppNotification; _resources = control.Resources; - var notificationDurationTextBox = control.FindChildByName("NotificationDurationTextBox") as TextBox; + var notificationDurationTextBox = control.FindChild("NotificationDurationTextBox") as TextBox; if (notificationDurationTextBox != null) { notificationDurationTextBox.TextChanged += NotificationDurationTextBox_TextChanged; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InfiniteCanvas/InfiniteCanvasPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InfiniteCanvas/InfiniteCanvasPage.xaml.cs index 005164105c2..630f665750a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InfiniteCanvas/InfiniteCanvasPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/InfiniteCanvas/InfiniteCanvasPage.xaml.cs @@ -29,7 +29,7 @@ public InfiniteCanvasPage() public void OnXamlRendered(FrameworkElement control) { - _infiniteCanvas = control.FindChildByName("canvas") as InfiniteCanvas; + _infiniteCanvas = control.FindChild("canvas") as InfiniteCanvas; } private void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ItemsReorderAnimation/ItemsReorderAnimationPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ItemsReorderAnimation/ItemsReorderAnimationPage.xaml.cs index f55559e57b5..398dac81217 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ItemsReorderAnimation/ItemsReorderAnimationPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ItemsReorderAnimation/ItemsReorderAnimationPage.xaml.cs @@ -19,7 +19,7 @@ public ItemsReorderAnimationPage() public async void OnXamlRendered(FrameworkElement control) { - imageView = control.FindChildByName("ImageView") as GridView; + imageView = control.FindChild("ImageView") as GridView; if (imageView != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs index af5f546e65c..27e54614340 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ListViewExtensions/ListViewExtensionsPage.xaml.cs @@ -24,7 +24,7 @@ public ListViewExtensionsPage() public async void OnXamlRendered(FrameworkElement control) { - var sampleListView = control.FindChildByName("SampleListView") as ListView; + var sampleListView = control.FindChild("SampleListView") as ListView; if (sampleListView != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs index 5e480990bc4..eec283755c3 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Loading/LoadingPage.xaml.cs @@ -25,10 +25,10 @@ public LoadingPage() public async void OnXamlRendered(FrameworkElement control) { loadingControl = control.FindDescendant("LoadingControl") as Loading; - loadingContentControl = control.FindChildByName("LoadingContentControl") as ContentControl; + loadingContentControl = control.FindChild("LoadingContentControl") as ContentControl; resources = control.Resources; - if (control.FindChildByName("AdaptiveGridViewControl") is AdaptiveGridView gridView) + if (control.FindChild("AdaptiveGridViewControl") is AdaptiveGridView gridView) { gridView.ItemsSource = await new Data.PhotosDataSource().GetItemsAsync(); } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock/MarkdownTextBlockPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock/MarkdownTextBlockPage.xaml.cs index d19885094d6..4c597e0ecc2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock/MarkdownTextBlockPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock/MarkdownTextBlockPage.xaml.cs @@ -31,9 +31,9 @@ public MarkdownTextBlockPage() public void OnXamlRendered(FrameworkElement control) { - unformattedText = control.FindChildByName("UnformattedText") as TextBox; + unformattedText = control.FindChild("UnformattedText") as TextBox; - markdownText = control.FindChildByName("MarkdownText") as MarkdownTextBlock; + markdownText = control.FindChild("MarkdownText") as MarkdownTextBlock; if (markdownText != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Menu/MenuPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Menu/MenuPage.xaml.cs index 166f794f06e..253ec55fe62 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Menu/MenuPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Menu/MenuPage.xaml.cs @@ -22,7 +22,7 @@ public MenuPage() public void OnXamlRendered(FrameworkElement control) { - fileMenu = control.FindChildByName("FileMenu") as MenuItem; + fileMenu = control.FindChild("FileMenu") as MenuItem; } #pragma warning restore CS0618 // Type or member is obsolete diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/OrbitView/OrbitViewPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/OrbitView/OrbitViewPage.xaml.cs index 4288367b402..24e29bead1e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/OrbitView/OrbitViewPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/OrbitView/OrbitViewPage.xaml.cs @@ -28,13 +28,13 @@ public OrbitViewPage() public void OnXamlRendered(FrameworkElement control) { - var people = control.FindChildByName("People") as OrbitView; + var people = control.FindChild("People") as OrbitView; if (people != null) { people.ItemClick += People_ItemClick; } - var devices = control.FindChildByName("Devices") as OrbitView; + var devices = control.FindChild("Devices") as OrbitView; if (devices != null) { devices.ItemsSource = DeviceList; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/PrintHelper/PrintHelperPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/PrintHelper/PrintHelperPage.xaml.cs index 1da05d79702..4722e7fe6e2 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/PrintHelper/PrintHelperPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/PrintHelper/PrintHelperPage.xaml.cs @@ -10,7 +10,6 @@ using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Navigation; namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages { @@ -40,7 +39,7 @@ public PrintHelperPage() public void OnXamlRendered(FrameworkElement control) { - var listView = control.FindChildByName("PrintSampleListView") as ListView; + var listView = control.FindChild("PrintSampleListView") as ListView; if (listView == null) { customPrintTemplate = null; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RotatorTile/RotatorTilePage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RotatorTile/RotatorTilePage.xaml.cs index 12693187e88..b7966bafc91 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RotatorTile/RotatorTilePage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/RotatorTile/RotatorTilePage.xaml.cs @@ -22,28 +22,28 @@ public RotatorTilePage() public void OnXamlRendered(FrameworkElement control) { - var tile1 = control.FindChildByName("Tile1") as RotatorTile; + var tile1 = control.FindChild("Tile1") as RotatorTile; if (tile1 != null) { tile1.ItemsSource = _pictures; } - var tile2 = control.FindChildByName("Tile2") as RotatorTile; + var tile2 = control.FindChild("Tile2") as RotatorTile; if (tile2 != null) { tile2.ItemsSource = _pictures; } - var tile3 = control.FindChildByName("Tile3") as RotatorTile; + var tile3 = control.FindChild("Tile3") as RotatorTile; if (tile3 != null) { tile3.ItemsSource = _pictures; } - var tile4 = control.FindChildByName("Tile4") as RotatorTile; + var tile4 = control.FindChild("Tile4") as RotatorTile; if (tile4 != null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollHeader/ScrollHeaderPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollHeader/ScrollHeaderPage.xaml.cs index 1e6b85e8db5..45aa930c47a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollHeader/ScrollHeaderPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollHeader/ScrollHeaderPage.xaml.cs @@ -22,7 +22,7 @@ public ScrollHeaderPage() public void OnXamlRendered(FrameworkElement control) { - var listView = control.FindChildByName("listView") as ListView; + var listView = control.FindChild("listView") as ListView; if (listView != null) { listView.ItemsSource = _items; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollViewerExtensions/ScrollViewerExtensionsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollViewerExtensions/ScrollViewerExtensionsPage.xaml.cs index 3c327b172cf..e9d7602a9d6 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollViewerExtensions/ScrollViewerExtensionsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ScrollViewerExtensions/ScrollViewerExtensionsPage.xaml.cs @@ -30,12 +30,12 @@ public ScrollViewerExtensionsPage() public void OnXamlRendered(FrameworkElement control) { - var listView = control.FindChildByName("listView") as ListView; + var listView = control.FindChild("listView") as ListView; if (listView != null) { listView.ItemsSource = _items; - var shapesPanel = control.FindChildByName("shapesPanel") as StackPanel; + var shapesPanel = control.FindChild("shapesPanel") as StackPanel; if (shapesPanel != null) { var listScrollViewer = listView.FindDescendant(); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredPanel/StaggeredPanelPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredPanel/StaggeredPanelPage.xaml.cs index fd34e60cc64..08e01228cb6 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredPanel/StaggeredPanelPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/StaggeredPanel/StaggeredPanelPage.xaml.cs @@ -21,7 +21,7 @@ public StaggeredPanelPage() public async void OnXamlRendered(FrameworkElement control) { - var gridView = control.FindChildByName("GridView") as ItemsControl; + var gridView = control.FindChild("GridView") as ItemsControl; if (gridView == null) { diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextBoxMask/TextBoxMaskPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextBoxMask/TextBoxMaskPage.xaml.cs index b47b5217f1e..68626db4677 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextBoxMask/TextBoxMaskPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextBoxMask/TextBoxMaskPage.xaml.cs @@ -23,7 +23,7 @@ public TextBoxMaskPage() public void OnXamlRendered(FrameworkElement control) { - alphaTextBox = control.FindChildByName("AlphaTextBox") as TextBox; + alphaTextBox = control.FindChild("AlphaTextBox") as TextBox; } private void Load() diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextToolbar/TextToolbarPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextToolbar/TextToolbarPage.xaml.cs index 1d70f2ebc67..c954736079f 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextToolbar/TextToolbarPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TextToolbar/TextToolbarPage.xaml.cs @@ -28,14 +28,14 @@ public TextToolbarPage() public void OnXamlRendered(FrameworkElement control) { - _toolbar = control.FindChildByName("Toolbar") as TextToolbar; + _toolbar = control.FindChild("Toolbar") as TextToolbar; - if (control.FindChildByName("EditZone") is RichEditBox editZone) + if (control.FindChild("EditZone") is RichEditBox editZone) { editZone.TextChanged += EditZone_TextChanged; } - if (control.FindChildByName("Previewer") is MarkdownTextBlock previewer) + if (control.FindChild("Previewer") is MarkdownTextBlock previewer) { _previewer = previewer; _previewer.LinkClicked += Previewer_LinkClicked; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TokenizingTextBox/TokenizingTextBoxPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TokenizingTextBox/TokenizingTextBoxPage.xaml.cs index 704dde5ec43..d4470dc3aec 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TokenizingTextBox/TokenizingTextBoxPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TokenizingTextBox/TokenizingTextBoxPage.xaml.cs @@ -123,7 +123,7 @@ public void OnXamlRendered(FrameworkElement control) _ttb.TokenItemAdding -= TokenItemCreating; } - if (control.FindChildByName("TokenBox") is TokenizingTextBox ttb) + if (control.FindChild("TokenBox") is TokenizingTextBox ttb) { _ttb = ttb; _ttb.TokenItemAdded += TokenItemAdded; @@ -147,7 +147,7 @@ public void OnXamlRendered(FrameworkElement control) _ttbEmail.PreviewKeyDown -= EmailPreviewKeyDown; } - if (control.FindChildByName("TokenBoxEmail") is TokenizingTextBox ttbEmail) + if (control.FindChild("TokenBoxEmail") is TokenizingTextBox ttbEmail) { _ttbEmail = ttbEmail; @@ -168,7 +168,7 @@ public void OnXamlRendered(FrameworkElement control) _ttbEmailSuggestions.PreviewKeyDown -= EmailList_PreviewKeyDown; } - if (control.FindChildByName("EmailList") is ListView ttbList) + if (control.FindChild("EmailList") is ListView ttbList) { _ttbEmailSuggestions = ttbList; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ViewportBehavior/ViewportBehaviorPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ViewportBehavior/ViewportBehaviorPage.xaml.cs index 7ffbbe7edfe..70310796169 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ViewportBehavior/ViewportBehaviorPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ViewportBehavior/ViewportBehaviorPage.xaml.cs @@ -35,13 +35,13 @@ public ViewportBehaviorPage() public void OnXamlRendered(FrameworkElement control) { - if (control.FindChildByName("EffectElement") is Image effectElement) + if (control.FindChild("EffectElement") is Image effectElement) { _effectElement = effectElement; ////TODO: _effectElement.Blur(value: 10, duration: 0).Start(); } - if (control.FindChildByName("EffectElementHost") is FrameworkElement effectElementHost) + if (control.FindChild("EffectElementHost") is FrameworkElement effectElementHost) { var behaviors = Interaction.GetBehaviors(effectElementHost); var viewportBehavior = behaviors.OfType().FirstOrDefault(); diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapPanel/WrapPanelPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapPanel/WrapPanelPage.xaml.cs index 2d7825629d0..8a586f38d85 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapPanel/WrapPanelPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/WrapPanel/WrapPanelPage.xaml.cs @@ -29,7 +29,7 @@ public WrapPanelPage() public void OnXamlRendered(FrameworkElement control) { - _itemControl = control.FindChildByName("WrapPanelContainer") as ListView; + _itemControl = control.FindChild("WrapPanelContainer") as ListView; if (_itemControl != null) { diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/ScrollViewer/ScrollViewerExtensions.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/ScrollViewer/ScrollViewerExtensions.cs index 07f880b029c..5c9620f2d89 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/ScrollViewer/ScrollViewerExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/ScrollViewer/ScrollViewerExtensions.cs @@ -44,7 +44,7 @@ private static bool ChangeHorizontalScrollBarMarginProperty(FrameworkElement sen var scrollViewer = sender as ScrollViewer ?? sender.FindDescendant(); // Last scrollbar with "HorizontalScrollBar" as name is our target to set its margin and avoid it overlapping the header - var scrollBar = scrollViewer?.FindDescendants().LastOrDefault(bar => bar.Name == "HorizontalScrollBar"); + var scrollBar = scrollViewer?.FindDescendants().OfType().LastOrDefault(bar => bar.Name == "HorizontalScrollBar"); if (scrollBar == null) { @@ -99,7 +99,7 @@ private static bool ChangeVerticalScrollBarMarginProperty(FrameworkElement sende var scrollViewer = sender as ScrollViewer ?? sender.FindDescendant(); // Last scrollbar with "HorizontalScrollBar" as name is our target to set its margin and avoid it overlapping the header - var scrollBar = scrollViewer?.FindDescendants().LastOrDefault(bar => bar.Name == "VerticalScrollBar"); + var scrollBar = scrollViewer?.FindDescendants().OfType().LastOrDefault(bar => bar.Name == "VerticalScrollBar"); if (scrollBar == null) { From bf823e0793e45a287413d6e402f27ed5c5f8d9a6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 22 Jan 2021 19:07:57 +0100 Subject: [PATCH 13/29] Fixed some more leftover references --- Microsoft.Toolkit.Uwp.SampleApp/readme.md | 2 +- ...Test_BitmapIconExtensionMarkupExtension.cs | 4 +-- .../Extensions/Test_EnumValuesExtension.cs | 2 +- .../Test_FontIconExtensionMarkupExtension.cs | 6 ++-- ..._FontIconSourceExtensionMarkupExtension.cs | 6 ++-- .../Test_NullableBoolMarkupExtension.cs | 10 +++--- .../Controls/Test_TextToolbar_Localization.cs | 2 +- .../Test_TokenizingTextBox_General.cs | 2 +- .../Controls/Test_UniformGrid_AutoLayout.cs | 32 +++++++++---------- .../Controls/Test_UniformGrid_Dimensions.cs | 12 +++---- .../Test_UniformGrid_RowColDefinitions.cs | 16 +++++----- .../XamlIslandsTest_StringExtensions.cs | 2 +- 12 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/readme.md b/Microsoft.Toolkit.Uwp.SampleApp/readme.md index 732af5f010e..8a48e970904 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/readme.md +++ b/Microsoft.Toolkit.Uwp.SampleApp/readme.md @@ -95,7 +95,7 @@ Therefore, for any new control/extension, you should still have a simplified sni This gets called whenever the template gets parsed (due to loading or user modification). Here you can use the [LogicalTree](https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs) extensions to grab named controls in the template and register their events. **Check for null first** as the developer may have removed the name from the element. ```csharp -var markdownText = control.FindChildByName("MarkdownText") as MarkdownTextBlock; +var markdownText = control.FindChild("MarkdownText") as MarkdownTextBlock; if (markdownText != null) { markdownText.LinkClicked += MarkdownText_LinkClicked; diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_BitmapIconExtensionMarkupExtension.cs b/UnitTests/UnitTests.UWP/Extensions/Test_BitmapIconExtensionMarkupExtension.cs index 7eba199289f..a4139faca06 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_BitmapIconExtensionMarkupExtension.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_BitmapIconExtensionMarkupExtension.cs @@ -33,7 +33,7 @@ public void Test_BitmapIconExtension_MarkupExtension_ProvideImage() ") as FrameworkElement; - var button = treeroot.FindChildByName("RootButton") as Button; + var button = treeroot.FindChild("RootButton") as Button; Assert.IsNotNull(button, $"Could not find the {nameof(Button)} control in tree."); @@ -66,7 +66,7 @@ public void Test_BitmapIconExtension_MarkupExtension_ProvideImageAndMonochrome() ") as FrameworkElement; - var button = treeroot.FindChildByName("RootButton") as Button; + var button = treeroot.FindChild("RootButton") as Button; Assert.IsNotNull(button, $"Could not find the {nameof(Button)} control in tree."); diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_EnumValuesExtension.cs b/UnitTests/UnitTests.UWP/Extensions/Test_EnumValuesExtension.cs index cfd9b1a1406..a072a84b0b3 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_EnumValuesExtension.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_EnumValuesExtension.cs @@ -27,7 +27,7 @@ public void Test_EnumValuesExtension_MarkupExtension() ") as FrameworkElement; - var list = treeRoot.FindChildByName("Check") as ListView; + var list = treeRoot.FindChild("Check") as ListView; Assert.IsNotNull(list, "Could not find ListView control in tree."); diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_FontIconExtensionMarkupExtension.cs b/UnitTests/UnitTests.UWP/Extensions/Test_FontIconExtensionMarkupExtension.cs index a8b7465c587..477ea451b0d 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_FontIconExtensionMarkupExtension.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_FontIconExtensionMarkupExtension.cs @@ -26,7 +26,7 @@ public void Test_FontIconExtension_MarkupExtension_ProvideSegoeMdl2Asset() ") as FrameworkElement; - var button = treeroot.FindChildByName("Check") as AppBarButton; + var button = treeroot.FindChild("Check") as AppBarButton; Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree."); @@ -49,7 +49,7 @@ public void Test_FontIconExtension_MarkupExtension_ProvideSegoeUI() ") as FrameworkElement; - var button = treeroot.FindChildByName("Check") as AppBarButton; + var button = treeroot.FindChild("Check") as AppBarButton; Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree."); @@ -72,7 +72,7 @@ public void Test_FontIconExtension_MarkupExtension_ProvideCustomFontIcon() ") as FrameworkElement; - var button = treeroot.FindChildByName("Check") as AppBarButton; + var button = treeroot.FindChild("Check") as AppBarButton; Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree."); diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs b/UnitTests/UnitTests.UWP/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs index a2aa7f2a1fc..317c32fac18 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs @@ -28,7 +28,7 @@ public void Test_FontIconSourceExtension_MarkupExtension_ProvideSegoeMdl2Asset() ") as FrameworkElement; - var button = treeRoot.FindChildByName("Check") as MockSwipeItem; + var button = treeRoot.FindChild("Check") as MockSwipeItem; Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree."); @@ -52,7 +52,7 @@ public void Test_FontIconSourceExtension_MarkupExtension_ProvideSegoeUI() ") as FrameworkElement; - var button = treeRoot.FindChildByName("Check") as MockSwipeItem; + var button = treeRoot.FindChild("Check") as MockSwipeItem; Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree."); @@ -76,7 +76,7 @@ public void Test_FontIconSourceExtension_MarkupExtension_ProvideCustomFontIcon() ") as FrameworkElement; - var button = treeRoot.FindChildByName("Check") as MockSwipeItem; + var button = treeRoot.FindChild("Check") as MockSwipeItem; Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree."); diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_NullableBoolMarkupExtension.cs b/UnitTests/UnitTests.UWP/Extensions/Test_NullableBoolMarkupExtension.cs index e7af0783c8f..4aaec2c2859 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_NullableBoolMarkupExtension.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_NullableBoolMarkupExtension.cs @@ -26,7 +26,7 @@ public void Test_NullableBool_MarkupExtension_ProvidesTrue() ") as FrameworkElement; - var toggle = treeroot.FindChildByName("Check") as CheckBox; + var toggle = treeroot.FindChild("Check") as CheckBox; Assert.IsNotNull(toggle, "Could not find checkbox control in tree."); @@ -44,7 +44,7 @@ public void Test_NullableBool_MarkupExtension_ProvidesFalse() ") as FrameworkElement; - var toggle = treeroot.FindChildByName("Check") as CheckBox; + var toggle = treeroot.FindChild("Check") as CheckBox; Assert.IsNotNull(toggle, "Could not find checkbox control in tree."); @@ -62,7 +62,7 @@ public void Test_NullableBool_MarkupExtension_ProvidesNull() ") as FrameworkElement; - var toggle = treeroot.FindChildByName("Check") as CheckBox; + var toggle = treeroot.FindChild("Check") as CheckBox; Assert.IsNotNull(toggle, "Could not find checkbox control in tree."); @@ -80,7 +80,7 @@ public void Test_NullableBool_MarkupExtension_ProvidesNullStillWithTrueValue() ") as FrameworkElement; - var toggle = treeroot.FindChildByName("Check") as CheckBox; + var toggle = treeroot.FindChild("Check") as CheckBox; Assert.IsNotNull(toggle, "Could not find checkbox control in tree."); @@ -99,7 +99,7 @@ public void Test_NullableBool_MarkupExtension_ProvidesNullStillWithFalseValue() ") as FrameworkElement; - var toggle = treeroot.FindChildByName("Check") as CheckBox; + var toggle = treeroot.FindChild("Check") as CheckBox; Assert.IsNotNull(toggle, "Could not find checkbox control in tree."); diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TextToolbar_Localization.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TextToolbar_Localization.cs index 6d9dbe48dff..34c6131ffc5 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_TextToolbar_Localization.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TextToolbar_Localization.cs @@ -43,7 +43,7 @@ public void Test_TextToolbar_Localization_Retrieve() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var toolbar = treeRoot.FindChildByName("TextToolbarControl") as TextToolbar; + var toolbar = treeRoot.FindChild("TextToolbarControl") as TextToolbar; Assert.IsNotNull(toolbar, "Could not find TextToolbar in tree."); diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_General.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_General.cs index 4822631c3d3..4139841cefc 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_General.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_General.cs @@ -31,7 +31,7 @@ public void Test_Clear() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var tokenBox = treeRoot.FindChildByName("tokenboxname") as TokenizingTextBox; + var tokenBox = treeRoot.FindChild("tokenboxname") as TokenizingTextBox; Assert.IsNotNull(tokenBox, "Could not find TokenizingTextBox in tree."); diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_AutoLayout.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_AutoLayout.cs index b24c51b2439..03158aef6e0 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_AutoLayout.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_AutoLayout.cs @@ -51,7 +51,7 @@ public void Test_UniformGrid_AutoLayout_FixedElementSingle() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -116,7 +116,7 @@ Otherwise we can't tell it apart from the other items. --> Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -176,7 +176,7 @@ public void Test_UniformGrid_AutoLayout_FixedElementSquare() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -222,7 +222,7 @@ public void Test_UniformGrid_AutoLayout_VerticalElement_FixedPosition() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -232,14 +232,14 @@ public void Test_UniformGrid_AutoLayout_VerticalElement_FixedPosition() grid.Measure(new Size(1000, 1000)); - var border = treeRoot.FindChildByName("OurItem") as Border; + var border = treeRoot.FindChild("OurItem") as Border; Assert.IsNotNull(border, "Could not find our item to test."); Assert.AreEqual(1, Grid.GetRow(border)); Assert.AreEqual(1, Grid.GetColumn(border)); - var border2 = treeRoot.FindChildByName("Shifted") as Border; + var border2 = treeRoot.FindChild("Shifted") as Border; Assert.IsNotNull(border2, "Could not find shifted item to test."); @@ -269,7 +269,7 @@ public void Test_UniformGrid_AutoLayout_VerticalElement() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -279,14 +279,14 @@ public void Test_UniformGrid_AutoLayout_VerticalElement() grid.Measure(new Size(1000, 1000)); - var border = treeRoot.FindChildByName("OurItem") as Border; + var border = treeRoot.FindChild("OurItem") as Border; Assert.IsNotNull(border, "Could not find our item to test."); Assert.AreEqual(1, Grid.GetRow(border)); Assert.AreEqual(1, Grid.GetColumn(border)); - var border2 = treeRoot.FindChildByName("Shifted") as Border; + var border2 = treeRoot.FindChild("Shifted") as Border; Assert.IsNotNull(border2, "Could not find shifted item to test."); @@ -316,7 +316,7 @@ public void Test_UniformGrid_AutoLayout_HorizontalElement() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -326,14 +326,14 @@ public void Test_UniformGrid_AutoLayout_HorizontalElement() grid.Measure(new Size(1000, 1000)); - var border = treeRoot.FindChildByName("OurItem") as Border; + var border = treeRoot.FindChild("OurItem") as Border; Assert.IsNotNull(border, "Could not find our item to test."); Assert.AreEqual(0, Grid.GetRow(border)); Assert.AreEqual(1, Grid.GetColumn(border)); - var border2 = treeRoot.FindChildByName("Shifted") as Border; + var border2 = treeRoot.FindChild("Shifted") as Border; Assert.IsNotNull(border2, "Could not find shifted item to test."); @@ -371,7 +371,7 @@ public void Test_UniformGrid_AutoLayout_LargeElement() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -411,7 +411,7 @@ public void Test_UniformGrid_AutoLayout_HorizontalElement_FixedPosition() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -421,14 +421,14 @@ public void Test_UniformGrid_AutoLayout_HorizontalElement_FixedPosition() grid.Measure(new Size(1000, 1000)); - var border = treeRoot.FindChildByName("OurItem") as Border; + var border = treeRoot.FindChild("OurItem") as Border; Assert.IsNotNull(border, "Could not find our item to test."); Assert.AreEqual(1, Grid.GetRow(border)); Assert.AreEqual(1, Grid.GetColumn(border)); - var border2 = treeRoot.FindChildByName("Shifted") as Border; + var border2 = treeRoot.FindChild("Shifted") as Border; Assert.IsNotNull(border2, "Could not find shifted item to test."); diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_Dimensions.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_Dimensions.cs index a8e43606e31..222c8cfdf48 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_Dimensions.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_Dimensions.cs @@ -29,7 +29,7 @@ public void Test_UniformGrid_GetDimensions_NoElements() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -65,7 +65,7 @@ public void Test_UniformGrid_GetDimensions_AllVisible() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -101,7 +101,7 @@ public void Test_UniformGrid_GetDimensions_SomeVisible() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -142,7 +142,7 @@ public void Test_UniformGrid_GetDimensions_FirstColumn() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -178,7 +178,7 @@ public void Test_UniformGrid_GetDimensions_ElementLarger() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -213,7 +213,7 @@ public void Test_UniformGrid_GetDimensions_FirstColumnEqualsColumns() Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var grid = treeRoot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeRoot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_RowColDefinitions.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_RowColDefinitions.cs index bf6539aea5f..b9de5de15d0 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_RowColDefinitions.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_UniformGrid_RowColDefinitions.cs @@ -36,7 +36,7 @@ public void Test_UniformGrid_SetupRowDefinitions_AllAutomatic() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -110,7 +110,7 @@ public void Test_UniformGrid_SetupRowDefinitions_FirstFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -206,7 +206,7 @@ public void Test_UniformGrid_SetupRowDefinitions_MiddleFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -310,7 +310,7 @@ public void Test_UniformGrid_SetupRowDefinitions_MiddleAndEndFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -437,7 +437,7 @@ public void Test_UniformGrid_SetupColumnDefinitions_AllAutomatic() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -511,7 +511,7 @@ public void Test_UniformGrid_SetupColumnDefinitions_FirstFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -609,7 +609,7 @@ public void Test_UniformGrid_SetupColumnDefinitions_MiddleFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); @@ -713,7 +713,7 @@ public void Test_UniformGrid_SetupColumnDefinitions_FirstAndEndFixed() Assert.IsNotNull(treeroot, "Could not load XAML tree."); - var grid = treeroot.FindChildByName("UniformGrid") as UniformGrid; + var grid = treeroot.FindChild("UniformGrid") as UniformGrid; Assert.IsNotNull(grid, "Could not find UniformGrid in tree."); diff --git a/UnitTests/UnitTests.XamlIslands.UWPApp/XamlIslandsTest_StringExtensions.cs b/UnitTests/UnitTests.XamlIslands.UWPApp/XamlIslandsTest_StringExtensions.cs index 3310f05c1a4..865d800c9e5 100644 --- a/UnitTests/UnitTests.XamlIslands.UWPApp/XamlIslandsTest_StringExtensions.cs +++ b/UnitTests/UnitTests.XamlIslands.UWPApp/XamlIslandsTest_StringExtensions.cs @@ -70,7 +70,7 @@ await App.Dispatcher.EnqueueAsync(() => Assert.IsNotNull(treeRoot, "Could not load XAML tree."); - var toolbar = treeRoot.FindChildByName("TextToolbarControl") as TextToolbar; + var toolbar = treeRoot.FindChild("TextToolbarControl") as TextToolbar; Assert.IsNotNull(toolbar, "Could not find TextToolbar in tree."); From dbf196d48f71110f1d146f8db44d78f54b7c430d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Jan 2021 14:06:32 +0100 Subject: [PATCH 14/29] Fixed bug with reflection in TryGetContentControl --- .../Extensions/Tree/LogicalTree.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index c6d7fa6da77..9b0bef59cd5 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -566,13 +566,24 @@ public static IEnumerable FindParents(this FrameworkElement el /// The retrieved content control, or if not available. public static UIElement? TryGetContentControl(this FrameworkElement element) { - Type type = element.GetType(); + Type? type = element.GetType(); - if (type.GetCustomAttribute(true) is ContentPropertyAttribute attribute && - type.GetProperty(attribute.Name) is PropertyInfo propertyInfo && - propertyInfo.GetValue(element) is UIElement content) + while (type is not null) { - return content; + // We need to manually explore the custom attributes this way as the target one + // one is not returned by any of the other available GetCustomAttribute APIs. + foreach (CustomAttributeData attribute in type.CustomAttributes) + { + if (attribute.AttributeType == typeof(ContentPropertyAttribute)) + { + string propertyName = (string)attribute.NamedArguments[0].TypedValue.Value; + PropertyInfo propertyInfo = type.GetProperty(propertyName); + + return propertyInfo.GetValue(element) as UIElement; + } + } + + type = type.BaseType; } return null; From 422fa6c582836af179253a7990f44b1129eb92d7 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Jan 2021 14:09:41 +0100 Subject: [PATCH 15/29] Fixed a bug in TokenizingTextBox --- .../TokenizingTextBox/TokenizingTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs index be8920094bf..fac78ce6fe9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs @@ -532,7 +532,7 @@ private void GuardAgainstPlaceholderTextLayoutIssue() // // To combat this issue: // We toggle the visibility of the Placeholder ContentControl in order to force it's layout to update properly - var placeholder = ContainerFromItem(_lastTextEdit).FindDescendant("PlaceholderTextContentPresenter"); + var placeholder = ContainerFromItem(_lastTextEdit)?.FindDescendant("PlaceholderTextContentPresenter"); if (placeholder?.Visibility == Visibility.Visible) { From 1d4fdf1ccb71d998e207fe1d6b98e53456c55356 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 23 Jan 2021 17:04:47 +0100 Subject: [PATCH 16/29] Fixed remaining unit tests --- .../Extensions/Tree/LogicalTree.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 9b0bef59cd5..cc4d819d712 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -566,24 +566,25 @@ public static IEnumerable FindParents(this FrameworkElement el /// The retrieved content control, or if not available. public static UIElement? TryGetContentControl(this FrameworkElement element) { - Type? type = element.GetType(); + Type type = element.GetType(); + TypeInfo typeInfo = type.GetTypeInfo(); - while (type is not null) + while (typeInfo is not null) { // We need to manually explore the custom attributes this way as the target one - // one is not returned by any of the other available GetCustomAttribute APIs. - foreach (CustomAttributeData attribute in type.CustomAttributes) + // is not returned by any of the other available GetCustomAttribute APIs. + foreach (CustomAttributeData attribute in typeInfo.CustomAttributes) { if (attribute.AttributeType == typeof(ContentPropertyAttribute)) { string propertyName = (string)attribute.NamedArguments[0].TypedValue.Value; - PropertyInfo propertyInfo = type.GetProperty(propertyName); + PropertyInfo? propertyInfo = type.GetProperty(propertyName); - return propertyInfo.GetValue(element) as UIElement; + return propertyInfo?.GetValue(element) as UIElement; } } - type = type.BaseType; + typeInfo = typeInfo.BaseType.GetTypeInfo(); } return null; From 9e56083798ab87a76f1327cf0a866a5315e82605 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 18 Feb 2021 22:44:44 +0100 Subject: [PATCH 17/29] Fixed some build errors in UWP unit tests --- UITests/UITests.App/RequestPageMessage.cs | 2 +- .../Extensions/Test_LogicalTreeExtensions.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/UITests/UITests.App/RequestPageMessage.cs b/UITests/UITests.App/RequestPageMessage.cs index cddd704a842..4746f4ef358 100644 --- a/UITests/UITests.App/RequestPageMessage.cs +++ b/UITests/UITests.App/RequestPageMessage.cs @@ -12,7 +12,7 @@ public RequestPageMessage(string name) { PageName = name; } - + public string PageName { get; } } } diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs index c3fee518ff6..f3b2e9fe715 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs @@ -44,7 +44,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlock = treeRoot.FindChildByName("TargetElement"); + var textBlock = treeRoot.FindChild("TargetElement"); Assert.IsNotNull(textBlock, "Expected to find something."); Assert.IsInstanceOfType(textBlock, typeof(TextBlock), "Didn't find expected typed element."); @@ -79,7 +79,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlock = treeRoot.FindChildByName("TargetElement"); + var textBlock = treeRoot.FindChild("TargetElement"); Assert.IsNull(textBlock, "Didn't expect to find anything."); }); @@ -250,7 +250,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlocks = treeRoot.FindChildren(); + var textBlocks = treeRoot.FindChildren().OfType(); Assert.IsNotNull(textBlocks, "Expected to find something."); @@ -438,7 +438,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsNotNull(startingPoint, "Could not find starting element."); // Main Test - var grid = startingPoint.FindParentByName("TargetElement"); + var grid = startingPoint.FindParent("TargetElement"); Assert.IsNotNull(grid, "Expected to find Grid"); Assert.AreEqual(targetGrid, grid, "Grid didn't match expected."); @@ -485,7 +485,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsNotNull(startingPoint, "Could not find starting element."); // Main Test - var grid = startingPoint.FindParentByName("TargetElement"); + var grid = startingPoint.FindParent("TargetElement"); Assert.IsNull(grid, "Didn't expect to find anything."); }); From 64c56d7597907c2917063ca8efd4f99392f2b3ee Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 18 Feb 2021 22:48:04 +0100 Subject: [PATCH 18/29] Fixed an ArgumentNullException in TryGetContentControl --- Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index cc4d819d712..e4fb1f703e4 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -567,7 +567,7 @@ public static IEnumerable FindParents(this FrameworkElement el public static UIElement? TryGetContentControl(this FrameworkElement element) { Type type = element.GetType(); - TypeInfo typeInfo = type.GetTypeInfo(); + TypeInfo? typeInfo = type.GetTypeInfo(); while (typeInfo is not null) { @@ -584,7 +584,7 @@ public static IEnumerable FindParents(this FrameworkElement el } } - typeInfo = typeInfo.BaseType.GetTypeInfo(); + typeInfo = typeInfo.BaseType?.GetTypeInfo(); } return null; From 6d3b8b98b5ddd3cb8ab4f94cf9f0a17bb64d98fa Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 19 Feb 2021 20:00:14 +0100 Subject: [PATCH 19/29] Fixed some leftover unit test build errors --- .../Extensions/Test_LogicalTreeExtensions.cs | 2 +- .../Extensions/Test_VisualTreeExtensions.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs index f3b2e9fe715..2d0b82beb3c 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_LogicalTreeExtensions.cs @@ -294,7 +294,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var thing = treeRoot.FindChildren(); + var thing = treeRoot.FindChildren().OfType(); Assert.IsNotNull(thing, "Expected to still have enumerable."); diff --git a/UnitTests/UnitTests.UWP/Extensions/Test_VisualTreeExtensions.cs b/UnitTests/UnitTests.UWP/Extensions/Test_VisualTreeExtensions.cs index 33e431222f5..3cdb722b9d3 100644 --- a/UnitTests/UnitTests.UWP/Extensions/Test_VisualTreeExtensions.cs +++ b/UnitTests/UnitTests.UWP/Extensions/Test_VisualTreeExtensions.cs @@ -45,7 +45,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlock = treeRoot.FindDescendantByName("TargetElement"); + var textBlock = treeRoot.FindDescendant("TargetElement"); Assert.IsNotNull(textBlock, "Expected to find something."); Assert.IsInstanceOfType(textBlock, typeof(TextBlock), "Didn't find expected typed element."); @@ -80,7 +80,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlock = treeRoot.FindDescendantByName("TargetElement"); + var textBlock = treeRoot.FindDescendant("TargetElement"); Assert.IsNull(textBlock, "Didn't expect to find anything."); }); @@ -220,7 +220,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var textBlocks = treeRoot.FindDescendants(); + var textBlocks = treeRoot.FindDescendants().OfType(); Assert.IsNotNull(textBlocks, "Expected to find something."); @@ -267,7 +267,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => await SetTestContentAsync(treeRoot); // Main Test - var thing = treeRoot.FindDescendants(); + var thing = treeRoot.FindDescendants().OfType(); Assert.IsNotNull(thing, "Expected to find something."); @@ -411,7 +411,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsNotNull(startingPoint, "Could not find starting element."); // Main Test - var grid = startingPoint.FindAscendantByName("TargetElement"); + var grid = startingPoint.FindAscendant("TargetElement"); Assert.IsNotNull(grid, "Expected to find Grid"); Assert.AreEqual(targetGrid, grid, "Grid didn't match expected."); @@ -458,7 +458,7 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsNotNull(startingPoint, "Could not find starting element."); // Main Test - var grid = startingPoint.FindAscendantByName("TargetElement"); + var grid = startingPoint.FindAscendant("TargetElement"); Assert.IsNull(grid, "Didn't expect to find anything."); }); From dce431130f79c1c70a9c78acd712e0c234159e7c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 19 Feb 2021 21:34:01 +0100 Subject: [PATCH 20/29] Minor API improvements Tweaked TryGetContentControl and TryFindResource, and added a throwing version of FindResource --- .../TokenizingTextBoxItem.AutoSuggestBox.cs | 12 +++-- .../Extensions/Tree/LogicalTree.cs | 52 ++++++++++++++----- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs index 190a0973f23..15bc0a438b5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs @@ -5,10 +5,8 @@ using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.Foundation; using Windows.System; -using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; @@ -107,7 +105,15 @@ private void OnApplyTemplateAutoSuggestBox(AutoSuggestBox auto) { // This can be expensive, could we optimize? // Also, this is changing the FontSize on the IconSource (which could be shared?) - fis.FontSize = Owner.TryFindResource("TokenizingTextBoxIconFontSize") as double? ?? 16; + if (Owner.TryFindResource("TokenizingTextBoxIconFontSize", out object resource) && + resource is double fontSize) + { + fis.FontSize = fontSize; + } + else + { + fis.FontSize = 16; + } } var iconBinding = new Binding() diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index e4fb1f703e4..136b4df8744 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -110,7 +110,7 @@ public static class LogicalTree } } } - else if (element.TryGetContentControl() is FrameworkElement contentControl) + else if (element.GetContentControl() is FrameworkElement contentControl) { if (contentControl is T result && predicate(result)) { @@ -179,7 +179,7 @@ public static class LogicalTree } } } - else if (element.TryGetContentControl() is FrameworkElement contentControl) + else if (element.GetContentControl() is FrameworkElement contentControl) { if (contentControl is T result && predicate(result, state)) { @@ -328,7 +328,7 @@ public static IEnumerable FindChildren(this FrameworkElement e } } } - else if (element.TryGetContentControl() is FrameworkElement contentControl) + else if (element.GetContentControl() is FrameworkElement contentControl) { yield return contentControl; @@ -560,11 +560,11 @@ public static IEnumerable FindParents(this FrameworkElement el } /// - /// Tries to retrieve the content property of this element as defined by . + /// Gets the content property of this element as defined by , if available. /// /// The parent element. /// The retrieved content control, or if not available. - public static UIElement? TryGetContentControl(this FrameworkElement element) + public static UIElement? GetContentControl(this FrameworkElement element) { Type type = element.GetType(); TypeInfo? typeInfo = type.GetTypeInfo(); @@ -595,15 +595,41 @@ public static IEnumerable FindParents(this FrameworkElement el /// If the key is not found in the current element's resources, the logical tree is then /// searched element-by-element to look for the resource in each element's resources. /// If none of the elements contain the resource, the Application's resources are then searched. - /// See: - /// And also: + /// See: . + /// And also: . /// /// The to start searching for the target resource. /// The resource key to search for. - /// The requested resource, or . - public static object? TryFindResource(this FrameworkElement element, object resourceKey) + /// The requested resource. + /// Thrown when no resource is found with the specified key. + public static object FindResource(this FrameworkElement element, object resourceKey) { - object? value = null; + if (TryFindResource(element, resourceKey, out object? value)) + { + return value!; + } + + static object Throw() => throw new Exception("No resource was found with the specified key"); + + return Throw(); + } + + /// + /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. + /// If the key is not found in the current element's resources, the logical tree is then + /// searched element-by-element to look for the resource in each element's resources. + /// If none of the elements contain the resource, the Application's resources are then searched. + /// See: . + /// And also: . + /// + /// The to start searching for the target resource. + /// The resource key to search for. + /// The resulting value, if present. + /// Whether or not a value with the specified key has been found. + public static bool TryFindResource(this FrameworkElement element, object resourceKey, out object? value) + { + value = null; + FrameworkElement? current = element; // Look in our dictionary and then walk-up parents. We use a do-while loop here @@ -613,7 +639,7 @@ public static IEnumerable FindParents(this FrameworkElement el { if (current.Resources?.TryGetValue(resourceKey, out value) == true) { - return value!; + return true; } current = current.Parent as FrameworkElement; @@ -621,9 +647,7 @@ public static IEnumerable FindParents(this FrameworkElement el while (current is not null); // Finally try application resources - Application.Current?.Resources?.TryGetValue(resourceKey, out value); - - return value; + return Application.Current?.Resources?.TryGetValue(resourceKey, out value) == true; } } } From 6fe63a6344b9369914206b7ccfe7e286c4a3014a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 09:56:18 +0100 Subject: [PATCH 21/29] Added optimized content paths for logical tree extensions --- .../Extensions/Tree/LogicalTree.cs | 126 ++++++++++++++++-- 1 file changed, 117 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 136b4df8744..4a0e854dd50 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -110,14 +110,50 @@ public static class LogicalTree } } } - else if (element.GetContentControl() is FrameworkElement contentControl) + else if (element is UserControl userControl) { - if (contentControl is T result && predicate(result)) + if (userControl.Content is T result && predicate(result)) { return result; } - return FindChild(contentControl, predicate); + if (userControl.Content is FrameworkElement content) + { + return FindChild(content, predicate); + } + } + else if (element is ContentControl contentControl) + { + if (contentControl.Content is T result && predicate(result)) + { + return result; + } + + if (contentControl.Content is FrameworkElement content) + { + return FindChild(content, predicate); + } + } + else if (element is Border border) + { + if (border.Child is T result && predicate(result)) + { + return result; + } + + if (border.Child is FrameworkElement child) + { + return FindChild(child, predicate); + } + } + else if (element.GetContentControl() is FrameworkElement containedControl) + { + if (containedControl is T result && predicate(result)) + { + return result; + } + + return FindChild(containedControl, predicate); } return null; @@ -179,14 +215,50 @@ public static class LogicalTree } } } - else if (element.GetContentControl() is FrameworkElement contentControl) + else if (element is UserControl userControl) + { + if (userControl.Content is T result && predicate(result, state)) + { + return result; + } + + if (userControl.Content is FrameworkElement content) + { + return FindChild(content, state, predicate); + } + } + else if (element is ContentControl contentControl) + { + if (contentControl.Content is T result && predicate(result, state)) + { + return result; + } + + if (contentControl.Content is FrameworkElement content) + { + return FindChild(content, state, predicate); + } + } + else if (element is Border border) + { + if (border.Child is T result && predicate(result, state)) + { + return result; + } + + if (border.Child is FrameworkElement child) + { + return FindChild(child, state, predicate); + } + } + else if (element.GetContentControl() is FrameworkElement containedControl) { - if (contentControl is T result && predicate(result, state)) + if (containedControl is T result && predicate(result, state)) { return result; } - return FindChild(contentControl, state, predicate); + return FindChild(containedControl, state, predicate); } return null; @@ -328,11 +400,47 @@ public static IEnumerable FindChildren(this FrameworkElement e } } } - else if (element.GetContentControl() is FrameworkElement contentControl) + else if (element is UserControl userControl) + { + if (userControl.Content is FrameworkElement content) + { + yield return content; + + foreach (FrameworkElement childOfContent in FindChildren(content)) + { + yield return childOfContent; + } + } + } + else if (element is ContentControl contentControl) + { + if (contentControl.Content is FrameworkElement content) + { + yield return content; + + foreach (FrameworkElement childOfContent in FindChildren(content)) + { + yield return childOfContent; + } + } + } + else if (element is Border border) + { + if (border.Child is FrameworkElement child) + { + yield return child; + + foreach (FrameworkElement childOfChild in FindChildren(child)) + { + yield return childOfChild; + } + } + } + else if (element.GetContentControl() is FrameworkElement containedControl) { - yield return contentControl; + yield return containedControl; - foreach (FrameworkElement childOfChild in FindChildren(contentControl)) + foreach (FrameworkElement childOfChild in FindChildren(containedControl)) { yield return childOfChild; } From f243bb0a99949a64b3ec48dfadb0b5e06db5bf6a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 10:52:35 +0100 Subject: [PATCH 22/29] Added new internal predicate types --- .../Predicates/Interfaces/IPredicate{T}.cs | 20 +++++++++ .../Tree/Predicates/PredicateByAny{T}.cs | 22 +++++++++ .../Predicates/PredicateByFunc{T,TState}.cs | 45 +++++++++++++++++++ .../Tree/Predicates/PredicateByFunc{T}.cs | 37 +++++++++++++++ .../Tree/Predicates/PredicateByName.cs | 44 ++++++++++++++++++ .../Tree/Predicates/PredicateByType{T}.cs | 37 +++++++++++++++ 6 files changed, 205 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByName.cs create mode 100644 Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs new file mode 100644 index 00000000000..48178b8b603 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An interface representing a predicate for items of a given type. + /// + /// The type of items to match. + internal interface IPredicate + { + /// + /// Performs a match with the current predicate over a target instance. + /// + /// The input element to match. + /// Whether the match evaluation was successful. + bool Match(T element); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs new file mode 100644 index 00000000000..63701b2330d --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An type matching all instances of a given type. + /// + /// The type of items to match. + internal readonly struct PredicateByAny : IPredicate + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Match(T element) + { + return true; + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs new file mode 100644 index 00000000000..86ef859282a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs @@ -0,0 +1,45 @@ +// 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.Runtime.CompilerServices; + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An type matching items of a given type. + /// + /// The type of items to match. + /// The type of state to use when matching items. + internal readonly struct PredicateByFunc : IPredicate + { + /// + /// The state to give as input to . + /// + private readonly TState state; + + /// + /// The predicatee to use to match items. + /// + private readonly Func predicate; + + /// + /// Initializes a new instance of the struct. + /// + /// The state to give as input to . + /// The predicatee to use to match items. + public PredicateByFunc(TState state, Func predicate) + { + this.state = state; + this.predicate = predicate; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Match(T element) + { + return this.predicate(element, state); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs new file mode 100644 index 00000000000..10893f05621 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs @@ -0,0 +1,37 @@ +// 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.Runtime.CompilerServices; + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An type matching items of a given type. + /// + /// The type of items to match. + internal readonly struct PredicateByFunc : IPredicate + { + /// + /// The predicatee to use to match items. + /// + private readonly Func predicate; + + /// + /// Initializes a new instance of the struct. + /// + /// The predicatee to use to match items. + public PredicateByFunc(Func predicate) + { + this.predicate = predicate; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Match(T element) + { + return this.predicate(element); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByName.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByName.cs new file mode 100644 index 00000000000..414b5ec353d --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByName.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; +using System.Runtime.CompilerServices; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An type matching instances by name. + /// + internal readonly struct PredicateByName : IPredicate + { + /// + /// The name of the element to look for. + /// + private readonly string name; + + /// + /// The comparison type to use to match . + /// + private readonly StringComparison comparisonType; + + /// + /// Initializes a new instance of the struct. + /// + /// The name of the element to look for. + /// The comparison type to use to match . + public PredicateByName(string name, StringComparison comparisonType) + { + this.name = name; + this.comparisonType = comparisonType; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Match(FrameworkElement element) + { + return element.Name.Equals(this.name, this.comparisonType); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs new file mode 100644 index 00000000000..02d4a3b61b5 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs @@ -0,0 +1,37 @@ +// 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.Runtime.CompilerServices; + +namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree +{ + /// + /// An type matching items of a given type. + /// + /// The type of items to match. + internal readonly struct PredicateByType : IPredicate + { + /// + /// The type of element to match. + /// + private readonly Type type; + + /// + /// Initializes a new instance of the struct. + /// + /// The type of element to match. + public PredicateByType(Type type) + { + this.type = type; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Match(T element) + { + return element.GetType() == this.type; + } + } +} From 2f7e3390814d7c4fd00066fdfbf28a13a1b44bdb Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 11:07:12 +0100 Subject: [PATCH 23/29] Performance improvements, removed code duplication --- .../Extensions/Tree/LogicalTree.cs | 209 ++++++------------ .../Predicates/Interfaces/IPredicate{T}.cs | 3 +- .../Tree/Predicates/PredicateByAny{T}.cs | 1 + .../Predicates/PredicateByFunc{T,TState}.cs | 1 + .../Tree/Predicates/PredicateByFunc{T}.cs | 1 + .../Extensions/Tree/VisualTree.cs | 139 +++++------- .../Tree/Predicates/PredicateByType.cs | 7 +- 7 files changed, 135 insertions(+), 226 deletions(-) rename Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs => Microsoft.Toolkit.uwp.UI/Extensions/Tree/Predicates/PredicateByType.cs (83%) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 4a0e854dd50..861e6d789ce 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Microsoft.Toolkit.Uwp.UI.Extensions.Tree; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; @@ -27,10 +28,9 @@ public static class LogicalTree /// The child that was found, or . public static FrameworkElement? FindChild(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - return FindChild( - element, - (name, comparisonType), - static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + PredicateByName predicateByName = new(name, comparisonType); + + return FindChild(element, ref predicateByName); } /// @@ -42,7 +42,9 @@ public static class LogicalTree public static T? FindChild(this FrameworkElement element) where T : notnull, FrameworkElement { - return FindChild(element, static _ => true); + PredicateByAny predicateByAny = default; + + return FindChild>(element, ref predicateByAny); } /// @@ -53,7 +55,9 @@ public static class LogicalTree /// The child that was found, or . public static FrameworkElement? FindChild(this FrameworkElement element, Type type) { - return FindChild(element, type, static (e, t) => e.GetType() == t); + PredicateByType predicateByType = new(type); + + return FindChild(element, ref predicateByType); } /// @@ -66,97 +70,9 @@ public static class LogicalTree public static T? FindChild(this FrameworkElement element, Func predicate) where T : notnull, FrameworkElement { - if (element is Panel panel) - { - foreach (UIElement child in panel.Children) - { - if (child is not FrameworkElement current) - { - continue; - } - - if (child is T result && predicate(result)) - { - return result; - } - - T? descendant = FindChild(current, predicate); - - if (descendant is not null) - { - return descendant; - } - } - } - else if (element is ItemsControl itemsControl) - { - foreach (object item in itemsControl.Items) - { - if (item is not FrameworkElement current) - { - continue; - } - - if (item is T result && predicate(result)) - { - return result; - } - - T? descendant = FindChild(current, predicate); + PredicateByFunc predicateByFunc = new(predicate); - if (descendant is not null) - { - return descendant; - } - } - } - else if (element is UserControl userControl) - { - if (userControl.Content is T result && predicate(result)) - { - return result; - } - - if (userControl.Content is FrameworkElement content) - { - return FindChild(content, predicate); - } - } - else if (element is ContentControl contentControl) - { - if (contentControl.Content is T result && predicate(result)) - { - return result; - } - - if (contentControl.Content is FrameworkElement content) - { - return FindChild(content, predicate); - } - } - else if (element is Border border) - { - if (border.Child is T result && predicate(result)) - { - return result; - } - - if (border.Child is FrameworkElement child) - { - return FindChild(child, predicate); - } - } - else if (element.GetContentControl() is FrameworkElement containedControl) - { - if (containedControl is T result && predicate(result)) - { - return result; - } - - return FindChild(containedControl, predicate); - } - - return null; + return FindChild>(element, ref predicateByFunc); } /// @@ -170,6 +86,23 @@ public static class LogicalTree /// The child that was found, or . public static T? FindChild(this FrameworkElement element, TState state, Func predicate) where T : notnull, FrameworkElement + { + PredicateByFunc predicateByFunc = new(state, predicate); + + return FindChild>(element, ref predicateByFunc); + } + + /// + /// Find the first child element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The type of predicate in use. + /// The root element. + /// The predicatee to use to match the child nodes. + /// The child that was found, or . + private static T? FindChild(this FrameworkElement element, ref TPredicate predicate) + where T : notnull, FrameworkElement + where TPredicate : struct, IPredicate { if (element is Panel panel) { @@ -180,12 +113,12 @@ public static class LogicalTree continue; } - if (child is T result && predicate(result, state)) + if (child is T result && predicate.Match(result)) { return result; } - T? descendant = FindChild(current, state, predicate); + T? descendant = FindChild(current, ref predicate); if (descendant is not null) { @@ -202,12 +135,12 @@ public static class LogicalTree continue; } - if (item is T result && predicate(result, state)) + if (item is T result && predicate.Match(result)) { return result; } - T? descendant = FindChild(current, state, predicate); + T? descendant = FindChild(current, ref predicate); if (descendant is not null) { @@ -217,48 +150,48 @@ public static class LogicalTree } else if (element is UserControl userControl) { - if (userControl.Content is T result && predicate(result, state)) + if (userControl.Content is T result && predicate.Match(result)) { return result; } if (userControl.Content is FrameworkElement content) { - return FindChild(content, state, predicate); + return FindChild(content, ref predicate); } } else if (element is ContentControl contentControl) { - if (contentControl.Content is T result && predicate(result, state)) + if (contentControl.Content is T result && predicate.Match(result)) { return result; } if (contentControl.Content is FrameworkElement content) { - return FindChild(content, state, predicate); + return FindChild(content, ref predicate); } } else if (element is Border border) { - if (border.Child is T result && predicate(result, state)) + if (border.Child is T result && predicate.Match(result)) { return result; } if (border.Child is FrameworkElement child) { - return FindChild(child, state, predicate); + return FindChild(child, ref predicate); } } else if (element.GetContentControl() is FrameworkElement containedControl) { - if (containedControl is T result && predicate(result, state)) + if (containedControl is T result && predicate.Match(result)) { return result; } - return FindChild(containedControl, state, predicate); + return FindChild(containedControl, ref predicate); } return null; @@ -456,10 +389,9 @@ public static IEnumerable FindChildren(this FrameworkElement e /// The parent that was found, or . public static FrameworkElement? FindParent(this FrameworkElement element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - return FindParent( - element, - (name, comparisonType), - static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + PredicateByName predicateByName = new(name, comparisonType); + + return FindParent(element, ref predicateByName); } /// @@ -471,20 +403,9 @@ public static IEnumerable FindChildren(this FrameworkElement e public static T? FindParent(this FrameworkElement element) where T : notnull, FrameworkElement { - while (true) - { - if (element.Parent is not FrameworkElement parent) - { - return null; - } - - if (parent is T result) - { - return result; - } + PredicateByAny predicateByAny = default; - element = parent; - } + return FindParent>(element, ref predicateByAny); } /// @@ -495,7 +416,9 @@ public static IEnumerable FindChildren(this FrameworkElement e /// The parent that was found, or . public static FrameworkElement? FindParent(this FrameworkElement element, Type type) { - return FindParent(element, type, static (e, t) => e.GetType() == t); + PredicateByType predicateByType = new(type); + + return FindParent(element, ref predicateByType); } /// @@ -508,20 +431,9 @@ public static IEnumerable FindChildren(this FrameworkElement e public static T? FindParent(this FrameworkElement element, Func predicate) where T : notnull, FrameworkElement { - while (true) - { - if (element.Parent is not FrameworkElement parent) - { - return null; - } - - if (parent is T result && predicate(result)) - { - return result; - } + PredicateByFunc predicateByFunc = new(predicate); - element = parent; - } + return FindParent>(element, ref predicateByFunc); } /// @@ -535,6 +447,23 @@ public static IEnumerable FindChildren(this FrameworkElement e /// The parent that was found, or . public static T? FindParent(this FrameworkElement element, TState state, Func predicate) where T : notnull, FrameworkElement + { + PredicateByFunc predicateByFunc = new(state, predicate); + + return FindParent>(element, ref predicateByFunc); + } + + /// + /// Find the first parent element matching a given predicate. + /// + /// The type of elements to match. + /// The type of predicate in use. + /// The starting element. + /// The predicatee to use to match the parent nodes. + /// The parent that was found, or . + private static T? FindParent(this FrameworkElement element, ref TPredicate predicate) + where T : notnull, FrameworkElement + where TPredicate : struct, IPredicate { while (true) { @@ -543,7 +472,7 @@ public static IEnumerable FindChildren(this FrameworkElement e return null; } - if (parent is T result && predicate(result, state)) + if (parent is T result && predicate.Match(result)) { return result; } diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs index 48178b8b603..089f2f11b41 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/Interfaces/IPredicate{T}.cs @@ -8,7 +8,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree /// An interface representing a predicate for items of a given type. /// /// The type of items to match. - internal interface IPredicate + internal interface IPredicate + where T : class { /// /// Performs a match with the current predicate over a target instance. diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs index 63701b2330d..46fc3935f81 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByAny{T}.cs @@ -11,6 +11,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree /// /// The type of items to match. internal readonly struct PredicateByAny : IPredicate + where T : class { /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs index 86ef859282a..08052ab604c 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T,TState}.cs @@ -13,6 +13,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree /// The type of items to match. /// The type of state to use when matching items. internal readonly struct PredicateByFunc : IPredicate + where T : class { /// /// The state to give as input to . diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs index 10893f05621..eca05b0483e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByFunc{T}.cs @@ -12,6 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree /// /// The type of items to match. internal readonly struct PredicateByFunc : IPredicate + where T : class { /// /// The predicatee to use to match items. diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs index 5a2fa1f70d8..55c997b80c1 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/VisualTree.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Microsoft.Toolkit.Uwp.UI.Extensions.Tree; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; @@ -25,10 +26,9 @@ public static class VisualTree /// The descendant that was found, or . public static FrameworkElement? FindDescendant(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - return FindDescendant( - element, - (name, comparisonType), - static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + PredicateByName predicateByName = new(name, comparisonType); + + return FindDescendant(element, ref predicateByName); } /// @@ -40,26 +40,9 @@ public static class VisualTree public static T? FindDescendant(this DependencyObject element) where T : notnull, DependencyObject { - int childrenCount = VisualTreeHelper.GetChildrenCount(element); - - for (var i = 0; i < childrenCount; i++) - { - DependencyObject child = VisualTreeHelper.GetChild(element, i); + PredicateByAny predicateByAny = default; - if (child is T result) - { - return result; - } - - T? descendant = FindDescendant(child); - - if (descendant is not null) - { - return descendant; - } - } - - return null; + return FindDescendant>(element, ref predicateByAny); } /// @@ -70,7 +53,9 @@ public static class VisualTree /// The descendant that was found, or . public static DependencyObject? FindDescendant(this DependencyObject element, Type type) { - return FindDescendant(element, type, static (e, t) => e.GetType() == t); + PredicateByType predicateByType = new(type); + + return FindDescendant(element, ref predicateByType); } /// @@ -83,26 +68,9 @@ public static class VisualTree public static T? FindDescendant(this DependencyObject element, Func predicate) where T : notnull, DependencyObject { - int childrenCount = VisualTreeHelper.GetChildrenCount(element); + PredicateByFunc predicateByFunc = new(predicate); - for (var i = 0; i < childrenCount; i++) - { - DependencyObject child = VisualTreeHelper.GetChild(element, i); - - if (child is T result && predicate(result)) - { - return result; - } - - T? descendant = FindDescendant(child, predicate); - - if (descendant is not null) - { - return descendant; - } - } - - return null; + return FindDescendant>(element, ref predicateByFunc); } /// @@ -116,6 +84,23 @@ public static class VisualTree /// The descendant that was found, or . public static T? FindDescendant(this DependencyObject element, TState state, Func predicate) where T : notnull, DependencyObject + { + PredicateByFunc predicateByFunc = new(state, predicate); + + return FindDescendant>(element, ref predicateByFunc); + } + + /// + /// Find the first descendant element matching a given predicate, using a depth-first search. + /// + /// The type of elements to match. + /// The type of predicate in use. + /// The root element. + /// The predicatee to use to match the descendant nodes. + /// The descendant that was found, or . + private static T? FindDescendant(this DependencyObject element, ref TPredicate predicate) + where T : notnull, DependencyObject + where TPredicate : struct, IPredicate { int childrenCount = VisualTreeHelper.GetChildrenCount(element); @@ -123,12 +108,12 @@ public static class VisualTree { DependencyObject child = VisualTreeHelper.GetChild(element, i); - if (child is T result && predicate(result, state)) + if (child is T result && predicate.Match(result)) { return result; } - T? descendant = FindDescendant(child, state, predicate); + T? descendant = FindDescendant(child, ref predicate); if (descendant is not null) { @@ -265,10 +250,9 @@ public static IEnumerable FindDescendants(this DependencyObjec /// The ascendant that was found, or . public static FrameworkElement? FindAscendant(this DependencyObject element, string name, StringComparison comparisonType = StringComparison.Ordinal) { - return FindAscendant( - element, - (name, comparisonType), - static (e, s) => s.Name.Equals(e.Name, s.ComparisonType)); + PredicateByName predicateByName = new(name, comparisonType); + + return FindAscendant(element, ref predicateByName); } /// @@ -280,22 +264,9 @@ public static IEnumerable FindDescendants(this DependencyObjec public static T? FindAscendant(this DependencyObject element) where T : notnull, DependencyObject { - while (true) - { - DependencyObject? parent = VisualTreeHelper.GetParent(element); - - if (parent is null) - { - return null; - } + PredicateByAny predicateByAny = default; - if (parent is T result) - { - return result; - } - - element = parent; - } + return FindAscendant>(element, ref predicateByAny); } /// @@ -306,7 +277,9 @@ public static IEnumerable FindDescendants(this DependencyObjec /// The ascendant that was found, or . public static DependencyObject? FindAscendant(this DependencyObject element, Type type) { - return FindAscendant(element, type, static (e, t) => e.GetType() == t); + PredicateByType predicateByType = new(type); + + return FindAscendant(element, ref predicateByType); } /// @@ -319,22 +292,9 @@ public static IEnumerable FindDescendants(this DependencyObjec public static T? FindAscendant(this DependencyObject element, Func predicate) where T : notnull, DependencyObject { - while (true) - { - DependencyObject? parent = VisualTreeHelper.GetParent(element); + PredicateByFunc predicateByFunc = new(predicate); - if (parent is null) - { - return null; - } - - if (parent is T result && predicate(result)) - { - return result; - } - - element = parent; - } + return FindAscendant>(element, ref predicateByFunc); } /// @@ -348,6 +308,23 @@ public static IEnumerable FindDescendants(this DependencyObjec /// The ascendant that was found, or . public static T? FindAscendant(this DependencyObject element, TState state, Func predicate) where T : notnull, DependencyObject + { + PredicateByFunc predicateByFunc = new(state, predicate); + + return FindAscendant>(element, ref predicateByFunc); + } + + /// + /// Find the first ascendant element matching a given predicate. + /// + /// The type of elements to match. + /// The type of predicate in use. + /// The starting element. + /// The predicatee to use to match the ascendant nodes. + /// The ascendant that was found, or . + private static T? FindAscendant(this DependencyObject element, ref TPredicate predicate) + where T : notnull, DependencyObject + where TPredicate : struct, IPredicate { while (true) { @@ -358,7 +335,7 @@ public static IEnumerable FindDescendants(this DependencyObjec return null; } - if (parent is T result && predicate(result, state)) + if (parent is T result && predicate.Match(result)) { return result; } diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs b/Microsoft.Toolkit.uwp.UI/Extensions/Tree/Predicates/PredicateByType.cs similarity index 83% rename from Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs rename to Microsoft.Toolkit.uwp.UI/Extensions/Tree/Predicates/PredicateByType.cs index 02d4a3b61b5..897d9f4999d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/Predicates/PredicateByType{T}.cs +++ b/Microsoft.Toolkit.uwp.UI/Extensions/Tree/Predicates/PredicateByType.cs @@ -10,8 +10,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree /// /// An type matching items of a given type. /// - /// The type of items to match. - internal readonly struct PredicateByType : IPredicate + internal readonly struct PredicateByType : IPredicate { /// /// The type of element to match. @@ -19,7 +18,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Extensions.Tree private readonly Type type; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The type of element to match. public PredicateByType(Type type) @@ -29,7 +28,7 @@ public PredicateByType(Type type) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Match(T element) + public bool Match(object element) { return element.GetType() == this.type; } From 8df6288f58179ced0621104ccc5d95b2e462fbd8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 11:16:01 +0100 Subject: [PATCH 24/29] Added some manual tail recursion unwrapping --- .../Extensions/Tree/LogicalTree.cs | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 861e6d789ce..641e568de2f 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -104,6 +104,11 @@ public static class LogicalTree where T : notnull, FrameworkElement where TPredicate : struct, IPredicate { + // Jump label to manually optimize the tail recursive paths for elements with a single + // child by just overwriting the current element and jumping back to the start of the + // method. This avoids a recursive call and one stack frame every time. + Start: + if (element is Panel panel) { foreach (UIElement child in panel.Children) @@ -157,7 +162,9 @@ public static class LogicalTree if (userControl.Content is FrameworkElement content) { - return FindChild(content, ref predicate); + element = content; + + goto Start; } } else if (element is ContentControl contentControl) @@ -169,7 +176,9 @@ public static class LogicalTree if (contentControl.Content is FrameworkElement content) { - return FindChild(content, ref predicate); + element = content; + + goto Start; } } else if (element is Border border) @@ -181,7 +190,9 @@ public static class LogicalTree if (border.Child is FrameworkElement child) { - return FindChild(child, ref predicate); + element = child; + + goto Start; } } else if (element.GetContentControl() is FrameworkElement containedControl) @@ -191,7 +202,9 @@ public static class LogicalTree return result; } - return FindChild(containedControl, ref predicate); + element = containedControl; + + goto Start; } return null; @@ -299,6 +312,8 @@ public static class LogicalTree /// All the child instance from . public static IEnumerable FindChildren(this FrameworkElement element) { + Start: + if (element is Panel panel) { foreach (UIElement child in panel.Children) @@ -339,10 +354,9 @@ public static IEnumerable FindChildren(this FrameworkElement e { yield return content; - foreach (FrameworkElement childOfContent in FindChildren(content)) - { - yield return childOfContent; - } + element = content; + + goto Start; } } else if (element is ContentControl contentControl) @@ -351,10 +365,9 @@ public static IEnumerable FindChildren(this FrameworkElement e { yield return content; - foreach (FrameworkElement childOfContent in FindChildren(content)) - { - yield return childOfContent; - } + element = content; + + goto Start; } } else if (element is Border border) @@ -363,20 +376,18 @@ public static IEnumerable FindChildren(this FrameworkElement e { yield return child; - foreach (FrameworkElement childOfChild in FindChildren(child)) - { - yield return childOfChild; - } + element = child; + + goto Start; } } else if (element.GetContentControl() is FrameworkElement containedControl) { yield return containedControl; - foreach (FrameworkElement childOfChild in FindChildren(containedControl)) - { - yield return childOfChild; - } + element = containedControl; + + goto Start; } } From c63f1de99f257211f2d39e234959bd51776b3222 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 11:17:47 +0100 Subject: [PATCH 25/29] Fixed incorrect MS Docs link --- Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 641e568de2f..157c2e0628b 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -643,7 +643,7 @@ public static IEnumerable FindParents(this FrameworkElement el /// If the key is not found in the current element's resources, the logical tree is then /// searched element-by-element to look for the resource in each element's resources. /// If none of the elements contain the resource, the Application's resources are then searched. - /// See: . + /// See: . /// And also: . /// /// The to start searching for the target resource. From f13d98de4623ca1830f7c3b8f0b0da39bd921df6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Feb 2021 11:19:26 +0100 Subject: [PATCH 26/29] Improved exception for FindResource method --- Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 157c2e0628b..f33cbe7a40e 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -657,9 +657,9 @@ public static object FindResource(this FrameworkElement element, object resource return value!; } - static object Throw() => throw new Exception("No resource was found with the specified key"); + static object Throw(object resourceKey) => throw new KeyNotFoundException($"No resource was found with the key \"{resourceKey}\""); - return Throw(); + return Throw(resourceKey); } /// From ac3986a4109aed91360a0287153d165277bf2c60 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Feb 2021 19:47:09 +0100 Subject: [PATCH 27/29] Added FrameworkElement.TryFindResource overload --- .../TokenizingTextBoxItem.AutoSuggestBox.cs | 10 +------ .../Extensions/Tree/LogicalTree.cs | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs index 15bc0a438b5..edbaa29fc71 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxItem.AutoSuggestBox.cs @@ -105,15 +105,7 @@ private void OnApplyTemplateAutoSuggestBox(AutoSuggestBox auto) { // This can be expensive, could we optimize? // Also, this is changing the FontSize on the IconSource (which could be shared?) - if (Owner.TryFindResource("TokenizingTextBoxIconFontSize", out object resource) && - resource is double fontSize) - { - fis.FontSize = fontSize; - } - else - { - fis.FontSize = 16; - } + fis.FontSize = Owner.TryFindResource("TokenizingTextBoxIconFontSize") as double? ?? 16; } var iconBinding = new Binding() diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index f33cbe7a40e..a97c3f5a90d 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -672,11 +672,10 @@ public static object FindResource(this FrameworkElement element, object resource /// /// The to start searching for the target resource. /// The resource key to search for. - /// The resulting value, if present. - /// Whether or not a value with the specified key has been found. - public static bool TryFindResource(this FrameworkElement element, object resourceKey, out object? value) + /// The requested resource, or if it wasn't found. + public static object? TryFindResource(this FrameworkElement element, object resourceKey) { - value = null; + object? value = null; FrameworkElement? current = element; @@ -687,7 +686,7 @@ public static bool TryFindResource(this FrameworkElement element, object resourc { if (current.Resources?.TryGetValue(resourceKey, out value) == true) { - return true; + return value; } current = current.Parent as FrameworkElement; @@ -695,7 +694,26 @@ public static bool TryFindResource(this FrameworkElement element, object resourc while (current is not null); // Finally try application resources - return Application.Current?.Resources?.TryGetValue(resourceKey, out value) == true; + _ = Application.Current?.Resources?.TryGetValue(resourceKey, out value); + + return value; + } + + /// + /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. + /// If the key is not found in the current element's resources, the logical tree is then + /// searched element-by-element to look for the resource in each element's resources. + /// If none of the elements contain the resource, the Application's resources are then searched. + /// See: . + /// And also: . + /// + /// The to start searching for the target resource. + /// The resource key to search for. + /// The resulting value, if present. + /// Whether or not a value with the specified key has been found. + public static bool TryFindResource(this FrameworkElement element, object resourceKey, out object? value) + { + return (value = TryFindResource(element, resourceKey)) is not null; } } } From 96a634201bb063e415eaacb724c4fac387ff3b76 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Feb 2021 20:04:45 +0100 Subject: [PATCH 28/29] Fixed a typo in XML docs Co-authored-by: Michael Hawker MSFT (XAML Llama) <24302614+michael-hawker@users.noreply.github.com> --- Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index a97c3f5a90d..2efd800f26c 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -639,7 +639,7 @@ public static IEnumerable FindParents(this FrameworkElement el } /// - /// Provides a WPF compatible version of TryFindResource to provide a static resource lookup. + /// Provides a WPF compatible version of FindResource to provide a static resource lookup. /// If the key is not found in the current element's resources, the logical tree is then /// searched element-by-element to look for the resource in each element's resources. /// If none of the elements contain the resource, the Application's resources are then searched. From 13b972a5490c8bdfeefa8b66755de6c41f0188af Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Feb 2021 20:22:43 +0100 Subject: [PATCH 29/29] Moved UserControl test paths down in priority --- .../Extensions/Tree/LogicalTree.cs | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs index 2efd800f26c..4e07d0ddb50 100644 --- a/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs +++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Tree/LogicalTree.cs @@ -153,44 +153,46 @@ public static class LogicalTree } } } - else if (element is UserControl userControl) + else if (element is ContentControl contentControl) { - if (userControl.Content is T result && predicate.Match(result)) + if (contentControl.Content is T result && predicate.Match(result)) { return result; } - if (userControl.Content is FrameworkElement content) + if (contentControl.Content is FrameworkElement content) { element = content; goto Start; } } - else if (element is ContentControl contentControl) + else if (element is Border border) { - if (contentControl.Content is T result && predicate.Match(result)) + if (border.Child is T result && predicate.Match(result)) { return result; } - if (contentControl.Content is FrameworkElement content) + if (border.Child is FrameworkElement child) { - element = content; + element = child; goto Start; } } - else if (element is Border border) + else if (element is UserControl userControl) { - if (border.Child is T result && predicate.Match(result)) + // We put UserControl right before the slower reflection fallback path as + // this type is less likely to be used compared to the other ones above. + if (userControl.Content is T result && predicate.Match(result)) { return result; } - if (border.Child is FrameworkElement child) + if (userControl.Content is FrameworkElement content) { - element = child; + element = content; goto Start; } @@ -348,9 +350,9 @@ public static IEnumerable FindChildren(this FrameworkElement e } } } - else if (element is UserControl userControl) + else if (element is ContentControl contentControl) { - if (userControl.Content is FrameworkElement content) + if (contentControl.Content is FrameworkElement content) { yield return content; @@ -359,24 +361,24 @@ public static IEnumerable FindChildren(this FrameworkElement e goto Start; } } - else if (element is ContentControl contentControl) + else if (element is Border border) { - if (contentControl.Content is FrameworkElement content) + if (border.Child is FrameworkElement child) { - yield return content; + yield return child; - element = content; + element = child; goto Start; } } - else if (element is Border border) + else if (element is UserControl userControl) { - if (border.Child is FrameworkElement child) + if (userControl.Content is FrameworkElement content) { - yield return child; + yield return content; - element = child; + element = content; goto Start; }