diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml b/demo/Ursa.Demo/Pages/RatingDemo.axaml
new file mode 100644
index 0000000..89ab51f
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml
@@ -0,0 +1,123 @@
+
+
+
+ M12 5.99999C10.5 2.12432 5.75193 2.0557 3.40383 4.4038C0.865423 6.94221 0.999999 10.5 3.50001 14C5.52139 16.8299 9.83088 20.3136 11.4069 21.5438C11.7573 21.8172 12.2427 21.8172 12.5931 21.5438C14.1691 20.3136 18.4786 16.8299 20.5 14C23 10.5 23.1346 6.94221 20.5962 4.4038C18.2481 2.0557 14.5 2.12432 12 5.99999Z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs b/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs
new file mode 100644
index 0000000..8168903
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/RatingDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia.Controls;
+using Ursa.Demo.ViewModels;
+
+namespace Ursa.Demo.Pages;
+
+public partial class RatingDemo : UserControl
+{
+ public RatingDemo()
+ {
+ InitializeComponent();
+ this.DataContext = new RatingDemoViewModel();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index f981fa6..89a8532 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -51,6 +51,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyNumPad => new NumPadDemoViewModel(),
MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(),
MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(),
+ MenuKeys.MenuKeyRating => new RatingDemoViewModel(),
MenuKeys.MenuKeyScrollToButton => new ScrollToButtonDemoViewModel(),
MenuKeys.MenuKeySelectionList => new SelectionListDemoViewModel(),
MenuKeys.MenuKeySkeleton => new SkeletonDemoViewModel(),
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index ead5ab8..a6c1b79 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -38,6 +38,7 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "NumPad", Key = MenuKeys.MenuKeyNumPad },
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider },
+ new() { MenuHeader = "Rating", Key = MenuKeys.MenuKeyRating, Status = "New"},
new() { MenuHeader = "Scroll To", Key = MenuKeys.MenuKeyScrollToButton },
new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList },
new() { MenuHeader = "Skeleton", Key = MenuKeys.MenuKeySkeleton },
@@ -83,6 +84,7 @@ public static class MenuKeys
public const string MenuKeyNumPad = "NumPad";
public const string MenuKeyPagination = "Pagination";
public const string MenuKeyRangeSlider = "RangeSlider";
+ public const string MenuKeyRating = "Rating";
public const string MenuKeyScrollToButton = "ScrollToButton";
public const string MenuKeySelectionList = "SelectionList";
public const string MenuKeyTagInput = "TagInput";
diff --git a/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs
new file mode 100644
index 0000000..6028660
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/RatingDemoViewModel.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class RatingDemoViewModel : ViewModelBase
+{
+ [ObservableProperty] private bool _allowClear = true;
+ [ObservableProperty] private bool _allowHalf = true;
+ [ObservableProperty] private bool _isEnabled = true;
+ [ObservableProperty] private double _value;
+ [ObservableProperty] private double _defaultValue = 2.3;
+ [ObservableProperty] private int _count = 5;
+}
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Controls/Rating.axaml b/src/Ursa.Themes.Semi/Controls/Rating.axaml
new file mode 100644
index 0000000..6a91411
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/Rating.axaml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index bf6acad..23ba36e 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -28,6 +28,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml
new file mode 100644
index 0000000..f0cc810
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Dark/Rating.axaml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
index 7f6bc9c..86727f2 100644
--- a/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Dark/_index.axaml
@@ -12,6 +12,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml
new file mode 100644
index 0000000..ddb703a
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Light/Rating.axaml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
index 7f6bc9c..86727f2 100644
--- a/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Light/_index.axaml
@@ -12,6 +12,7 @@
+
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml
new file mode 100644
index 0000000..5065bb6
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Themes/Shared/Rating.axaml
@@ -0,0 +1,6 @@
+
+ 24
+ 16
+ 3 4
+ M10.7525 1.90411C11.1451 0.698628 12.8549 0.698631 13.2475 1.90411L15.2395 8.01946H21.6858C22.9565 8.01946 23.4848 9.64143 22.4568 10.3865L17.2417 14.1659L19.2337 20.2813C19.6263 21.4868 18.2431 22.4892 17.2151 21.7442L12 17.9647L6.78489 21.7442C5.75687 22.4892 4.37368 21.4868 4.76635 20.2813L6.75834 14.1659L1.54323 10.3865C0.515206 9.64142 1.04354 8.01946 2.31425 8.01946H8.76048L10.7525 1.90411Z
+
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
index 658f6ae..5350d09 100644
--- a/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
+++ b/src/Ursa.Themes.Semi/Themes/Shared/_index.axaml
@@ -13,6 +13,7 @@
+
diff --git a/src/Ursa/Controls/Rating/Rating.cs b/src/Ursa/Controls/Rating/Rating.cs
new file mode 100644
index 0000000..e2f277e
--- /dev/null
+++ b/src/Ursa/Controls/Rating/Rating.cs
@@ -0,0 +1,257 @@
+using Avalonia;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Interactivity;
+
+namespace Ursa.Controls;
+
+[PseudoClasses(PC_Selected)]
+[TemplatePart(PART_ItemsControl, typeof(ItemsControl))]
+public class Rating : TemplatedControl
+{
+ public const string PART_ItemsControl = "PART_ItemsControl";
+ protected const string PC_Selected = ":selected";
+
+ public static readonly StyledProperty ValueProperty =
+ AvaloniaProperty.Register(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly StyledProperty AllowClearProperty =
+ AvaloniaProperty.Register(nameof(AllowClear), true);
+
+ public static readonly StyledProperty AllowHalfProperty =
+ AvaloniaProperty.Register(nameof(AllowHalf));
+
+ public static readonly StyledProperty