diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs
index 60d6ef2..ea8f8f5 100644
--- a/demo/Ursa.Demo/Models/MenuKeys.cs
+++ b/demo/Ursa.Demo/Models/MenuKeys.cs
@@ -22,6 +22,7 @@ public static class MenuKeys
public const string MenuKeyNumericUpDown = "NumericUpDown";
public const string MenuKeyPagination = "Pagination";
public const string MenuKeyRangeSlider = "RangeSlider";
+ public const string MenuKeySelectionList = "SelectionList";
public const string MenuKeyTagInput = "TagInput";
public const string MenuKeyTimeline = "Timeline";
public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
diff --git a/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml b/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml
new file mode 100644
index 0000000..18068cf
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml.cs b/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml.cs
new file mode 100644
index 0000000..60d5ccd
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/SelectionBoxDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class SelectionListDemo : UserControl
+{
+ public SelectionListDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index 6797172..0a20fe2 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -44,6 +44,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyNumericUpDown => new NumericUpDownDemoViewModel(),
MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(),
MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(),
+ MenuKeys.MenuKeySelectionList => new SelectionListDemoViewModel(),
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index cf807af..1377af9 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -31,6 +31,7 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "NumericUpDown", Key = MenuKeys.MenuKeyNumericUpDown, Status = "New" },
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"},
+ new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList, Status = "New" },
new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput },
new() { MenuHeader = "Theme Toggler", Key = MenuKeys.MenuKeyThemeToggler },
new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" },
diff --git a/demo/Ursa.Demo/ViewModels/SelectionListDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/SelectionListDemoViewModel.cs
new file mode 100644
index 0000000..3711edd
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/SelectionListDemoViewModel.cs
@@ -0,0 +1,23 @@
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class SelectionListDemoViewModel: ObservableObject
+{
+ public ObservableCollection Items { get; set; }
+ [ObservableProperty] private string? _selectedItem;
+
+ public SelectionListDemoViewModel()
+ {
+ Items = new ObservableCollection()
+ {
+ "Ding", "Otter", "Husky", "Mr. 17", "Cass"
+ };
+ }
+
+ public void Clear()
+ {
+ SelectedItem = null;
+ }
+}
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Controls/SelectionList.axaml b/src/Ursa.Themes.Semi/Controls/SelectionList.axaml
new file mode 100644
index 0000000..8306ddc
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/SelectionList.axaml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index f770a28..4bd0c67 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -21,6 +21,7 @@
+
diff --git a/src/Ursa/Controls/SelectionList/SelectionList.cs b/src/Ursa/Controls/SelectionList/SelectionList.cs
new file mode 100644
index 0000000..ba102cc
--- /dev/null
+++ b/src/Ursa/Controls/SelectionList/SelectionList.cs
@@ -0,0 +1,153 @@
+using System.Runtime.CompilerServices;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Selection;
+using Avalonia.Controls.Templates;
+using Avalonia.Input;
+using Avalonia.Rendering.Composition;
+using Avalonia.Rendering.Composition.Animations;
+using Irihi.Avalonia.Shared.Helpers;
+
+namespace Ursa.Controls;
+
+[TemplatePart(PART_Indicator, typeof(ContentPresenter))]
+public class SelectionList: SelectingItemsControl
+{
+ public const string PART_Indicator = "PART_Indicator";
+ private static readonly FuncTemplate DefaultPanel = new(() => new StackPanel());
+
+ private ImplicitAnimationCollection? _implicitAnimations;
+ private ContentPresenter? _indicator;
+
+ public static readonly StyledProperty IndicatorProperty = AvaloniaProperty.Register(
+ nameof(Indicator));
+
+ public Control? Indicator
+ {
+ get => GetValue(IndicatorProperty);
+ set => SetValue(IndicatorProperty, value);
+ }
+
+ static SelectionList()
+ {
+ SelectionModeProperty.OverrideMetadata(
+ new StyledPropertyMetadata(
+ defaultValue: SelectionMode.Single,
+ coerce: (o, mode) => SelectionMode.Single)
+ );
+ SelectedItemProperty.Changed.AddClassHandler((list, args) =>
+ list.OnSelectedItemChanged(args));
+ }
+
+ private void OnSelectedItemChanged(AvaloniaPropertyChangedEventArgs