From cb88772a40f4f4643fd7cd9816e7497055b18442 Mon Sep 17 00:00:00 2001 From: "Dixon, Evan" Date: Tue, 21 Oct 2025 14:44:27 -0500 Subject: [PATCH 1/4] Show custom row actions even if no other actions are visible --- Mimeo.DynamicUI.Blazor/Forms/ODataGrid.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mimeo.DynamicUI.Blazor/Forms/ODataGrid.razor.cs b/Mimeo.DynamicUI.Blazor/Forms/ODataGrid.razor.cs index 2fe7dc3..f668fbf 100644 --- a/Mimeo.DynamicUI.Blazor/Forms/ODataGrid.razor.cs +++ b/Mimeo.DynamicUI.Blazor/Forms/ODataGrid.razor.cs @@ -428,7 +428,7 @@ private void ShowTooltip(ElementReference element, string languageKey) } private bool GetActionsMenuVisibility() { - return CanCopy || CanUpdate || CanView || CanDelete; + return CanCopy || CanUpdate || CanView || CanDelete || CustomRowActions.Any(); } private GridItem CreateGridItem(ViewModel listModel) From 2be8fd3547ba7e3d4eb6a82dccabd48d86f61bbe Mon Sep 17 00:00:00 2001 From: "Dixon, Evan" Date: Tue, 21 Oct 2025 15:10:30 -0500 Subject: [PATCH 2/4] Port refactors from a work-in-progress attempt to add support for other front-ends --- .../FormFields/DynamicField.cs | 8 +- .../FormFields/ListField.razor | 4 +- .../ViewModels/TestViewModel.cs | 32 +++--- .../Data/OData/ODataExpressionGenerator.cs | 5 + Mimeo.DynamicUI/FormFieldDefinition.cs | 2 + Mimeo.DynamicUI/FormFieldType.cs | 61 ++++++++++- Mimeo.DynamicUI/ListFieldDefinition.cs | 41 ++++++- Mimeo.DynamicUI/TextFieldDefinition.cs | 18 +++- Mimeo.DynamicUI/ViewModel.cs | 101 +++++++++++++++++- 9 files changed, 243 insertions(+), 29 deletions(-) diff --git a/Mimeo.DynamicUI.Blazor/FormFields/DynamicField.cs b/Mimeo.DynamicUI.Blazor/FormFields/DynamicField.cs index 137582e..f095a3b 100644 --- a/Mimeo.DynamicUI.Blazor/FormFields/DynamicField.cs +++ b/Mimeo.DynamicUI.Blazor/FormFields/DynamicField.cs @@ -9,6 +9,7 @@ public class DynamicField : ComponentBase private static readonly Dictionary formFieldTypeMap = new() { { FormFieldType.Text, typeof(TextField) }, + { FormFieldType.Combobox, typeof(TextField) }, { FormFieldType.Checkbox, typeof(CheckboxField) }, { FormFieldType.SingleSelect, typeof(SingleSelectField) }, { FormFieldType.MultiSelect, typeof(MultiSelectField) }, @@ -22,7 +23,12 @@ public class DynamicField : ComponentBase { FormFieldType.DateTime, typeof(DateTimeField) }, { FormFieldType.Integer, typeof(IntegerField) }, { FormFieldType.Decimal, typeof(DecimalField) }, +#pragma warning disable CS0618 // Type or member is obsolete (Justification: Backwards compatibility) { FormFieldType.List, typeof(ListField<>) }, +#pragma warning restore CS0618 // Type or member is obsolete + { FormFieldType.Table, typeof(ListField<>) }, + { FormFieldType.SectionList, typeof(ListField<>) }, + { FormFieldType.ReorderableSectionList, typeof(ListField<>) }, { FormFieldType.Nullable, typeof(NullableFormField) }, { FormFieldType.Guid, typeof(GuidFormField) } }; @@ -118,7 +124,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.CloseComponent(); } - private bool InheritsFormFieldBase(Type type) + private static bool InheritsFormFieldBase(Type type) { // The built-in methods don't support unbound generics (or I haven't found the correct one yet) diff --git a/Mimeo.DynamicUI.Blazor/FormFields/ListField.razor b/Mimeo.DynamicUI.Blazor/FormFields/ListField.razor index b31f40a..2065835 100644 --- a/Mimeo.DynamicUI.Blazor/FormFields/ListField.razor +++ b/Mimeo.DynamicUI.Blazor/FormFields/ListField.razor @@ -5,7 +5,7 @@ @typeparam T @inherits FormFieldBase> -@if (!wrapInViewModel && ListDefinition?.PresentationMode == ListFieldPresentationMode.SectionList) +@if (!wrapInViewModel && ListDefinition?.Type == FormFieldType.SectionList) { if (ReadOnly) { @@ -56,7 +56,7 @@ } } -else if (!wrapInViewModel && ListDefinition?.PresentationMode == ListFieldPresentationMode.ReorderableSectionList) +else if (!wrapInViewModel && ListDefinition?.Type == FormFieldType.ReorderableSectionList) { if (ReadOnly) { diff --git a/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs b/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs index 3a1ef69..f7badf4 100644 --- a/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs +++ b/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs @@ -144,7 +144,7 @@ protected override IEnumerable GetListFormFields() yield return FormField(() => Number); yield return FormField(() => Decimal); yield return FormField(() => DateTimeUtc, dateDisplayMode: DateDisplayMode.UserLocal); - yield return FormField(() => StringList); + yield return Table(() => StringList); } protected override IEnumerable GetDropDownListFormFields() @@ -185,13 +185,13 @@ protected override IEnumerable GetEditFormFields() yield return FormField(() => ComboBox, textType: TextType.SingleLine, items: ["Option 1", "option2languagekey", "Option 3"]); yield return new FormFieldDefinition(FormFieldType.Color, () => Color); yield return FormField(() => Section); - yield return FormField(() => StringList); + yield return Table(() => StringList); // Depending on the structure of the view model, sometimes a table is more appropriate - yield return FormField(() => SimpleModelList, mode: ListFieldPresentationMode.Table); + yield return Table(() => SimpleModelList); // but for sufficiently large view models, a section list is easier on the user - yield return FormField(() => AdvancedModelList, m => m.FormField(() => m.Property1), mode: ListFieldPresentationMode.SectionList); + yield return SectionList(() => AdvancedModelList, m => m.FormField(() => m.Property1)); yield return new SingleSelectDropDownFormFieldDefinition(() => RelatedModelId, relatedModelsSource, FormField(() => Name), idField); yield return new MultiSelectDropDownFormFieldDefinition(() => RelatedModelIds, relatedModelsSource, FormField(() => Name), idField); @@ -233,9 +233,9 @@ protected override IEnumerable GetSearchFormFields() yield return FormField(() => ComboBox, textType: TextType.SingleLine, items: ["Option 1", "option2languagekey", "Option 3"]); yield return new FormFieldDefinition(FormFieldType.Color, () => Color); yield return FormField(() => Section); - yield return FormField(() => StringList); - yield return FormField(() => SimpleModelList); - yield return FormField(() => AdvancedModelList); + yield return Table(() => StringList); + yield return Table(() => SimpleModelList); + yield return SectionList(() => AdvancedModelList); yield return FormField(() => Enabled); yield return new NullableFormFieldDefinition(() => NullableStringEnabled, FormField(() => NullableStringValue)); yield return new FormFieldDefinition(FormFieldType.Checkbox, () => EnableHiddenProperties) @@ -249,7 +249,7 @@ protected override IEnumerable GetSearchFormFields() } - public class SimpleSubViewModel : ViewModel, INotifyPropertyChanged + public class SimpleSubViewModel : ViewModel { public string? Property1 { @@ -257,7 +257,7 @@ public string? Property1 set { _property1 = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property1))); + RaisePropertyChanged(nameof(Property1)); } } private string? _property1; @@ -268,14 +268,11 @@ public int Property2 set { _property2 = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property2))); + RaisePropertyChanged(nameof(Property2)); } } private int _property2; - // INotifyPropertyChanged is partially supported by the UI, and is generally not required, but can be useful in niche situations - public event PropertyChangedEventHandler? PropertyChanged; - protected override IEnumerable GetEditFormFields() { yield return FormField(() => Property1); @@ -291,7 +288,7 @@ public string? Property1 set { _property1 = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property1))); + RaisePropertyChanged(nameof(Property1)); } } private string? _property1; @@ -302,21 +299,18 @@ public int Property2 set { _property2 = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property2))); + RaisePropertyChanged(nameof(Property2)); } } private int _property2; public List SubList { get; set; } = []; - // INotifyPropertyChanged is partially supported by the UI, and is generally not required, but can be useful in niche situations - public event PropertyChangedEventHandler? PropertyChanged; - protected override IEnumerable GetEditFormFields() { yield return FormField(() => Property1); yield return FormField(() => Property2); - yield return FormField(() => SubList); + yield return Table(() => SubList); } } diff --git a/Mimeo.DynamicUI/Data/OData/ODataExpressionGenerator.cs b/Mimeo.DynamicUI/Data/OData/ODataExpressionGenerator.cs index 8f938e4..3b6fc63 100644 --- a/Mimeo.DynamicUI/Data/OData/ODataExpressionGenerator.cs +++ b/Mimeo.DynamicUI/Data/OData/ODataExpressionGenerator.cs @@ -120,7 +120,12 @@ public IEnumerable GetSupportedFilterOperators(DataFieldDefi DataFilterOperator.GreaterThan, DataFilterOperator.GreaterThanOrEquals }; +#pragma warning disable CS0618 // Type or member is obsolete (Justification: Backwards compatibility) case FormFieldType.List: +#pragma warning restore CS0618 // Type or member is obsolete + case FormFieldType.Table: + case FormFieldType.SectionList: + case FormFieldType.ReorderableSectionList: var sampleItem = (filter.FormFieldDefinition as IListFieldDefinition)?.CreateNewItem(); if (sampleItem is ViewModel) { diff --git a/Mimeo.DynamicUI/FormFieldDefinition.cs b/Mimeo.DynamicUI/FormFieldDefinition.cs index b774b1b..6076529 100644 --- a/Mimeo.DynamicUI/FormFieldDefinition.cs +++ b/Mimeo.DynamicUI/FormFieldDefinition.cs @@ -132,6 +132,8 @@ public string FilterPropertyName public string LanguageKey { get; set; } + public string DescriptionLanguageKey => LanguageKey + "_desc"; + public Type PropertyType { get; set; } public Type? FilterType { get; set; } diff --git a/Mimeo.DynamicUI/FormFieldType.cs b/Mimeo.DynamicUI/FormFieldType.cs index de58a12..dc4490a 100644 --- a/Mimeo.DynamicUI/FormFieldType.cs +++ b/Mimeo.DynamicUI/FormFieldType.cs @@ -3,22 +3,79 @@ public enum FormFieldType { Hidden = 0, - Text, + + /// + /// A field that accepts user-entered text + /// + Text, + + /// + /// A field that accepts user-entered text but presents some pre-defined values + /// + Combobox, + Checkbox, + + /// + /// A field that presents the user with pre-defined value and allows selection of a single value + /// SingleSelect, + + /// + /// A field that presents the user with a dropdown to select a single pre-defined value + /// SingleSelectDropdown, + + /// + /// A field that presents the user with a dropdown to select a single value from a data source + /// SingleSelectDataSourceDropdown, + + /// + /// A field that presents the user with pre-defined value and allows selection of multiple values + /// MultiSelect, + + /// + /// A field that presents the user with a dropdown to select one or more pre-defined values + /// MultiSelectDropdown, + + /// + /// A field that presents the user with values that come from a data source and allows selection of multiple values + /// MultiSelectDataSourceDropdown, + Date, Time, DateTime, Color, Integer, - Decimal, + Decimal, + + [Obsolete("Use Table, SectionList, or ReorderableSectionList instead")] List, + + /// + /// Presents a list of items or view models in a table + /// + Table, + + /// + /// Presents a list of items or view models as a sequential list of sub-editors + /// + SectionList, + + /// + /// Presents a list of items or view models as a sequential list of sub-editors, with the ability to reorder items + /// + ReorderableSectionList, + + /// + /// Presents another form field, wrapped with a checkbox that is meant to indicate whether the value is null + /// Nullable, + Guid, Section, Custom diff --git a/Mimeo.DynamicUI/ListFieldDefinition.cs b/Mimeo.DynamicUI/ListFieldDefinition.cs index b4b104a..2cd174a 100644 --- a/Mimeo.DynamicUI/ListFieldDefinition.cs +++ b/Mimeo.DynamicUI/ListFieldDefinition.cs @@ -10,16 +10,54 @@ public interface IListFieldDefinition public class ListFieldDefinition : FormFieldDefinition, IListFieldDefinition { + [Obsolete("Use overload with FormFieldType instead")] public ListFieldDefinition(Expression>> @for) : base(FormFieldType.List, LinqExtensions.Cast, object?>(@for)) { } + public ListFieldDefinition(FormFieldType formFieldType, Expression>> @for) + : base(formFieldType, LinqExtensions.Cast, object?>(@for)) + { + switch (formFieldType) + { + case FormFieldType.Table: + case FormFieldType.SectionList: + case FormFieldType.ReorderableSectionList: + break; + default: + throw new ArgumentException("formFieldType must be Table, SectionList, or ReorderableSectionList", nameof(formFieldType)); + } + } + public FormFieldType? ItemFormFieldType { get; set; } public Func? NewItemCreator { get; set; } - public ListFieldPresentationMode PresentationMode { get; set; } = ListFieldPresentationMode.Table; + [Obsolete("Differentiate using FormFieldType instead")] + public ListFieldPresentationMode PresentationMode + { + get => _presentationMode; + set + { + _presentationMode = value; + if (_presentationMode == ListFieldPresentationMode.Table) + { + this.Type = FormFieldType.Table; + } + else if (_presentationMode == ListFieldPresentationMode.SectionList) + { + this.Type = FormFieldType.SectionList; + } + else if (_presentationMode == ListFieldPresentationMode.ReorderableSectionList) + { + this.Type = FormFieldType.ReorderableSectionList; + } + } + } + + [Obsolete("Differentiate using FormFieldType instead")] + private ListFieldPresentationMode _presentationMode = ListFieldPresentationMode.Table; /// /// For use with equal to or , @@ -50,6 +88,7 @@ public T CreateNewItem() object? IListFieldDefinition.CreateNewItem() => CreateNewItem(); } + [Obsolete("Differentiate using FormFieldType instead")] public enum ListFieldPresentationMode { Table, diff --git a/Mimeo.DynamicUI/TextFieldDefinition.cs b/Mimeo.DynamicUI/TextFieldDefinition.cs index 3894ae5..8b2a598 100644 --- a/Mimeo.DynamicUI/TextFieldDefinition.cs +++ b/Mimeo.DynamicUI/TextFieldDefinition.cs @@ -41,6 +41,22 @@ public TextType TextType /// /// An optional set of values that can be selected as a combobox. Requires to be . /// - public List? Items { get; set; } + public List? Items + { + get => _items; + set + { + _items = value; + + if (value != null && value.Count > 0) + { + // Backwards compatibility for when Items was just an option under a Text form field type + Type = FormFieldType.Combobox; + } + } + } + private List? _items; + + public bool MultiLine => TextType != TextType.SingleLine; } } diff --git a/Mimeo.DynamicUI/ViewModel.cs b/Mimeo.DynamicUI/ViewModel.cs index db3bf6c..9d3a5aa 100644 --- a/Mimeo.DynamicUI/ViewModel.cs +++ b/Mimeo.DynamicUI/ViewModel.cs @@ -1,11 +1,19 @@ using Mimeo.DynamicUI.Data; using Mimeo.DynamicUI.Extensions; +using System.ComponentModel; using System.Linq.Expressions; namespace Mimeo.DynamicUI { - public abstract class ViewModel + public abstract class ViewModel : INotifyPropertyChanged { + public event PropertyChangedEventHandler? PropertyChanged; + + protected void RaisePropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + public virtual object? GetValue(FormFieldDefinition field) { var property = this.GetType().GetProperty(field.PropertyName); @@ -26,7 +34,8 @@ public virtual void SetValue(FormFieldDefinition field, object? value) } property.SetValue(this, value); - field?.OnValueChanged?.Invoke(value); + field.OnValueChanged?.Invoke(value); + RaisePropertyChanged(field.PropertyName); } /// @@ -109,7 +118,10 @@ protected virtual IEnumerable GetSearchFormFields() /// /// Gets fields that should show up in an edit form for a single view model /// - protected abstract IEnumerable GetEditFormFields(); + protected virtual IEnumerable GetEditFormFields() + { + return Enumerable.Empty(); + } public FormFieldDefinition FormField(FormFieldType type, Expression> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null) { @@ -237,6 +249,7 @@ public FormFieldDefinition FormField(Expression> @for, bool read }.WithCustomLanguageKey(customLanguageKey); } + [Obsolete("Use Table(...), SectionList(...), or ReorderableSectionList(...) instead")] public FormFieldDefinition FormField(Expression>> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null, ListFieldPresentationMode? mode = null) { return new ListFieldDefinition(@for) @@ -251,6 +264,7 @@ public FormFieldDefinition FormField(Expression>> @for, bool rea }.WithCustomLanguageKey(customLanguageKey); } + [Obsolete("Use Table(...), SectionList(...), or ReorderableSectionList(...) instead")] public FormFieldDefinition FormField(Expression>> @for, Func headerField, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null, ListFieldPresentationMode? mode = null) { return new ListFieldDefinition(@for) @@ -266,6 +280,87 @@ public FormFieldDefinition FormField(Expression(Expression>> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.Table, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator + }.WithCustomLanguageKey(customLanguageKey); + } + + public FormFieldDefinition Table(Expression>> @for, Func headerField, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.Table, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator, + HeaderField = headerField + }.WithCustomLanguageKey(customLanguageKey); + } + + public FormFieldDefinition SectionList(Expression>> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.SectionList, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator + }.WithCustomLanguageKey(customLanguageKey); + } + + public FormFieldDefinition SectionList(Expression>> @for, Func headerField, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.SectionList, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator, + HeaderField = headerField + }.WithCustomLanguageKey(customLanguageKey); + } + + public FormFieldDefinition ReorderableSectionList(Expression>> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.SectionList, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator + }.WithCustomLanguageKey(customLanguageKey); + } + + public FormFieldDefinition ReorderableSectionList(Expression>> @for, Func headerField, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) + { + return new ListFieldDefinition(FormFieldType.SectionList, @for) + { + ReadOnly = readOnly, + Sortable = sortable, + Collapsed = collapsed, + DefaultSortDirection = defaultSort, + Filterable = filterable, + NewItemCreator = newItemCreator, + HeaderField = headerField + }.WithCustomLanguageKey(customLanguageKey); + } + public FormFieldDefinition FormField(Expression> @for, bool showTime = true, DateDisplayMode dateDisplayMode = DateDisplayMode.Utc, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null) { return new DateTimeFieldDefinition(LinqExtensions.Cast(@for)) From b807bad09110bc536d86de5faea588b6abd817c0 Mon Sep 17 00:00:00 2001 From: "Dixon, Evan" Date: Tue, 21 Oct 2025 15:24:25 -0500 Subject: [PATCH 3/4] Fix ReorderableSectionList form field type --- Mimeo.DynamicUI/ViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mimeo.DynamicUI/ViewModel.cs b/Mimeo.DynamicUI/ViewModel.cs index 9d3a5aa..47f0855 100644 --- a/Mimeo.DynamicUI/ViewModel.cs +++ b/Mimeo.DynamicUI/ViewModel.cs @@ -336,7 +336,7 @@ public FormFieldDefinition SectionList(Expression(Expression>> @for, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) { - return new ListFieldDefinition(FormFieldType.SectionList, @for) + return new ListFieldDefinition(FormFieldType.ReorderableSectionList, @for) { ReadOnly = readOnly, Sortable = sortable, @@ -349,7 +349,7 @@ public FormFieldDefinition ReorderableSectionList(Expression>> @ public FormFieldDefinition ReorderableSectionList(Expression>> @for, Func headerField, bool readOnly = false, bool sortable = true, bool collapsed = false, SortDirection defaultSort = SortDirection.None, bool filterable = true, string? customLanguageKey = null, Func? newItemCreator = null) { - return new ListFieldDefinition(FormFieldType.SectionList, @for) + return new ListFieldDefinition(FormFieldType.ReorderableSectionList, @for) { ReadOnly = readOnly, Sortable = sortable, From e788c1aa13073f4fe240db933f52e788a71e9018 Mon Sep 17 00:00:00 2001 From: "Dixon, Evan" Date: Wed, 22 Oct 2025 08:17:32 -0500 Subject: [PATCH 4/4] Cleanup --- .../Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs b/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs index f7badf4..14683b5 100644 --- a/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs +++ b/Mimeo.DynamicUI.Demo/Mimeo.DynamicUI.Demo.Shared/ViewModels/TestViewModel.cs @@ -280,7 +280,7 @@ protected override IEnumerable GetEditFormFields() } } - public class AdvancedSubViewModel : ViewModel, INotifyPropertyChanged + public class AdvancedSubViewModel : ViewModel { public string? Property1 {