diff --git a/samples/BehaviorsTestApplication/Controls/SingleSelectionTabControl.cs b/samples/BehaviorsTestApplication/Controls/SingleSelectionTabControl.cs
new file mode 100644
index 000000000..5e42fd6aa
--- /dev/null
+++ b/samples/BehaviorsTestApplication/Controls/SingleSelectionTabControl.cs
@@ -0,0 +1,14 @@
+using System;
+using Avalonia.Controls;
+
+namespace BehaviorsTestApplication.Controls;
+
+public class SingleSelectionTabControl : TabControl
+{
+ protected override Type StyleKeyOverride => typeof(TabControl);
+
+ static SingleSelectionTabControl()
+ {
+ SelectionModeProperty.OverrideDefaultValue(SelectionMode.Single);
+ }
+}
diff --git a/samples/BehaviorsTestApplication/Views/MainView.axaml b/samples/BehaviorsTestApplication/Views/MainView.axaml
index e41a046dc..a4ed52057 100644
--- a/samples/BehaviorsTestApplication/Views/MainView.axaml
+++ b/samples/BehaviorsTestApplication/Views/MainView.axaml
@@ -13,17 +13,20 @@
-
-
+
+
+
+
@@ -150,6 +153,6 @@
-
+
diff --git a/samples/BehaviorsTestApplication/Views/MainView.axaml.cs b/samples/BehaviorsTestApplication/Views/MainView.axaml.cs
index 988073e86..61441b7c4 100644
--- a/samples/BehaviorsTestApplication/Views/MainView.axaml.cs
+++ b/samples/BehaviorsTestApplication/Views/MainView.axaml.cs
@@ -1,5 +1,4 @@
-using Avalonia.Controls;
-using System.Linq;
+using Avalonia.Controls;
namespace BehaviorsTestApplication.Views;
@@ -9,28 +8,4 @@ public MainView()
{
InitializeComponent();
}
-
- private void OnSearchTextChanged(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
- {
- var query = SearchBox.Text?.ToLowerInvariant() ?? string.Empty;
- var visibleCount = 0;
-
- var tabItems = PagesTabControl.Items.OfType().ToList();
-
- foreach (var item in tabItems)
- {
- var header = item.Header?.ToString()?.ToLowerInvariant() ?? string.Empty;
- var visible = header.Contains(query);
- item.IsVisible = visible;
-
- if (visible)
- {
- visibleCount++;
- }
- }
-
- PagesTabControl.SelectedItem = tabItems.FirstOrDefault(x => x.IsVisible);
-
- NoMatchesText.IsVisible = visibleCount == 0;
- }
}
diff --git a/src/Avalonia.Xaml.Interactions.Custom/SelectingItemsControl/SelectingItemsControlSearchBehavior.cs b/src/Avalonia.Xaml.Interactions.Custom/SelectingItemsControl/SelectingItemsControlSearchBehavior.cs
new file mode 100644
index 000000000..ca29625f4
--- /dev/null
+++ b/src/Avalonia.Xaml.Interactions.Custom/SelectingItemsControl/SelectingItemsControlSearchBehavior.cs
@@ -0,0 +1,96 @@
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Xaml.Interactivity;
+
+namespace Avalonia.Xaml.Interactions.Custom;
+
+///
+/// Filters items based on the text of a search box.
+///
+public sealed class SelectingItemsControlSearchBehavior : StyledElementBehavior
+{
+ ///
+ /// Identifies the avalonia property.
+ ///
+ public static readonly StyledProperty SearchBoxProperty =
+ AvaloniaProperty.Register(nameof(SearchBox));
+
+ ///
+ /// Identifies the avalonia property.
+ ///
+ public static readonly StyledProperty NoMatchesControlProperty =
+ AvaloniaProperty.Register(nameof(NoMatchesControl));
+
+ ///
+ /// Gets or sets the search box control.
+ ///
+ [ResolveByName]
+ public TextBox? SearchBox
+ {
+ get => GetValue(SearchBoxProperty);
+ set => SetValue(SearchBoxProperty, value);
+ }
+
+ ///
+ /// Gets or sets the control displayed when no matches are found.
+ ///
+ [ResolveByName]
+ public Control? NoMatchesControl
+ {
+ get => GetValue(NoMatchesControlProperty);
+ set => SetValue(NoMatchesControlProperty, value);
+ }
+
+ ///
+ protected override void OnAttachedToVisualTree()
+ {
+ if (SearchBox is not null)
+ {
+ SearchBox.AddHandler(InputElement.TextInputEvent, SearchBox_TextChanged, RoutingStrategies.Bubble);
+ SearchBox.AddHandler(TextBox.TextChangedEvent, SearchBox_TextChanged, RoutingStrategies.Bubble);
+ }
+ }
+
+ ///
+ protected override void OnDetachedFromVisualTree()
+ {
+ if (SearchBox is not null)
+ {
+ SearchBox.RemoveHandler(InputElement.TextInputEvent, SearchBox_TextChanged);
+ SearchBox.RemoveHandler(TextBox.TextChangedEvent, SearchBox_TextChanged);
+ }
+ }
+
+ private void SearchBox_TextChanged(object? sender, RoutedEventArgs e)
+ {
+ if (AssociatedObject is null)
+ {
+ return;
+ }
+
+ var query = SearchBox?.Text?.ToLowerInvariant() ?? string.Empty;
+ var visibleCount = 0;
+ var tabItems = AssociatedObject.Items.OfType().ToList();
+
+ foreach (var item in tabItems)
+ {
+ var header = item.Header?.ToString()?.ToLowerInvariant() ?? string.Empty;
+ var visible = header.Contains(query);
+ item.IsVisible = visible;
+ if (visible)
+ {
+ visibleCount++;
+ }
+ }
+
+ AssociatedObject.SelectedItem = tabItems.FirstOrDefault(x => x.IsVisible);
+
+ if (NoMatchesControl is not null)
+ {
+ NoMatchesControl.IsVisible = visibleCount == 0;
+ }
+ }
+}