diff --git a/demo/Ursa.Demo/Pages/TagInputDemo.axaml b/demo/Ursa.Demo/Pages/TagInputDemo.axaml
new file mode 100644
index 0000000..d5751e5
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TagInputDemo.axaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/TagInputDemo.axaml.cs b/demo/Ursa.Demo/Pages/TagInputDemo.axaml.cs
new file mode 100644
index 0000000..f19dc0a
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TagInputDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class TagInputDemo : UserControl
+{
+ public TagInputDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Views/MainWindow.axaml b/demo/Ursa.Demo/Views/MainWindow.axaml
index d6da845..1b6c162 100644
--- a/demo/Ursa.Demo/Views/MainWindow.axaml
+++ b/demo/Ursa.Demo/Views/MainWindow.axaml
@@ -47,6 +47,9 @@
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/TagInput.axaml b/src/Ursa.Themes.Semi/Controls/TagInput.axaml
new file mode 100644
index 0000000..6829870
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/TagInput.axaml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index 7c621a3..bbf2752 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -9,6 +9,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/TagInput.axaml b/src/Ursa.Themes.Semi/Themes/Dark/TagInput.axaml
new file mode 100644
index 0000000..c32a707
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Dark/TagInput.axaml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
index c892d42..c9f2bea 100644
--- a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
@@ -9,6 +9,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Light/TagInput.axaml b/src/Ursa.Themes.Semi/Themes/Light/TagInput.axaml
new file mode 100644
index 0000000..c3b76e1
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Light/TagInput.axaml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
index c892d42..c9f2bea 100644
--- a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
@@ -9,6 +9,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/TagInput.axaml b/src/Ursa.Themes.Semi/Themes/Shared/TagInput.axaml
new file mode 100644
index 0000000..2f3d2b7
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Shared/TagInput.axaml
@@ -0,0 +1,5 @@
+
+
+ M17.6568 19.7782C18.2426 20.3639 19.1924 20.3639 19.7782 19.7782C20.3639 19.1924 20.3639 18.2426 19.7782 17.6568L14.1213 12L19.7782 6.34313C20.3639 5.75734 20.3639 4.8076 19.7782 4.22181C19.1924 3.63602 18.2426 3.63602 17.6568 4.22181L12 9.87866L6.34313 4.22181C5.75734 3.63602 4.8076 3.63602 4.22181 4.22181C3.63602 4.8076 3.63602 5.75734 4.22181 6.34313L9.87866 12L4.22181 17.6568C3.63602 18.2426 3.63602 19.1924 4.22181 19.7782C4.8076 20.3639 5.75734 20.3639 6.34313 19.7782L12 14.1213L17.6568 19.7782Z
+ 32
+
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
index 5e6a238..777a0a4 100644
--- a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
@@ -8,5 +8,6 @@
+
diff --git a/src/Ursa/AssemblyInfo.cs b/src/Ursa/AssemblyInfo.cs
index 0b0375e..af243bf 100644
--- a/src/Ursa/AssemblyInfo.cs
+++ b/src/Ursa/AssemblyInfo.cs
@@ -1,5 +1,5 @@
using Avalonia.Metadata;
+[assembly:XmlnsPrefix("https://irihi.tech/ursa", "u")]
[assembly:XmlnsDefinition("https://irihi.tech/ursa", "Ursa")]
[assembly:XmlnsDefinition("https://irihi.tech/ursa", "Ursa.Controls")]
-[assembly:XmlnsPrefix("https://irihi.tech/ursa", "u")]
\ No newline at end of file
diff --git a/src/Ursa/Controls/TagInput/ClosableTag.cs b/src/Ursa/Controls/TagInput/ClosableTag.cs
new file mode 100644
index 0000000..de609f2
--- /dev/null
+++ b/src/Ursa/Controls/TagInput/ClosableTag.cs
@@ -0,0 +1,46 @@
+using System.Windows.Input;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+
+namespace Ursa.Controls;
+
+[TemplatePart(PART_CloseButton, typeof(PathIcon))]
+public class ClosableTag: ContentControl
+{
+ public const string PART_CloseButton = "PART_CloseButton";
+ private PathIcon? _icon;
+ public static readonly StyledProperty CommandProperty = AvaloniaProperty.Register(
+ nameof(Command));
+
+ public ICommand? Command
+ {
+ get => GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ if (_icon != null)
+ {
+ _icon.PointerPressed -= OnPointerPressed;
+ }
+ _icon = e.NameScope.Find(PART_CloseButton);
+ if (_icon != null)
+ {
+ _icon.PointerPressed += OnPointerPressed;
+ }
+
+ }
+
+ private void OnPointerPressed(object? sender, PointerPressedEventArgs args)
+ {
+ if (Command != null && Command.CanExecute(null))
+ {
+ Command.Execute(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/TagInput/TagInput.cs b/src/Ursa/Controls/TagInput/TagInput.cs
new file mode 100644
index 0000000..5ee089b
--- /dev/null
+++ b/src/Ursa/Controls/TagInput/TagInput.cs
@@ -0,0 +1,213 @@
+using System.Collections;
+using System.Collections.ObjectModel;
+using Avalonia;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Templates;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Layout;
+using Avalonia.Styling;
+
+namespace Ursa.Controls;
+
+[TemplatePart(PART_ItemsControl, typeof(ItemsControl))]
+public class TagInput : TemplatedControl
+{
+ public const string PART_ItemsControl = "PART_ItemsControl";
+
+ private readonly TextBox _textBox;
+ private ItemsControl? _itemsControl;
+
+
+ public static readonly StyledProperty> TagsProperty =
+ AvaloniaProperty.Register>(
+ nameof(Tags));
+
+ public IList Tags
+ {
+ get => GetValue(TagsProperty);
+ set => SetValue(TagsProperty, value);
+ }
+
+ public static readonly DirectProperty ItemsProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Items), o => o.Items);
+
+ private IList _items;
+
+ public IList Items
+ {
+ get => _items;
+ private set => SetAndRaise(ItemsProperty, ref _items, value);
+ }
+
+ public TagInput()
+ {
+ _textBox = new TextBox();
+ _textBox.AddHandler(InputElement.KeyDownEvent, OnTextBoxKeyDown, RoutingStrategies.Tunnel);
+ Items = new AvaloniaList