From 51f5d4a060d6133f0e42d2e2cb1b717f1473d2c1 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 20:23:07 +0300 Subject: [PATCH 1/9] Add initial AutoFormView --- ...oFormViewMaterialConfigurationExtension.cs | 135 +++++++++ .../UraniumUIMaterialMauiProgramExtensions.cs | 5 +- src/UraniumUI/Controls/AutoFormView.cs | 269 ++++++++++++++++++ .../Extensions/NullableExtensions.cs | 35 +++ src/UraniumUI/Options/AutoFormViewOptions.cs | 15 + 5 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs create mode 100644 src/UraniumUI/Controls/AutoFormView.cs create mode 100644 src/UraniumUI/Extensions/NullableExtensions.cs create mode 100644 src/UraniumUI/Options/AutoFormViewOptions.cs diff --git a/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs b/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs new file mode 100644 index 00000000..14de4a43 --- /dev/null +++ b/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs @@ -0,0 +1,135 @@ +using InputKit.Shared.Controls; +using System.Reflection; +using UraniumUI.Controls; +using UraniumUI.Extensions; +using UraniumUI.Material.Controls; +using UraniumUI.Options; +using UraniumUI.Resources; + +namespace UraniumUI.Material.Extensions; + +public static class AutoFormViewMaterialConfigurationExtension +{ + public static MauiAppBuilder ConfigureAutoFormViewForMaterial(this MauiAppBuilder builder) + { + builder.Services.Configure(options => + { + options.EditorMapping[typeof(string)] = EditorForString; + options.EditorMapping[typeof(int)] = EditorForNumeric; + options.EditorMapping[typeof(double)] = EditorForNumeric; + options.EditorMapping[typeof(float)] = EditorForNumeric; + options.EditorMapping[typeof(bool)] = EditorForBoolean; + options.EditorMapping[typeof(Keyboard)] = EditorForKeyboard; + options.EditorMapping[typeof(Enum)] = EditorForEnum; + options.EditorMapping[typeof(DateTime)] = EditorForDateTime; + options.EditorMapping[typeof(TimeSpan)] = EditorForTimeSpan; + }); + + return builder; + } + + public static View EditorForString(PropertyInfo property, object source) + { + var editor = new TextField(); + editor.SetBinding(TextField.TextProperty, new Binding(property.Name, source: source)); + editor.AllowClear = true; + editor.Title = property.Name; + + return editor; + } + + public static View EditorForNumeric(PropertyInfo property, object source) + { + var editor = new TextField(); + editor.SetBinding(TextField.TextProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + editor.AllowClear = false; + editor.Keyboard = Keyboard.Numeric; + + return editor; + } + + public static View EditorForBoolean(PropertyInfo property, object source) + { + var editor = new UraniumUI.Material.Controls.CheckBox(); + editor.SetBinding(UraniumUI.Material.Controls.CheckBox.IsCheckedProperty, new Binding(property.Name, source: source)); + editor.Text = property.Name; + + return editor; + } + + public static View EditorForEnum(PropertyInfo property, object source) + { + var editor = new PickerField(); + + var values = Enum.GetValues(property.PropertyType.AsNonNullable()); + if (values.Length <= 5) + { + return CreateSelectionViewForValues(values, property, source); + } + + editor.ItemsSource = values; + editor.SetBinding(PickerField.SelectedItemProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + editor.AllowClear = false; + return editor; + } + + private static View CreateSelectionViewForValues(Array values, PropertyInfo property, object source) + { + var shouldUseSingleColumn = values.Length > 3; + var editor = new SelectionView + { + Color = ColorResource.GetColor("Primary", "PrimaryDark"), + ColumnSpacing = -2, + RowSpacing = shouldUseSingleColumn ? 5 : -2, + SelectionType = shouldUseSingleColumn ? InputKit.Shared.SelectionType.RadioButton : InputKit.Shared.SelectionType.Button, + ColumnNumber = shouldUseSingleColumn ? 1 : values.Length, + ItemsSource = values + }; + + editor.SetBinding(SelectionView.SelectedItemProperty, new Binding(property.Name, source: source)); + + return new VerticalStackLayout + { + Spacing = 6, + Children = { + new Label { Text = property.Name }, + editor + } + }; + } + + public static View EditorForKeyboard(PropertyInfo property, object source) + { + var editor = new PickerField(); + + editor.ItemsSource = typeof(Keyboard) + .GetProperties(BindingFlags.Public | BindingFlags.Static) + .Select(x => x.GetValue(null)) + .ToArray(); + + editor.SetBinding(PickerField.SelectedItemProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + editor.AllowClear = false; + return editor; + } + + public static View EditorForDateTime(PropertyInfo property, object source) + { + var editor = new DatePickerField(); + editor.SetBinding(DatePickerField.DateProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + editor.AllowClear = false; + return editor; + } + + public static View EditorForTimeSpan(PropertyInfo property, object source) + { + var editor = new TimePickerField(); + editor.SetBinding(TimePickerField.TimeProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + editor.AllowClear = false; + return editor; + } +} diff --git a/src/UraniumUI.Material/UraniumUIMaterialMauiProgramExtensions.cs b/src/UraniumUI.Material/UraniumUIMaterialMauiProgramExtensions.cs index c498d317..b66f744a 100644 --- a/src/UraniumUI.Material/UraniumUIMaterialMauiProgramExtensions.cs +++ b/src/UraniumUI.Material/UraniumUIMaterialMauiProgramExtensions.cs @@ -1,4 +1,5 @@ using UraniumUI.Material.Controls; +using UraniumUI.Material.Extensions; using UraniumUI.Material.Handlers; namespace UraniumUI; @@ -10,7 +11,9 @@ public static MauiAppBuilder UseUraniumUIMaterial(this MauiAppBuilder builder) { handlers.AddHandler(typeof(ButtonView), typeof(ButtonViewHandler)); }); - + + builder.ConfigureAutoFormViewForMaterial(); + return builder; } } diff --git a/src/UraniumUI/Controls/AutoFormView.cs b/src/UraniumUI/Controls/AutoFormView.cs new file mode 100644 index 00000000..051e3297 --- /dev/null +++ b/src/UraniumUI/Controls/AutoFormView.cs @@ -0,0 +1,269 @@ +using InputKit.Shared.Controls; +using System.Reflection; +using UraniumUI.Extensions; +using UraniumUI.Resources; +using UraniumUI.Options; +using Microsoft.Extensions.Options; + +namespace UraniumUI.Controls; +public class AutoFormView : FormView +{ + public object Source { get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); } + public static readonly BindableProperty SourceProperty = BindableProperty.Create( + nameof(Source), + typeof(object), + typeof(AutoFormView), + propertyChanged: (bindable, oldValue, newValue) => (bindable as AutoFormView).OnSourceChanged()); + + public PropertyInfo[] EditingProperties { get; protected set; } + + private ContentView _footerView = new ContentView(); + public View FooterView + { + get => _footerView.Content; set + { + _footerView.Content = value; + if (_propertiesContainer.Children.Count > 0 && !_propertiesContainer.Contains(_footerView)) + { + _propertiesContainer.Children.Add(_footerView); + } + } + } + + public bool ShowMissingProperties { get; set; } = true; + + public bool Hierarchical { get; set; } = false; + + public Type HierarchyLimitType { get; set; } = typeof(object); + + private Layout _propertiesContainer = new VerticalStackLayout + { + Spacing = 20, + Padding = 10, + }; + + public Layout ItemsLayout + { + get => _propertiesContainer; + set + { + Children.Remove(_propertiesContainer); + while (_propertiesContainer.Children.Count > 0) + { + var item = _propertiesContainer.Children.First(); + _propertiesContainer.Children.Remove(item); + value.Children.Add(item); + } + _propertiesContainer = value; + Children.Add(_propertiesContainer); + } + } + + protected Dictionary EditorMapping { get; } + + public AutoFormView() + { + Children.Add(_propertiesContainer); + + var options = UraniumServiceProvider.Current.GetRequiredService>(); + EditorMapping = options.Value.EditorMapping; + } + + protected void OnSourceChanged() + { + var flags = BindingFlags.Public | BindingFlags.Instance; + if (Hierarchical) + { + flags |= BindingFlags.FlattenHierarchy; + } + var props = Source.GetType() + .GetProperties(flags) + .Where(x => HierarchyLimitType.IsAssignableFrom(x.PropertyType)) + .ToArray(); + + EditingProperties = props; + + Render(); + } + + private void Render() + { + if (Source is null) + { + _propertiesContainer.Children.Clear(); + return; + } + + foreach (var property in EditingProperties) + { + var createEditor = EditorMapping.FirstOrDefault(x => x.Key.IsAssignableFrom(property.PropertyType.AsNonNullable())).Value; + if (createEditor != null) + { + _propertiesContainer.Children.Add(createEditor(property, Source)); + } + else if (ShowMissingProperties) + { + _propertiesContainer.Children.Add(new Label + { + Text = $"No editor for {property.Name} ({property.PropertyType})", + FontAttributes = FontAttributes.Italic + }); + } + } + + if (!_propertiesContainer.Children.Contains(_footerView)) + { + _propertiesContainer.Children.Add(_footerView); + } + + if (_footerView.Content == null) + { + _footerView.Content = GetFooter(); + } + } + + protected virtual View GetFooter() + { + var submitButton = new Button + { + Text = "Submit", + StyleClass = new List { "FilledButton" }, + }; + + var resetButton = new Button + { + Text = "Reset", + StyleClass = new List { "OutlinedButton" }, + }; + + FormView.SetIsSubmitButton(submitButton, true); + FormView.SetIsResetButton(resetButton, true); + return new StackLayout + { + Margin = new Thickness(10), + Spacing = 12, + Children = { submitButton, resetButton } + }; + } + + public static View EditorForString(PropertyInfo property, object source) + { + var editor = new Entry(); + editor.SetBinding(Entry.TextProperty, new Binding(property.Name, source: source)); + + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } + + public static View EditorForNumeric(PropertyInfo property, object source) + { + var editor = new Entry(); + editor.SetBinding(Entry.TextProperty, new Binding(property.Name, source: source)); + editor.Keyboard = Keyboard.Numeric; + + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } + + public static View EditorForBoolean(PropertyInfo property, object source) + { + var editor = new InputKit.Shared.Controls.CheckBox(); + editor.SetBinding(InputKit.Shared.Controls.CheckBox.IsCheckedProperty, new Binding(property.Name, source: source)); + editor.Text = property.Name; + + return editor; + } + + public static View EditorForEnum(PropertyInfo property, object source) + { + var editor = new Picker(); + + var values = Enum.GetValues(property.PropertyType.AsNonNullable()); + if (values.Length <= 5) + { + return CreateSelectionViewForValues(values, property, source); + } + + editor.ItemsSource = values; + editor.SetBinding(Picker.SelectedItemProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } + + private static View CreateSelectionViewForValues(Array values, PropertyInfo property, object source) + { + var shouldUseSingleColumn = values.Length > 3; + var editor = new SelectionView + { + Color = ColorResource.GetColor("Primary", "PrimaryDark"), + ColumnSpacing = -2, + RowSpacing = shouldUseSingleColumn ? 5 : -2, + SelectionType = shouldUseSingleColumn ? InputKit.Shared.SelectionType.RadioButton : InputKit.Shared.SelectionType.Button, + ColumnNumber = shouldUseSingleColumn ? 1 : values.Length, + ItemsSource = values + }; + + editor.SetBinding(SelectionView.SelectedItemProperty, new Binding(property.Name, source: source)); + + return new VerticalStackLayout + { + Spacing = 6, + Children = { + new Label { Text = property.Name }, + editor + } + }; + } + + public static View EditorForKeyboard(PropertyInfo property, object source) + { + var editor = new Picker(); + + editor.ItemsSource = typeof(Keyboard) + .GetProperties(BindingFlags.Public | BindingFlags.Static) + .Select(x => x.GetValue(null)) + .ToArray(); + + editor.SetBinding(Picker.SelectedItemProperty, new Binding(property.Name, source: source)); + editor.Title = property.Name; + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } + + private static View EditorForDateTime(PropertyInfo property, object source) + { + var editor = new DatePicker(); + editor.SetBinding(DatePicker.DateProperty, new Binding(property.Name, source: source)); + + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } + + private static View EditorForTimeSpan(PropertyInfo property, object source) + { + var editor = new TimePicker(); + editor.SetBinding(TimePicker.TimeProperty, new Binding(property.Name, source: source)); + + return new VerticalStackLayout + { + new Label { Text = property.Name }, + editor + }; + } +} diff --git a/src/UraniumUI/Extensions/NullableExtensions.cs b/src/UraniumUI/Extensions/NullableExtensions.cs new file mode 100644 index 00000000..5ef9a274 --- /dev/null +++ b/src/UraniumUI/Extensions/NullableExtensions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UraniumUI.Extensions; +public static class NullableExtensions +{ + public static bool IsNullable(this Type type) + { + if (type is null) + { + return false; + } + + return type.Name == typeof(Nullable<>).Name; + } + + public static T? MakeNullable(this T value) + where T : struct + { + var genericNullableType = typeof(Nullable<>).MakeGenericType(typeof(T)); + return Activator.CreateInstance(genericNullableType, args: new object[] { value }) as T?; + } + + public static Type AsNonNullable(this Type type) + { + if (type.IsNullable()) + { + return type.GetGenericArguments()[0]; + } + return type; + } +} diff --git a/src/UraniumUI/Options/AutoFormViewOptions.cs b/src/UraniumUI/Options/AutoFormViewOptions.cs new file mode 100644 index 00000000..e3f71b7a --- /dev/null +++ b/src/UraniumUI/Options/AutoFormViewOptions.cs @@ -0,0 +1,15 @@ +using InputKit.Shared.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace UraniumUI.Options; +public class AutoFormViewOptions +{ + public Dictionary EditorMapping { get; } = new(); + + public delegate View EditorForType(PropertyInfo property, object source); +} From 7beff439a577919b9566ec71d306475b23dc0aab Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 20:29:53 +0300 Subject: [PATCH 2/9] Configure default AutoFormView options --- src/UraniumUI/Controls/AutoFormView.cs | 45 ++++++++++--------- src/UraniumUI/MauiProgramExtensions.cs | 4 ++ src/UraniumUI/Options/AutoFormViewOptions.cs | 8 +--- .../Options/AutoFormViewOptionsExtensions.cs | 23 ++++++++++ 4 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 src/UraniumUI/Options/AutoFormViewOptionsExtensions.cs diff --git a/src/UraniumUI/Controls/AutoFormView.cs b/src/UraniumUI/Controls/AutoFormView.cs index 051e3297..9947e430 100644 --- a/src/UraniumUI/Controls/AutoFormView.cs +++ b/src/UraniumUI/Controls/AutoFormView.cs @@ -23,9 +23,9 @@ public View FooterView get => _footerView.Content; set { _footerView.Content = value; - if (_propertiesContainer.Children.Count > 0 && !_propertiesContainer.Contains(_footerView)) + if (_itemsLayout.Children.Count > 0 && !_itemsLayout.Contains(_footerView)) { - _propertiesContainer.Children.Add(_footerView); + _itemsLayout.Children.Add(_footerView); } } } @@ -36,7 +36,7 @@ public View FooterView public Type HierarchyLimitType { get; set; } = typeof(object); - private Layout _propertiesContainer = new VerticalStackLayout + private Layout _itemsLayout = new VerticalStackLayout { Spacing = 20, Padding = 10, @@ -44,29 +44,30 @@ public View FooterView public Layout ItemsLayout { - get => _propertiesContainer; + get => _itemsLayout; set { - Children.Remove(_propertiesContainer); - while (_propertiesContainer.Children.Count > 0) + Children.Remove(_itemsLayout); + while (_itemsLayout.Children.Count > 0) { - var item = _propertiesContainer.Children.First(); - _propertiesContainer.Children.Remove(item); + var item = _itemsLayout.Children.First(); + _itemsLayout.Children.Remove(item); value.Children.Add(item); } - _propertiesContainer = value; - Children.Add(_propertiesContainer); + _itemsLayout = value; + Children.Add(_itemsLayout); } } protected Dictionary EditorMapping { get; } + protected AutoFormViewOptions Options { get; } public AutoFormView() { - Children.Add(_propertiesContainer); + Children.Add(_itemsLayout); - var options = UraniumServiceProvider.Current.GetRequiredService>(); - EditorMapping = options.Value.EditorMapping; + Options = UraniumServiceProvider.Current.GetRequiredService>().Value; + EditorMapping = Options.EditorMapping; } protected void OnSourceChanged() @@ -90,7 +91,7 @@ private void Render() { if (Source is null) { - _propertiesContainer.Children.Clear(); + _itemsLayout.Children.Clear(); return; } @@ -99,11 +100,11 @@ private void Render() var createEditor = EditorMapping.FirstOrDefault(x => x.Key.IsAssignableFrom(property.PropertyType.AsNonNullable())).Value; if (createEditor != null) { - _propertiesContainer.Children.Add(createEditor(property, Source)); + _itemsLayout.Children.Add(createEditor(property, Source)); } else if (ShowMissingProperties) { - _propertiesContainer.Children.Add(new Label + _itemsLayout.Children.Add(new Label { Text = $"No editor for {property.Name} ({property.PropertyType})", FontAttributes = FontAttributes.Italic @@ -111,9 +112,9 @@ private void Render() } } - if (!_propertiesContainer.Children.Contains(_footerView)) + if (!_itemsLayout.Children.Contains(_footerView)) { - _propertiesContainer.Children.Add(_footerView); + _itemsLayout.Children.Add(_footerView); } if (_footerView.Content == null) @@ -150,7 +151,7 @@ public static View EditorForString(PropertyInfo property, object source) { var editor = new Entry(); editor.SetBinding(Entry.TextProperty, new Binding(property.Name, source: source)); - + return new VerticalStackLayout { new Label { Text = property.Name }, @@ -200,7 +201,7 @@ public static View EditorForEnum(PropertyInfo property, object source) }; } - private static View CreateSelectionViewForValues(Array values, PropertyInfo property, object source) + public static View CreateSelectionViewForValues(Array values, PropertyInfo property, object source) { var shouldUseSingleColumn = values.Length > 3; var editor = new SelectionView @@ -243,7 +244,7 @@ public static View EditorForKeyboard(PropertyInfo property, object source) }; } - private static View EditorForDateTime(PropertyInfo property, object source) + public static View EditorForDateTime(PropertyInfo property, object source) { var editor = new DatePicker(); editor.SetBinding(DatePicker.DateProperty, new Binding(property.Name, source: source)); @@ -255,7 +256,7 @@ private static View EditorForDateTime(PropertyInfo property, object source) }; } - private static View EditorForTimeSpan(PropertyInfo property, object source) + public static View EditorForTimeSpan(PropertyInfo property, object source) { var editor = new TimePicker(); editor.SetBinding(TimePicker.TimeProperty, new Binding(property.Name, source: source)); diff --git a/src/UraniumUI/MauiProgramExtensions.cs b/src/UraniumUI/MauiProgramExtensions.cs index b3108fd2..c394e1b5 100644 --- a/src/UraniumUI/MauiProgramExtensions.cs +++ b/src/UraniumUI/MauiProgramExtensions.cs @@ -3,6 +3,7 @@ using UraniumUI.Controls; using UraniumUI.Dialogs; using UraniumUI.Handlers; +using UraniumUI.Options; using UraniumUI.Views; namespace UraniumUI; @@ -21,6 +22,9 @@ public static MauiAppBuilder UseUraniumUI(this MauiAppBuilder builder) }); builder.Services.AddTransient(); + + builder.ConfigureAutoFormViewDefaults(); + return builder; } diff --git a/src/UraniumUI/Options/AutoFormViewOptions.cs b/src/UraniumUI/Options/AutoFormViewOptions.cs index e3f71b7a..a9904d83 100644 --- a/src/UraniumUI/Options/AutoFormViewOptions.cs +++ b/src/UraniumUI/Options/AutoFormViewOptions.cs @@ -1,10 +1,4 @@ -using InputKit.Shared.Controls; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; namespace UraniumUI.Options; public class AutoFormViewOptions diff --git a/src/UraniumUI/Options/AutoFormViewOptionsExtensions.cs b/src/UraniumUI/Options/AutoFormViewOptionsExtensions.cs new file mode 100644 index 00000000..c021795b --- /dev/null +++ b/src/UraniumUI/Options/AutoFormViewOptionsExtensions.cs @@ -0,0 +1,23 @@ +using UraniumUI.Controls; + +namespace UraniumUI.Options; +public static class AutoFormViewOptionsExtensions +{ + public static MauiAppBuilder ConfigureAutoFormViewDefaults(this MauiAppBuilder builder) + { + builder.Services.Configure(options => + { + options.EditorMapping[typeof(string)] = AutoFormView.EditorForString; + options.EditorMapping[typeof(int)] = AutoFormView.EditorForNumeric; + options.EditorMapping[typeof(double)] = AutoFormView.EditorForNumeric; + options.EditorMapping[typeof(float)] = AutoFormView.EditorForNumeric; + options.EditorMapping[typeof(bool)] = AutoFormView.EditorForBoolean; + options.EditorMapping[typeof(Keyboard)] = AutoFormView.EditorForKeyboard; + options.EditorMapping[typeof(Enum)] = AutoFormView.EditorForEnum; + options.EditorMapping[typeof(DateTime)] = AutoFormView.EditorForDateTime; + options.EditorMapping[typeof(TimeSpan)] = AutoFormView.EditorForTimeSpan; + }); + + return builder; + } +} From 440748085c94aeaf4f24295a9715025de33fe034 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 20:30:10 +0300 Subject: [PATCH 3/9] Fix material autoform extensions --- ...ension.cs => AutoFormViewMaterialConfigurationExtensions.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/UraniumUI.Material/Extensions/{AutoFormViewMaterialConfigurationExtension.cs => AutoFormViewMaterialConfigurationExtensions.cs} (98%) diff --git a/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs b/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtensions.cs similarity index 98% rename from src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs rename to src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtensions.cs index 14de4a43..a427ce6d 100644 --- a/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtension.cs +++ b/src/UraniumUI.Material/Extensions/AutoFormViewMaterialConfigurationExtensions.cs @@ -8,7 +8,7 @@ namespace UraniumUI.Material.Extensions; -public static class AutoFormViewMaterialConfigurationExtension +public static class AutoFormViewMaterialConfigurationExtensions { public static MauiAppBuilder ConfigureAutoFormViewForMaterial(this MauiAppBuilder builder) { From 27331601025d4b0f35f1da8a9c88a39c0dda96f3 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 21:17:36 +0300 Subject: [PATCH 4/9] Add AutoFormViewPage --- demo/UraniumApp/AppShell.xaml | 4 +++ demo/UraniumApp/Pages/AutoFormViewPage.xaml | 15 ++++++++++ .../UraniumApp/Pages/AutoFormViewPage.xaml.cs | 9 ++++++ .../ViewModels/AutoFormViewPageViewModel.cs | 30 +++++++++++++++++++ src/UraniumUI/Options/AutoFormViewOptions.cs | 23 ++++++++++++-- 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 demo/UraniumApp/Pages/AutoFormViewPage.xaml create mode 100644 demo/UraniumApp/Pages/AutoFormViewPage.xaml.cs create mode 100644 demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs diff --git a/demo/UraniumApp/AppShell.xaml b/demo/UraniumApp/AppShell.xaml index 247e3496..c75bc64b 100644 --- a/demo/UraniumApp/AppShell.xaml +++ b/demo/UraniumApp/AppShell.xaml @@ -144,6 +144,10 @@ + + + + diff --git a/demo/UraniumApp/Pages/AutoFormViewPage.xaml b/demo/UraniumApp/Pages/AutoFormViewPage.xaml new file mode 100644 index 00000000..00f4c640 --- /dev/null +++ b/demo/UraniumApp/Pages/AutoFormViewPage.xaml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/demo/UraniumApp/Pages/AutoFormViewPage.xaml.cs b/demo/UraniumApp/Pages/AutoFormViewPage.xaml.cs new file mode 100644 index 00000000..fb7d4fc6 --- /dev/null +++ b/demo/UraniumApp/Pages/AutoFormViewPage.xaml.cs @@ -0,0 +1,9 @@ +namespace UraniumApp.Pages; + +public partial class AutoFormViewPage : ContentPage +{ + public AutoFormViewPage() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs b/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs new file mode 100644 index 00000000..a402eba4 --- /dev/null +++ b/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs @@ -0,0 +1,30 @@ +using DotNurse.Injector.Attributes; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.ComponentModel.DataAnnotations; + +namespace UraniumApp.ViewModels; + +[RegisterAs(typeof(AutoFormViewPageViewModel))] +public class AutoFormViewPageViewModel : ReactiveObject +{ + [EmailAddress] + [Required] + [MinLength(5)] + [Reactive] + public string Email { get; set; } + [Reactive] + public string FullName { get; set; } + [Reactive] + public Gender Gender { get; set; } + [Reactive] + public DateTime? BirthDate { get; set; } + [Reactive] + public TimeSpan? MeetingTime { get; set; } + [Reactive] + public int? NumberOfSeats { get; set; } + + [Required] + [Reactive] + public bool IsTermsAndConditionsAccepted { get; set; } +} diff --git a/src/UraniumUI/Options/AutoFormViewOptions.cs b/src/UraniumUI/Options/AutoFormViewOptions.cs index a9904d83..13bdb8f9 100644 --- a/src/UraniumUI/Options/AutoFormViewOptions.cs +++ b/src/UraniumUI/Options/AutoFormViewOptions.cs @@ -1,9 +1,28 @@ -using System.Reflection; +using InputKit.Shared.Validations; +using System.ComponentModel.DataAnnotations; +using System.Reflection; namespace UraniumUI.Options; -public class AutoFormViewOptions +public sealed class AutoFormViewOptions { public Dictionary EditorMapping { get; } = new(); public delegate View EditorForType(PropertyInfo property, object source); + + public List> PostEditorActions { get; } = new(); + + public Func PropertyNameFactory { get; set; } = DefaultPropertyNameFactory; + + public Func> ValidationFactory { get; set; } + + private static string DefaultPropertyNameFactory(PropertyInfo property) + { + var attribute = property.GetCustomAttribute(); + if (attribute != null) + { + return attribute.Name; + } + + return property.Name; + } } From b6d0506dff8f97146826761108410319abfc5cd8 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 21:18:24 +0300 Subject: [PATCH 5/9] Add DataAnnotations support --- demo/UraniumApp/MauiProgram.cs | 7 +++++ .../Validations/DataAnotationValidation.cs | 13 +++++++++- src/UraniumUI/Controls/AutoFormView.cs | 26 +++++++++++++++---- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/demo/UraniumApp/MauiProgram.cs b/demo/UraniumApp/MauiProgram.cs index 32751b82..ca51b176 100644 --- a/demo/UraniumApp/MauiProgram.cs +++ b/demo/UraniumApp/MauiProgram.cs @@ -6,6 +6,8 @@ using System.Reactive; using UraniumUI; using UraniumUI.Dialogs; +using UraniumUI.Options; +using UraniumUI.Validations; namespace UraniumApp; @@ -31,6 +33,11 @@ public static MauiApp CreateMauiApp() fonts.AddFluentIconFonts(); }); + builder.Services.Configure(options => + { + options.ValidationFactory = DataAnnotationValidation.CreateValidations; + }); + RxApp.DefaultExceptionHandler = new AnonymousObserver(ex => { App.Current.MainPage.DisplayAlert("Error", ex.Message, "OK"); diff --git a/src/UraniumUI.Validations.DataAnnotations/Validations/DataAnotationValidation.cs b/src/UraniumUI.Validations.DataAnnotations/Validations/DataAnotationValidation.cs index af04b4ac..4b06adc1 100644 --- a/src/UraniumUI.Validations.DataAnnotations/Validations/DataAnotationValidation.cs +++ b/src/UraniumUI.Validations.DataAnnotations/Validations/DataAnotationValidation.cs @@ -1,5 +1,6 @@ using InputKit.Shared.Validations; using System.ComponentModel.DataAnnotations; +using System.Reflection; namespace UraniumUI.Validations; public class DataAnnotationValidation : IValidation @@ -7,7 +8,7 @@ public class DataAnnotationValidation : IValidation public string Message { get; protected set; } public ValidationAttribute Attribute { get; } - + public string PropertyName { get; } public DataAnnotationValidation(ValidationAttribute attribute, string propertyName) @@ -30,4 +31,14 @@ public bool Validate(object value) return false; } } + + public static IEnumerable CreateValidations(PropertyInfo property) + { + var attributes = property.GetCustomAttributes(); + + foreach (var attribute in attributes) + { + yield return new DataAnnotationValidation(attribute, property.Name); + } + } } \ No newline at end of file diff --git a/src/UraniumUI/Controls/AutoFormView.cs b/src/UraniumUI/Controls/AutoFormView.cs index 9947e430..47fbdf65 100644 --- a/src/UraniumUI/Controls/AutoFormView.cs +++ b/src/UraniumUI/Controls/AutoFormView.cs @@ -4,6 +4,7 @@ using UraniumUI.Resources; using UraniumUI.Options; using Microsoft.Extensions.Options; +using InputKit.Shared.Abstraction; namespace UraniumUI.Controls; public class AutoFormView : FormView @@ -52,10 +53,13 @@ public Layout ItemsLayout { var item = _itemsLayout.Children.First(); _itemsLayout.Children.Remove(item); - value.Children.Add(item); + value?.Children.Add(item); } _itemsLayout = value; - Children.Add(_itemsLayout); + if (_itemsLayout != null) + { + Children.Add(_itemsLayout); + } } } @@ -100,7 +104,19 @@ private void Render() var createEditor = EditorMapping.FirstOrDefault(x => x.Key.IsAssignableFrom(property.PropertyType.AsNonNullable())).Value; if (createEditor != null) { - _itemsLayout.Children.Add(createEditor(property, Source)); + var editor = createEditor(property, Source); + + foreach (var action in Options.PostEditorActions) + { + action(editor, property); + } + + if (editor is IValidatable validatable && Options.ValidationFactory != null) + { + validatable.Validations.AddRange(Options.ValidationFactory(property)); + } + + _itemsLayout.Children.Add(editor); } else if (ShowMissingProperties) { @@ -111,7 +127,7 @@ private void Render() }); } } - + if (!_itemsLayout.Children.Contains(_footerView)) { _itemsLayout.Children.Add(_footerView); @@ -151,7 +167,7 @@ public static View EditorForString(PropertyInfo property, object source) { var editor = new Entry(); editor.SetBinding(Entry.TextProperty, new Binding(property.Name, source: source)); - + return new VerticalStackLayout { new Label { Text = property.Name }, From ddef5483e0ffcf31c90ed08af1497be1488e1551 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 21:57:19 +0300 Subject: [PATCH 6/9] Add initial documentation for AutoFormView --- .../ViewModels/AutoFormViewPageViewModel.cs | 21 +-- docs/en/infrastructure/AutoFormView.md | 122 ++++++++++++++++++ .../images/autoformview-example-dark.png | Bin 0 -> 21322 bytes .../autoformview-itemslayout-grid-dark.png | Bin 0 -> 14424 bytes ...utoformview-showmissingproperties-dark.png | Bin 0 -> 40496 bytes 5 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 docs/en/infrastructure/AutoFormView.md create mode 100644 docs/en/infrastructure/images/autoformview-example-dark.png create mode 100644 docs/en/infrastructure/images/autoformview-itemslayout-grid-dark.png create mode 100644 docs/en/infrastructure/images/autoformview-showmissingproperties-dark.png diff --git a/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs b/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs index a402eba4..ed253b37 100644 --- a/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs +++ b/demo/UraniumApp/ViewModels/AutoFormViewPageViewModel.cs @@ -11,20 +11,13 @@ public class AutoFormViewPageViewModel : ReactiveObject [EmailAddress] [Required] [MinLength(5)] - [Reactive] - public string Email { get; set; } - [Reactive] - public string FullName { get; set; } - [Reactive] - public Gender Gender { get; set; } - [Reactive] - public DateTime? BirthDate { get; set; } - [Reactive] - public TimeSpan? MeetingTime { get; set; } - [Reactive] - public int? NumberOfSeats { get; set; } + [Reactive] public string Email { get; set; } + [Reactive] public string FullName { get; set; } + [Reactive] public Gender Gender { get; set; } + [Reactive] public DateTime? BirthDate { get; set; } + [Reactive] public TimeSpan? MeetingTime { get; set; } + [Reactive] public int? NumberOfSeats { get; set; } [Required] - [Reactive] - public bool IsTermsAndConditionsAccepted { get; set; } + [Reactive] public bool IsTermsAndConditionsAccepted { get; set; } } diff --git a/docs/en/infrastructure/AutoFormView.md b/docs/en/infrastructure/AutoFormView.md new file mode 100644 index 00000000..38941c6f --- /dev/null +++ b/docs/en/infrastructure/AutoFormView.md @@ -0,0 +1,122 @@ +# AutoFormView + +The `AutoFormView` is a view that automatically generates a form based on the properties of a model. It is a subclass of `FormView` and uses the same APIs. + +## Usage + +`AutoFormView` is defined in `UraniumUI.Controls` namespace. You can use it in XAML like this: + +```xml +xmlns:uranium="http://schemas.enisn-projects.io/dotnet/maui/uraniumui" +``` + +Then you can use it like this: + +```xml + +``` + +### Example + +```csharp +public class AutoFormViewPageViewModel : ViewModelBase +{ + [Reactive] public string Email { get; set; } + [Reactive] public string FullName { get; set; } + [Reactive] public Gender Gender { get; set; } + [Reactive] public DateTime? BirthDate { get; set; } + [Reactive] public TimeSpan? MeetingTime { get; set; } + [Reactive] public int? NumberOfSeats { get; set; } + [Reactive] public bool IsTermsAndConditionsAccepted { get; set; } +} +``` + +```xml + +``` + +![AutoFormView](images/autoformview-example-dark.png) + + +## Configuration + +AutoFormView can be configured using the `AutoFormViewOptions` in the MauiProgram.cs file. Here is an example of how to configure the `AutoFormView`: + +```csharp +builder.Services.Configure(options => +{ + // configure options here +}); +``` + +### DataAnnotations +It's not supported DataAnnotations by default. You can add `UraniumUI.Validations.DataAnnotations` package to project and configure `AutoFormViewOptions` to use DataAnnotations. + +```csharp +builder.Services.Configure(options => +{ + options.ValidationFactory = DataAnnotationValidation.CreateValidations; +}); +``` + +### EditorMapping +You can configure the `AutoFormView` to use a specific editor for a type. For example, you can configure the `AutoFormViewOptions` to use a `Editor` for `string` properties. + +```csharp +builder.Services.Configure(options => +{ + options.EditorMapping[typeof(string)] = (property, source) => + { + var editor = new Entry(); + editor.SetBinding(Entry.TextProperty, new Binding(property.Name, source: source)); + return editor; + }; +}); +``` + +> Note: The following types are already mapped by default: `string`, `int`, `float`, `double`, `DateTime`, `TimeSpan`, `bool`, `Enum`, `Keyboard`. + + +### Property Name Mapping +You can configure the `PropertyNameFactory` property of `AutoFormViewOptions` to use a custom factory to get the property name. For example, you can implement a localization factory to get the property name from a resource file. + +```csharp +builder.Services.Configure(options => +{ + options.PropertyNameFactory = property => + { + return Localize(property.Name); + }; +}); +``` + +## Customization + +You can customize the `AutoFormView`. + + +## ItemsLayout +You can customize the `ItemsLayout` of the `AutoFormView` using the `ItemsLayout` property. For example, you can use a `GridLayout` to display the properties in a grid. + +> **Note:** It's not the same as the `ItemsLayout` of the `CollectionView`. This is a **real** layout that will be used to place editors into children. Such as `StackLayout`, `Grid`, `FlexLayout`, etc. + +```xml + + + + + +``` + +![AutoFormView](images/autoformview-itemslayout-grid-dark.png) + + +## ShowMissingProperties + +You can configure the `AutoFormView` to show missing properties using the `ShowMissingProperties` property. For example, you can set the `ShowMissingProperties` to `true` to show all properties of the model. + +```xml + +``` + +![AutoFormView](images/autoformview-showmissingproperties-dark.png) \ No newline at end of file diff --git a/docs/en/infrastructure/images/autoformview-example-dark.png b/docs/en/infrastructure/images/autoformview-example-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..d24ebca3d41d0506263bfa460eaa5732c05dbdc8 GIT binary patch literal 21322 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 S(V{^JG}K}AA?CrT3;zLSbJCRn literal 0 HcmV?d00001 diff --git a/docs/en/infrastructure/images/autoformview-itemslayout-grid-dark.png b/docs/en/infrastructure/images/autoformview-itemslayout-grid-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..894ba805c742af20a6c604b6092a1b1d3d1325d8 GIT binary patch literal 14424 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 C2mwq0 literal 0 HcmV?d00001 From e3e030179a09a9c41d4a9242341b29484ff438af Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 22:52:05 +0300 Subject: [PATCH 7/9] Add properties for Submit & Reset buttons --- demo/UraniumApp/Pages/AutoFormViewPage.xaml | 3 + docs/en/infrastructure/AutoFormView.md | 23 ++- .../images/autoformview-footerlayout-dark.png | Bin 0 -> 16963 bytes src/UraniumUI/Controls/AutoFormView.cs | 138 ++++++++++++++---- 4 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 docs/en/infrastructure/images/autoformview-footerlayout-dark.png diff --git a/demo/UraniumApp/Pages/AutoFormViewPage.xaml b/demo/UraniumApp/Pages/AutoFormViewPage.xaml index 00f4c640..7e98dd28 100644 --- a/demo/UraniumApp/Pages/AutoFormViewPage.xaml +++ b/demo/UraniumApp/Pages/AutoFormViewPage.xaml @@ -10,6 +10,9 @@ Title="AutoFormViewPage"> + + + \ No newline at end of file diff --git a/docs/en/infrastructure/AutoFormView.md b/docs/en/infrastructure/AutoFormView.md index 38941c6f..6fdeb6ec 100644 --- a/docs/en/infrastructure/AutoFormView.md +++ b/docs/en/infrastructure/AutoFormView.md @@ -111,6 +111,19 @@ You can customize the `ItemsLayout` of the `AutoFormView` using the `ItemsLayout ![AutoFormView](images/autoformview-itemslayout-grid-dark.png) +## FooterLayout +You can customize the `FooterLayout` of the `AutoFormView` using the `FooterLayout` property. For example, you can use a `HorizontalStackLayout` to display the buttons in a horizontal stack. + +```xml + + + + + +``` + +![AutoFormView](images/autoformview-footerlayout-dark.png) + ## ShowMissingProperties You can configure the `AutoFormView` to show missing properties using the `ShowMissingProperties` property. For example, you can set the `ShowMissingProperties` to `true` to show all properties of the model. @@ -119,4 +132,12 @@ You can configure the `AutoFormView` to show missing properties using the `ShowM ``` -![AutoFormView](images/autoformview-showmissingproperties-dark.png) \ No newline at end of file +![AutoFormView](images/autoformview-showmissingproperties-dark.png) + + +## Other Properties + +- `ShowSubmitButton`: Indicates whether the submit button is visible. The default value is `true`. +- `SohwResetButton`: Indicates whether the reset button is visible. The default value is `true`. +- `SubmitButtonText`: The text of the submit button. The default value is `Submit`. +- `ResetButtonText`: The text of the reset button. The default value is `Reset`. \ No newline at end of file diff --git a/docs/en/infrastructure/images/autoformview-footerlayout-dark.png b/docs/en/infrastructure/images/autoformview-footerlayout-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..80c8fee5b215397f7d86bed3f22e12961fcd8621 GIT binary patch literal 16963 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 _footerView.Content; set - { - _footerView.Content = value; - if (_itemsLayout.Children.Count > 0 && !_itemsLayout.Contains(_footerView)) - { - _itemsLayout.Children.Add(_footerView); - } - } - } + public bool ShowSubmitButton { get => (bool)GetValue(ShowSubmitbuttonProperty); set => SetValue(ShowSubmitbuttonProperty, value); } + + public static readonly BindableProperty ShowSubmitbuttonProperty = BindableProperty.Create( + nameof(ShowSubmitButton), typeof(bool), typeof(AutoFormView), defaultValue: true, + propertyChanged: (bindable, oldValue, newValue) => (bindable as AutoFormView).OnShowSubmitButtonChanged()); + + public bool ShowResetButton { get => (bool)GetValue(ShowResetButtonProperty); set => SetValue(ShowResetButtonProperty, value); } + + public static readonly BindableProperty ShowResetButtonProperty = BindableProperty.Create( + nameof(ShowResetButton), typeof(bool), typeof(AutoFormView), defaultValue: true, + propertyChanged: (bindable, oldValue, newValue) => (bindable as AutoFormView).OnShowResetButtonChanged()); + + public string SubmitButtonText { get => (string)GetValue(SubmitButtonTextProperty); set => SetValue(SubmitButtonTextProperty, value); } + + public static readonly BindableProperty SubmitButtonTextProperty = BindableProperty.Create( + nameof(SubmitButtonText), typeof(string), typeof(AutoFormView), defaultValue: "Submit", + propertyChanged: (bindable, oldValue, newValue) => (bindable as AutoFormView).OnSubmitButtonTextChanged()); + + public string ResetButtonText { get => (string)GetValue(ResetButtonTextProperty); set => SetValue(ResetButtonTextProperty, value); } + + public static readonly BindableProperty ResetButtonTextProperty = BindableProperty.Create( + nameof(ResetButtonText), typeof(string), typeof(AutoFormView), defaultValue: "Reset", + propertyChanged: (bindable, oldValue, newValue) => (bindable as AutoFormView).OnResetButtonTextChanged()); public bool ShowMissingProperties { get; set; } = true; @@ -63,6 +74,28 @@ public Layout ItemsLayout } } + private Layout _footerLayout = new VerticalStackLayout { Spacing = 12 }; + + public Layout FooterLayout + { + get => _footerLayout; + set + { + ItemsLayout.Remove(_footerLayout); + while (ItemsLayout.Children.Count > 0) + { + var item = _footerLayout.Children.First(); + ItemsLayout.Children.Remove(item); + value?.Children.Add(item); + } + _footerLayout = value; + if (ItemsLayout.Children.Count > 0 && _footerLayout != null) + { + ItemsLayout.Children.Add(_footerLayout); + } + } + } + protected Dictionary EditorMapping { get; } protected AutoFormViewOptions Options { get; } @@ -128,39 +161,80 @@ private void Render() } } - if (!_itemsLayout.Children.Contains(_footerView)) + if (!_itemsLayout.Children.Contains(_footerLayout)) { - _itemsLayout.Children.Add(_footerView); + _itemsLayout.Children.Add(_footerLayout); + OnShowSubmitButtonChanged(); + OnShowResetButtonChanged(); } + } - if (_footerView.Content == null) + Button? submitButton; + protected virtual void OnShowSubmitButtonChanged() + { + if (ShowSubmitButton) { - _footerView.Content = GetFooter(); + if (submitButton is null) + { + submitButton = new Button + { + Text = SubmitButtonText, + StyleClass = new[] { "FilledButton" }, + Command = new Command(Submit) + }; + _footerLayout.Children.Insert(0, submitButton); + } + } + else + { + if (submitButton is not null) + { + _footerLayout.Children.Remove(submitButton); + submitButton = null; + } } } - protected virtual View GetFooter() + Button? resetButton; + protected virtual void OnShowResetButtonChanged() { - var submitButton = new Button + if (ShowResetButton) { - Text = "Submit", - StyleClass = new List { "FilledButton" }, - }; + if (resetButton is null) + { + resetButton = new Button + { + Text = ResetButtonText, + StyleClass = new[] { "OutlinedButton" }, + Command = new Command(Reset) + }; + _footerLayout.Children.Add(resetButton); + } + } + else + { + if (resetButton != null) + { + _footerLayout.Children.Remove(resetButton); + resetButton = null; + } + } + } - var resetButton = new Button + protected virtual void OnSubmitButtonTextChanged() + { + if (submitButton != null) { - Text = "Reset", - StyleClass = new List { "OutlinedButton" }, - }; + submitButton.Text = SubmitButtonText; + } + } - FormView.SetIsSubmitButton(submitButton, true); - FormView.SetIsResetButton(resetButton, true); - return new StackLayout + protected virtual void OnResetButtonTextChanged() + { + if (resetButton != null) { - Margin = new Thickness(10), - Spacing = 12, - Children = { submitButton, resetButton } - }; + resetButton.Text = ResetButtonText; + } } public static View EditorForString(PropertyInfo property, object source) From 7d663c15e126b180203df72e731e5651aed59d76 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 22:52:46 +0300 Subject: [PATCH 8/9] Update InputKit to `4.4.2` --- .../UraniumUI.Validations.DataAnnotations.csproj | 2 +- src/UraniumUI/UraniumUI.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UraniumUI.Validations.DataAnnotations/UraniumUI.Validations.DataAnnotations.csproj b/src/UraniumUI.Validations.DataAnnotations/UraniumUI.Validations.DataAnnotations.csproj index 09c752bc..65e78583 100644 --- a/src/UraniumUI.Validations.DataAnnotations/UraniumUI.Validations.DataAnnotations.csproj +++ b/src/UraniumUI.Validations.DataAnnotations/UraniumUI.Validations.DataAnnotations.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/UraniumUI/UraniumUI.csproj b/src/UraniumUI/UraniumUI.csproj index 33ddc8c9..431bb7fa 100644 --- a/src/UraniumUI/UraniumUI.csproj +++ b/src/UraniumUI/UraniumUI.csproj @@ -25,7 +25,7 @@ - + From d4858045e283f7aa1c12ae59f90ec75d1ffff0a7 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 23 Feb 2024 22:57:44 +0300 Subject: [PATCH 9/9] Use original FormView commands in AutoFormView --- src/UraniumUI/Controls/AutoFormView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UraniumUI/Controls/AutoFormView.cs b/src/UraniumUI/Controls/AutoFormView.cs index 90f7a284..79a06dc0 100644 --- a/src/UraniumUI/Controls/AutoFormView.cs +++ b/src/UraniumUI/Controls/AutoFormView.cs @@ -180,7 +180,7 @@ protected virtual void OnShowSubmitButtonChanged() { Text = SubmitButtonText, StyleClass = new[] { "FilledButton" }, - Command = new Command(Submit) + Command = buttonSubmitCommand, }; _footerLayout.Children.Insert(0, submitButton); } @@ -206,7 +206,7 @@ protected virtual void OnShowResetButtonChanged() { Text = ResetButtonText, StyleClass = new[] { "OutlinedButton" }, - Command = new Command(Reset) + Command = buttonResetCommand, }; _footerLayout.Children.Add(resetButton); }