From a01245a78411816663c4b8ab53011ed04a69a433 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Mar 2026 00:25:21 +0500 Subject: [PATCH] QoL for ControlCatalog: add sidebar search, auto-sort, and default page selection (#20987) * feat(ControlCatalog): add sidebar search, auto-sort, and default page selection - Add search TextBox (PART_SearchBox) to HamburgerMenu pane template that filters sidebar items by header text (case-insensitive) - Auto-sort TabItems alphabetically by Header on first load - Add IsDefaultPage attached property on HamburgerMenu to preselect a specific TabItem (set on Buttons page in MainView) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: replace IsDefaultPage attached property with simple IsSelected IsSelected on TabItem is sufficient for preselecting a page after auto-sort. Removes the custom IsDefaultPage attached property. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- samples/ControlCatalog/MainView.xaml | 2 +- .../HamburgerMenu/HamburgerMenu.cs | 68 ++++++++++++++++++- .../HamburgerMenu/HamburgerMenu.xaml | 17 ++++- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index adea1b90fc..3a6d57801a 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -16,7 +16,7 @@ - + diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs b/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs index 57f8a138af..0e45b28a78 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs @@ -1,4 +1,6 @@ -using Avalonia; +using System; +using System.Linq; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Media; @@ -8,6 +10,8 @@ namespace ControlSamples public class HamburgerMenu : TabControl { private SplitView? _splitView; + private TextBox? _searchBox; + private bool _initialized; public static readonly StyledProperty PaneBackgroundProperty = SplitView.PaneBackgroundProperty.AddOwner(); @@ -41,6 +45,68 @@ namespace ControlSamples base.OnApplyTemplate(e); _splitView = e.NameScope.Find("PART_NavigationPane"); + _searchBox = e.NameScope.Find("PART_SearchBox"); + + if (_searchBox is not null) + { + _searchBox.TextChanged += OnSearchTextChanged; + } + } + + protected override void OnLoaded(Avalonia.Interactivity.RoutedEventArgs e) + { + base.OnLoaded(e); + + if (!_initialized) + { + _initialized = true; + SortItems(); + } + } + + private void SortItems() + { + var items = Items.OfType().ToList(); + var sorted = items.OrderBy(t => t.Header?.ToString() ?? "", StringComparer.OrdinalIgnoreCase).ToList(); + + // Only reorder if needed + bool needsSort = false; + for (int i = 0; i < items.Count; i++) + { + if (!ReferenceEquals(items[i], sorted[i])) + { + needsSort = true; + break; + } + } + + if (needsSort) + { + Items.Clear(); + foreach (var item in sorted) + { + Items.Add(item); + } + } + } + + private void OnSearchTextChanged(object? sender, TextChangedEventArgs e) + { + var searchText = _searchBox?.Text; + var hasFilter = !string.IsNullOrWhiteSpace(searchText); + + foreach (var item in Items.OfType()) + { + if (hasFilter) + { + var header = item.Header?.ToString() ?? ""; + item.IsVisible = header.Contains(searchText!, StringComparison.OrdinalIgnoreCase); + } + else + { + item.IsVisible = true; + } + } } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml index 49972bdc64..d9b3e3c73f 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml @@ -175,10 +175,21 @@ OpenPaneLength="{StaticResource PaneExpandWidth}" PaneBackground="Transparent"> - + + + + + + @@ -195,7 +206,7 @@