diff --git a/demo/Directory.Build.props b/demo/Directory.Build.props
index ae8bdf5..c950d6a 100644
--- a/demo/Directory.Build.props
+++ b/demo/Directory.Build.props
@@ -1,6 +1,6 @@
enable
- 11.0.0
+ 11.0.9
diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs
index f6a52e3..4c707a6 100644
--- a/demo/Ursa.Demo/Models/MenuKeys.cs
+++ b/demo/Ursa.Demo/Models/MenuKeys.cs
@@ -13,6 +13,7 @@ public static class MenuKeys
public const string MenuKeyDrawer = "Drawer";
public const string MenuKeyDualBadge = "DualBadge";
public const string MenuKeyEnumSelector = "EnumSelector";
+ public const string MenuKeyForm = "Form";
public const string MenuKeyImageViewer = "ImageViewer";
public const string MenuKeyIpBox = "IPv4Box";
public const string MenuKeyIconButton = "IconButton";
diff --git a/demo/Ursa.Demo/Pages/FormDemo.axaml b/demo/Ursa.Demo/Pages/FormDemo.axaml
new file mode 100644
index 0000000..288392a
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/FormDemo.axaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/FormDemo.axaml.cs b/demo/Ursa.Demo/Pages/FormDemo.axaml.cs
new file mode 100644
index 0000000..64a5f0c
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/FormDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class FormDemo : UserControl
+{
+ public FormDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Ursa.Demo.csproj b/demo/Ursa.Demo/Ursa.Demo.csproj
index d11b6e7..94a0b01 100644
--- a/demo/Ursa.Demo/Ursa.Demo.csproj
+++ b/demo/Ursa.Demo/Ursa.Demo.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/demo/Ursa.Demo/ViewModels/FormDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/FormDemoViewModel.cs
new file mode 100644
index 0000000..5f706a0
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/FormDemoViewModel.cs
@@ -0,0 +1,48 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class FormDemoViewModel: ObservableObject
+{
+ [ObservableProperty] private DataModel _model;
+
+ public FormDemoViewModel()
+ {
+ Model = new DataModel();
+ }
+}
+
+public partial class DataModel : ObservableObject
+{
+ private string _name;
+
+ [MinLength(10)]
+ public string Name
+ {
+ get=>_name;
+ set => SetProperty(ref _name, value);
+ }
+
+ private string _email;
+
+ [EmailAddress]
+ public string Email
+ {
+ get=>_email;
+ set => SetProperty(ref _email, value);
+ }
+
+ private DateTime _date;
+ public DateTime Date
+ {
+ get => _date;
+ set => SetProperty(ref _date, value);
+ }
+
+ public DataModel()
+ {
+ Date = DateTime.Today;
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index 699c59d..e5a996d 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -35,6 +35,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyDrawer => new DrawerDemoViewModel(),
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
MenuKeys.MenuKeyEnumSelector => new EnumSelectorDemoViewModel(),
+ MenuKeys.MenuKeyForm => new FormDemoViewModel(),
MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(),
MenuKeys.MenuKeyIconButton => new IconButtonDemoViewModel(),
MenuKeys.MenuKeyIpBox => new IPv4BoxDemoViewModel(),
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index 429f98b..54e6f8b 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -22,6 +22,7 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "Drawer", Key = MenuKeys.MenuKeyDrawer },
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
new() { MenuHeader = "Enum Selector", Key = MenuKeys.MenuKeyEnumSelector },
+ new() { MenuHeader = "Form", Key = MenuKeys.MenuKeyForm },
new() { MenuHeader = "Icon Button", Key = MenuKeys.MenuKeyIconButton },
new() { MenuHeader = "ImageViewer", Key = MenuKeys.MenuKeyImageViewer },
new() { MenuHeader = "IPv4Box", Key = MenuKeys.MenuKeyIpBox },
diff --git a/src/Ursa.Themes.Semi/Controls/Form.axaml b/src/Ursa.Themes.Semi/Controls/Form.axaml
new file mode 100644
index 0000000..039fa0e
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/Form.axaml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index 04e03e5..848b1b9 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -12,6 +12,7 @@
+
diff --git a/src/Ursa/Controls/Form/Form.cs b/src/Ursa/Controls/Form/Form.cs
new file mode 100644
index 0000000..8110b99
--- /dev/null
+++ b/src/Ursa/Controls/Form/Form.cs
@@ -0,0 +1,76 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Layout;
+using Ursa.Common;
+
+namespace Ursa.Controls;
+
+[PseudoClasses(PC_FixedWidth)]
+public class Form: ItemsControl
+{
+ public const string PC_FixedWidth = ":fixed-width";
+
+ public static readonly StyledProperty LabelWidthProperty = AvaloniaProperty.Register