From 550b980f545029590144e77f684c9b7cba3bbe8f Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Sat, 20 Jun 2026 18:16:14 +0800 Subject: [PATCH] feat: Add SearchBar control, themes and demo. --- .../Data/DemoInfo.json | 1 + .../UserControl/Controls/SearchBarDemo.axaml | 144 +++++++++ .../Controls/SearchBarDemo.axaml.cs | 25 ++ .../Controls/Input/SearchBar.cs | 129 ++++++++ .../Themes/Styles/MergedStyles.axaml | 1 + .../Themes/Styles/SearchBar.axaml | 294 ++++++++++++++++++ 6 files changed, 594 insertions(+) create mode 100644 src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml create mode 100644 src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml.cs create mode 100644 src/Avalonia/HandyControl_Avalonia/Controls/Input/SearchBar.cs create mode 100644 src/Avalonia/HandyControl_Avalonia/Themes/Styles/SearchBar.axaml diff --git a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json index 5c507e64..d2c0eb21 100644 --- a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json +++ b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json @@ -47,6 +47,7 @@ [ "ProgressButton", "ProgressButtonDemo", "Brush.ProgressBarStatusBar8", "", "Button" ], [ "Card", "CardDemo", "Brush.ContactCard", "", "DataDisplay" ], [ "PinBox", "PinBoxDemo", "Brush.Ellipsis", "", "DataEntry" ], + [ "SearchBar", "SearchBarDemo", "Brush.Search", "", "DataEntry" ], [ "ElementGroup", "ElementGroupDemo", "Brush.ButtonGroup", "", "Layout" ], [ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ], [ "Grid", "GridDemo", "Brush.GridPane", "", "Layout" ], diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml new file mode 100644 index 00000000..bd17ae08 --- /dev/null +++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml.cs b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml.cs new file mode 100644 index 00000000..861e4bd2 --- /dev/null +++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/SearchBarDemo.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia.Interactivity; +using HandyControl.Controls; +using HandyControl.Data; + +namespace HandyControlDemo.UserControl; + +public partial class SearchBarDemo : Avalonia.Controls.UserControl +{ + public SearchBarDemo() + { + InitializeComponent(); + } + + private void SearchBar_OnSearchStarted(object? sender, FunctionEventArgs e) + { + if (string.IsNullOrWhiteSpace(e.Info)) + { + return; + } + + var growlType = System.Type.GetType("HandyControl.Controls.Growl, HandyControl"); + var method = growlType?.GetMethod("Info", [typeof(string), typeof(string)]); + method?.Invoke(null, [e.Info, string.Empty]); + } +} diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/Input/SearchBar.cs b/src/Avalonia/HandyControl_Avalonia/Controls/Input/SearchBar.cs new file mode 100644 index 00000000..61f24624 --- /dev/null +++ b/src/Avalonia/HandyControl_Avalonia/Controls/Input/SearchBar.cs @@ -0,0 +1,129 @@ +using System; +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Interactivity; +using HandyControl.Data; + +namespace HandyControl.Controls; + +[TemplatePart(Name = ElementSearchButton, Type = typeof(Button))] +public class SearchBar : TextBox +{ + private const string ElementSearchButton = "PART_SearchButton"; + + public static readonly RoutedEvent> SearchStartedEvent = + RoutedEvent.Register>(nameof(SearchStarted), RoutingStrategies.Bubble); + + public static readonly StyledProperty IsRealTimeProperty = + AvaloniaProperty.Register(nameof(IsRealTime)); + + public static readonly StyledProperty CommandProperty = + AvaloniaProperty.Register(nameof(Command)); + + public static readonly StyledProperty CommandParameterProperty = + AvaloniaProperty.Register(nameof(CommandParameter)); + + public static readonly StyledProperty CommandTargetProperty = + AvaloniaProperty.Register(nameof(CommandTarget)); + + private Button? _searchButton; + + static SearchBar() + { + TextProperty.Changed.AddClassHandler((searchBar, _) => + { + if (searchBar.IsRealTime) + { + searchBar.OnSearchStarted(); + } + }); + } + + public event EventHandler> SearchStarted + { + add => AddHandler(SearchStartedEvent, value); + remove => RemoveHandler(SearchStartedEvent, value); + } + + public bool IsRealTime + { + get => GetValue(IsRealTimeProperty); + set => SetValue(IsRealTimeProperty, value); + } + + public ICommand? Command + { + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + public object? CommandParameter + { + get => GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + public IInputElement? CommandTarget + { + get => GetValue(CommandTargetProperty); + set => SetValue(CommandTargetProperty, value); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + if (_searchButton != null) + { + _searchButton.Click -= SearchButtonOnClick; + } + + _searchButton = e.NameScope.Find +