diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs
index 64c2e37..2b30912 100644
--- a/demo/Ursa.Demo/Models/MenuKeys.cs
+++ b/demo/Ursa.Demo/Models/MenuKeys.cs
@@ -9,6 +9,7 @@ public static class MenuKeys
public const string MenuKeyDialog = "Dialog";
public const string MenuKeyDivider = "Divider";
public const string MenuKeyDualBadge = "DualBadge";
+ public const string MenuKeyEnumSelector = "EnumSelector";
public const string MenuKeyImageViewer = "ImageViewer";
public const string MenuKeyIpBox = "IPv4Box";
public const string MenuKeyIconButton = "IconButton";
@@ -18,7 +19,9 @@ public static class MenuKeys
public const string MenuKeyNavigation = "Navigation";
public const string MenuKeyNumericUpDown = "NumericUpDown";
public const string MenuKeyPagination = "Pagination";
+ public const string MenuKeyRangeSlider = "RangeSlider";
public const string MenuKeyTagInput = "TagInput";
public const string MenuKeyTimeline = "Timeline";
+ public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/ButtonGroupDemo.axaml b/demo/Ursa.Demo/Pages/ButtonGroupDemo.axaml
index 30c0a03..4081616 100644
--- a/demo/Ursa.Demo/Pages/ButtonGroupDemo.axaml
+++ b/demo/Ursa.Demo/Pages/ButtonGroupDemo.axaml
@@ -13,10 +13,23 @@
mc:Ignorable="d">
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml b/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml
new file mode 100644
index 0000000..b35256d
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml.cs b/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml.cs
new file mode 100644
index 0000000..da6250a
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/EnumSelectorDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class EnumSelectorDemo : UserControl
+{
+ public EnumSelectorDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml b/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml
new file mode 100644
index 0000000..7aa427b
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml.cs b/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml.cs
new file mode 100644
index 0000000..fe3181d
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/RangeSliderDemo.axaml.cs
@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Ursa.Demo.ViewModels;
+
+namespace Ursa.Demo.Pages;
+
+public partial class RangeSliderDemo : UserControl
+{
+ public RangeSliderDemo()
+ {
+ InitializeComponent();
+ this.DataContext = new RangeSliderDemoViewModel();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml b/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml
new file mode 100644
index 0000000..ae22c4b
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml.cs b/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml.cs
new file mode 100644
index 0000000..72e0a25
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TwoTonePathIconDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class TwoTonePathIconDemo : UserControl
+{
+ public TwoTonePathIconDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/ButtonGroupDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/ButtonGroupDemoViewModel.cs
index 88c5299..02f6ea1 100644
--- a/demo/Ursa.Demo/ViewModels/ButtonGroupDemoViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/ButtonGroupDemoViewModel.cs
@@ -1,11 +1,35 @@
using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.Input;
+using Ursa.Controls;
namespace Ursa.Demo.ViewModels;
public class ButtonGroupDemoViewModel: ViewModelBase
{
- public ObservableCollection Items { get; set; } = new ()
+ public ObservableCollection Items { get; set; } = new ()
{
- "Ding", "Otter", "Husky", "Mr. 17", "Cass"
+ new ButtonItem(){Name = "Ding" },
+ new ButtonItem(){Name = "Otter" },
+ new ButtonItem(){Name = "Husky" },
+ new ButtonItem(){Name = "Mr. 17" },
+ new ButtonItem(){Name = "Cass" },
};
+}
+
+public class ButtonItem
+{
+ public string? Name { get; set; }
+ public ICommand InvokeCommand { get; set; }
+
+ public ButtonItem()
+ {
+ InvokeCommand = new AsyncRelayCommand(Invoke);
+ }
+
+ private async Task Invoke()
+ {
+ await MessageBox.ShowAsync("Hello " + Name);
+ }
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/EnumSelectorDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/EnumSelectorDemoViewModel.cs
new file mode 100644
index 0000000..06be6d4
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/EnumSelectorDemoViewModel.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Windows.Input;
+using Avalonia.Animation;
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Layout;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+
+namespace Ursa.Demo.ViewModels;
+
+public class EnumSelectorDemoViewModel: ObservableObject
+{
+ public ObservableCollection Types { get; set; }
+
+ private Type? _selectedType;
+ public Type? SelectedType
+ {
+ get => _selectedType;
+ set
+ {
+ SetProperty(ref _selectedType, value);
+ Value = null;
+ }
+ }
+
+ private object? _value;
+ public object? Value
+ {
+ get => _value;
+ set => SetProperty(ref _value, value);
+ }
+
+ public EnumSelectorDemoViewModel()
+ {
+ Types = new ObservableCollection()
+ {
+ typeof(HorizontalAlignment),
+ typeof(VerticalAlignment),
+ typeof(Orientation),
+ typeof(Dock),
+ typeof(GridResizeDirection),
+ typeof(DayOfWeek),
+ typeof(FillMode),
+ typeof(IterationType),
+ typeof(BindingMode),
+ typeof(BindingPriority),
+ typeof(StandardCursorType),
+ typeof(Key),
+ typeof(KeyModifiers),
+ typeof(RoutingStrategies),
+ typeof(CustomEnum),
+ };
+ }
+}
+
+public enum CustomEnum
+{
+ [Description("是")]
+ Yes,
+ [Description("否")]
+ No,
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index a4a24ab..50ab100 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -31,6 +31,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyDialog => new DialogDemoViewModel(),
MenuKeys.MenuKeyDivider => new DividerDemoViewModel(),
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
+ MenuKeys.MenuKeyEnumSelector => new EnumSelectorDemoViewModel(),
MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(),
MenuKeys.MenuKeyIconButton => new IconButtonDemoViewModel(),
MenuKeys.MenuKeyIpBox => new IPv4BoxDemoViewModel(),
@@ -40,8 +41,10 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyNavigation => new NavigationMenuDemoViewModel(),
MenuKeys.MenuKeyNumericUpDown => new NumericUpDownDemoViewModel(),
MenuKeys.MenuKeyPagination => new PaginationDemoViewModel(),
+ MenuKeys.MenuKeyRangeSlider => new RangeSliderDemoViewModel(),
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
+ MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
};
}
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MenuItemViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuItemViewModel.cs
index 42be205..279fb52 100644
--- a/demo/Ursa.Demo/ViewModels/MenuItemViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuItemViewModel.cs
@@ -5,11 +5,19 @@ using CommunityToolkit.Mvvm.Messaging;
namespace Ursa.Demo.ViewModels;
+public enum ControlStatus
+{
+ New,
+ Beta,
+ Stable,
+}
+
public class MenuItemViewModel: ViewModelBase
{
public string MenuHeader { get; set; }
public string MenuIconName { get; set; }
public string Key { get; set; }
+ public string Status { get; set; }
public bool IsSeparator { get; set; }
public ObservableCollection Children { get; set; } = new();
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index 0195ee8..3114d4d 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -14,21 +14,24 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "Controls", IsSeparator = true },
new() { MenuHeader = "Badge", Key = MenuKeys.MenuKeyBadge },
new() { MenuHeader = "Banner", Key = MenuKeys.MenuKeyBanner },
- new() { MenuHeader = "ButtonGroup", Key = MenuKeys.MenuKeyButtonGroup },
new() { MenuHeader = "Dialog", Key = MenuKeys.MenuKeyDialog },
+ new() { MenuHeader = "ButtonGroup", Key = MenuKeys.MenuKeyButtonGroup, Status = "Updated"},
new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider },
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
+ new() { MenuHeader = "Enum Selector", Key = MenuKeys.MenuKeyEnumSelector },
new() { MenuHeader = "IconButton", Key = MenuKeys.MenuKeyIconButton },
new() { MenuHeader = "ImageViewer", Key = MenuKeys.MenuKeyImageViewer },
new() { MenuHeader = "IPv4Box", Key = MenuKeys.MenuKeyIpBox },
new() { MenuHeader = "KeyGestureInput", Key = MenuKeys.MenuKeyKeyGestureInput },
new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading },
- new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox },
- new() { MenuHeader = "Navigation", Key = MenuKeys.MenuKeyNavigation },
- new() { MenuHeader = "NumericUpDown", Key = MenuKeys.MenuKeyNumericUpDown },
+ new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox, Status = "New" },
+ new() { MenuHeader = "Navigation", Key = MenuKeys.MenuKeyNavigation, Status = "WIP" },
+ new() { MenuHeader = "NumericUpDown", Key = MenuKeys.MenuKeyNumericUpDown, Status = "New" },
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
+ new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"},
new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput },
- new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline },
+ new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" },
+ new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon, Status = "New"},
};
}
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/RangeSliderDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/RangeSliderDemoViewModel.cs
new file mode 100644
index 0000000..9e8fd67
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/RangeSliderDemoViewModel.cs
@@ -0,0 +1,16 @@
+using System.Collections.ObjectModel;
+using Avalonia.Layout;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class RangeSliderDemoViewModel: ObservableObject
+{
+ public ObservableCollection Orientations { get; set; } = new ObservableCollection()
+ {
+ Orientation.Horizontal,
+ Orientation.Vertical
+ };
+
+ [ObservableProperty] private Orientation _orientation;
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/TwoTonePathIconDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/TwoTonePathIconDemoViewModel.cs
new file mode 100644
index 0000000..d2112a1
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/TwoTonePathIconDemoViewModel.cs
@@ -0,0 +1,8 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public class TwoTonePathIconDemoViewModel:ObservableObject
+{
+
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Views/MainView.axaml b/demo/Ursa.Demo/Views/MainView.axaml
index 2a5bce2..e038f33 100644
--- a/demo/Ursa.Demo/Views/MainView.axaml
+++ b/demo/Ursa.Demo/Views/MainView.axaml
@@ -43,8 +43,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/EnumSelector.axaml b/src/Ursa.Themes.Semi/Controls/EnumSelector.axaml
new file mode 100644
index 0000000..841aea3
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/EnumSelector.axaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/RangeSlider.axaml b/src/Ursa.Themes.Semi/Controls/RangeSlider.axaml
new file mode 100644
index 0000000..7d66251
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/RangeSlider.axaml
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/TwoTonePathIcon.axaml b/src/Ursa.Themes.Semi/Controls/TwoTonePathIcon.axaml
new file mode 100644
index 0000000..1fe4731
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/TwoTonePathIcon.axaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index 03b88dc..f21a847 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -8,6 +8,7 @@
+
@@ -17,7 +18,9 @@
+
+
diff --git a/src/Ursa/Controls/ButtonGroup.cs b/src/Ursa/Controls/ButtonGroup.cs
index 773a1b4..92f997c 100644
--- a/src/Ursa/Controls/ButtonGroup.cs
+++ b/src/Ursa/Controls/ButtonGroup.cs
@@ -3,6 +3,7 @@ using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
+using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Metadata;
@@ -10,6 +11,40 @@ namespace Ursa.Controls;
public class ButtonGroup: ItemsControl
{
+ public static readonly StyledProperty CommandBindingProperty = AvaloniaProperty.Register(
+ nameof(CommandBinding));
+
+ [AssignBinding]
+ [InheritDataTypeFromItems(nameof(ItemsSource))]
+ public IBinding? CommandBinding
+ {
+ get => GetValue(CommandBindingProperty);
+ set => SetValue(CommandBindingProperty, value);
+ }
+
+ public static readonly StyledProperty CommandParameterBindingProperty = AvaloniaProperty.Register(
+ nameof(CommandParameterBinding));
+
+ [AssignBinding]
+ [InheritDataTypeFromItems(nameof(ItemsSource))]
+ public IBinding? CommandParameterBinding
+ {
+ get => GetValue(CommandParameterBindingProperty);
+ set => SetValue(CommandParameterBindingProperty, value);
+ }
+
+ public static readonly StyledProperty ContentBindingProperty = AvaloniaProperty.Register(
+ nameof(ContentBinding));
+
+ [AssignBinding]
+ [InheritDataTypeFromItems(nameof(ItemsSource))]
+ public IBinding? ContentBinding
+ {
+ get => GetValue(ContentBindingProperty);
+ set => SetValue(ContentBindingProperty, value);
+ }
+
+
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
recycleKey = null;
@@ -20,4 +55,28 @@ public class ButtonGroup: ItemsControl
{
return new Button();
}
+
+ protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
+ {
+ base.PrepareContainerForItemOverride(container, item, index);
+ if(container is Button button)
+ {
+ if ( CommandBinding is not null)
+ {
+ button[!Button.CommandProperty] = CommandBinding;
+ }
+ if ( CommandParameterBinding is not null)
+ {
+ button[!Button.CommandParameterProperty] = CommandParameterBinding;
+ }
+ if ( ContentBinding is not null)
+ {
+ button[!Button.ContentProperty] = ContentBinding;
+ }
+ if (ItemTemplate is not null)
+ {
+ button.ContentTemplate = ItemTemplate;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Ursa/Controls/EnumSelector/EnumSelector.cs b/src/Ursa/Controls/EnumSelector/EnumSelector.cs
new file mode 100644
index 0000000..821e6fe
--- /dev/null
+++ b/src/Ursa/Controls/EnumSelector/EnumSelector.cs
@@ -0,0 +1,158 @@
+using System.ComponentModel;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+
+namespace Ursa.Controls;
+
+public class EnumItemTuple
+{
+ public string? DisplayName { get; set; }
+ public object? Value { get; set; }
+}
+
+public class EnumSelector: TemplatedControl
+{
+ public static readonly StyledProperty EnumTypeProperty = AvaloniaProperty.Register(
+ nameof(EnumType), validate: OnTypeValidate);
+
+ public Type? EnumType
+ {
+ get => GetValue(EnumTypeProperty);
+ set => SetValue(EnumTypeProperty, value);
+ }
+
+ private static bool OnTypeValidate(Type? arg)
+ {
+ if (arg is null) return true;
+ return arg.IsEnum;
+ }
+
+ public static readonly StyledProperty