feat: support dynamic form group generation, add demo.
This commit is contained in:
24
demo/Ursa.Demo/DataTemplates/FormDataTemplateSelector.cs
Normal file
24
demo/Ursa.Demo/DataTemplates/FormDataTemplateSelector.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.Converters;
|
||||||
|
|
||||||
|
public class FormDataTemplateSelector: ResourceDictionary, IDataTemplate
|
||||||
|
{
|
||||||
|
public Control? Build(object? param)
|
||||||
|
{
|
||||||
|
if (param is null) return null;
|
||||||
|
var type = param.GetType();
|
||||||
|
if (this.TryGetResource(type, null, out var template) && template is IDataTemplate dataTemplate)
|
||||||
|
{
|
||||||
|
return dataTemplate.Build(param);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is IFromItemViewModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:u="https://irihi.tech/ursa"
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
xmlns:vm="clr-namespace:Ursa.Demo.ViewModels;assembly=Ursa.Demo"
|
xmlns:vm="clr-namespace:Ursa.Demo.ViewModels;assembly=Ursa.Demo"
|
||||||
|
xmlns:converters="clr-namespace:Ursa.Demo.Converters"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
@@ -50,6 +51,31 @@
|
|||||||
<TextBox Width="300" u:FormItem.Label="Name" Text="{Binding Name}"/>
|
<TextBox Width="300" u:FormItem.Label="Name" Text="{Binding Name}"/>
|
||||||
<TextBox Width="300" u:FormItem.Label="Email" Text="{Binding Email}" />
|
<TextBox Width="300" u:FormItem.Label="Email" Text="{Binding Email}" />
|
||||||
</u:Form>
|
</u:Form>
|
||||||
|
<u:Divider Content="MVVM setup for dynamic items. " />
|
||||||
|
<u:Form ItemsSource="{Binding FormGroups}" HorizontalAlignment="Stretch" LabelPosition="Left" LabelWidth="*">
|
||||||
|
<u:Form.Styles>
|
||||||
|
<Style Selector="u|FormGroup" x:DataType="vm:IFormGroupViewModel">
|
||||||
|
<Setter Property="Header" Value="{Binding Title}" />
|
||||||
|
<Setter Property="ItemsSource" Value="{Binding Items}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="u|FormItem" x:DataType="vm:IFromItemViewModel">
|
||||||
|
<Setter Property="Label" Value="{Binding Label}" />
|
||||||
|
</Style>
|
||||||
|
</u:Form.Styles>
|
||||||
|
<u:Form.ItemTemplate>
|
||||||
|
<converters:FormDataTemplateSelector>
|
||||||
|
<DataTemplate x:Key="{x:Type vm:FormTextViewModel}" DataType="vm:FormTextViewModel">
|
||||||
|
<TextBox Text="{Binding Value}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate x:Key="{x:Type vm:FormAgeViewModel}" DataType="vm:FormAgeViewModel">
|
||||||
|
<u:NumericUIntUpDown Value="{Binding Age}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate x:Key="{x:Type vm:FormDateRangeViewModel}" DataType="vm:FormDateRangeViewModel">
|
||||||
|
<u:DateRangePicker SelectedStartDate="{Binding Start}" SelectedEndDate="{Binding End}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</converters:FormDataTemplateSelector>
|
||||||
|
</u:Form.ItemTemplate>
|
||||||
|
</u:Form>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
|
|
||||||
namespace Ursa.Demo.ViewModels;
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
@@ -11,13 +13,48 @@ public partial class FormDemoViewModel : ObservableObject
|
|||||||
public FormDemoViewModel()
|
public FormDemoViewModel()
|
||||||
{
|
{
|
||||||
Model = new DataModel();
|
Model = new DataModel();
|
||||||
|
FormGroups = new ObservableCollection<IFormElement>
|
||||||
|
{
|
||||||
|
new FormGroupViewModel
|
||||||
|
{
|
||||||
|
Title = "Basic Information",
|
||||||
|
Items = new ObservableCollection<IFromItemViewModel>
|
||||||
|
{
|
||||||
|
new FormTextViewModel { Label = "Name" },
|
||||||
|
new FormAgeViewModel { Label = "Age" },
|
||||||
|
new FormTextViewModel { Label = "Email" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FormGroupViewModel
|
||||||
|
{
|
||||||
|
Title = "Education Information",
|
||||||
|
Items = new ObservableCollection<IFromItemViewModel>
|
||||||
|
{
|
||||||
|
new FormTextViewModel { Label = "College" },
|
||||||
|
new FormDateRangeViewModel { Label = "Study Time" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FormTextViewModel(){ Label = "Other" }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<IFormElement> FormGroups { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class DataModel : ObservableObject
|
public class DataModel : ObservableObject
|
||||||
{
|
{
|
||||||
|
private DateTime _date;
|
||||||
|
|
||||||
|
private string _email = string.Empty;
|
||||||
private string _name = string.Empty;
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
private double _number;
|
||||||
|
|
||||||
|
public DataModel()
|
||||||
|
{
|
||||||
|
Date = DateTime.Today;
|
||||||
|
}
|
||||||
|
|
||||||
[MinLength(10)]
|
[MinLength(10)]
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
@@ -25,8 +62,6 @@ public partial class DataModel : ObservableObject
|
|||||||
set => SetProperty(ref _name, value);
|
set => SetProperty(ref _name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double _number;
|
|
||||||
|
|
||||||
[Range(0.0, 10.0)]
|
[Range(0.0, 10.0)]
|
||||||
public double Number
|
public double Number
|
||||||
{
|
{
|
||||||
@@ -34,8 +69,6 @@ public partial class DataModel : ObservableObject
|
|||||||
set => SetProperty(ref _number, value);
|
set => SetProperty(ref _number, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _email = string.Empty;
|
|
||||||
|
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
public string Email
|
public string Email
|
||||||
{
|
{
|
||||||
@@ -43,16 +76,50 @@ public partial class DataModel : ObservableObject
|
|||||||
set => SetProperty(ref _email, value);
|
set => SetProperty(ref _email, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime _date;
|
|
||||||
|
|
||||||
public DateTime Date
|
public DateTime Date
|
||||||
{
|
{
|
||||||
get => _date;
|
get => _date;
|
||||||
set => SetProperty(ref _date, value);
|
set => SetProperty(ref _date, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DataModel()
|
public interface IFormElement
|
||||||
{
|
{
|
||||||
Date = DateTime.Today;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IFormGroupViewModel : IFormGroup, IFormElement
|
||||||
|
{
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public ObservableCollection<IFromItemViewModel> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IFromItemViewModel: IFormElement
|
||||||
|
{
|
||||||
|
public string? Label { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class FormGroupViewModel : ObservableObject, IFormGroupViewModel
|
||||||
|
{
|
||||||
|
[ObservableProperty] private string? _title;
|
||||||
|
public ObservableCollection<IFromItemViewModel> Items { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class FormTextViewModel : ObservableObject, IFromItemViewModel
|
||||||
|
{
|
||||||
|
[ObservableProperty] private string? _label;
|
||||||
|
[ObservableProperty] private string? _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class FormAgeViewModel : ObservableObject, IFromItemViewModel
|
||||||
|
{
|
||||||
|
[ObservableProperty] private uint? _age;
|
||||||
|
[ObservableProperty] private string? _label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class FormDateRangeViewModel : ObservableObject, IFromItemViewModel
|
||||||
|
{
|
||||||
|
[ObservableProperty] private DateTime? _end;
|
||||||
|
[ObservableProperty] private string? _label;
|
||||||
|
[ObservableProperty] private DateTime? _start;
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
Name="PART_ContentPresenter"
|
Name="PART_ContentPresenter"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
@@ -111,6 +112,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
@@ -120,6 +122,7 @@
|
|||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:FormItem">
|
<ControlTemplate TargetType="u:FormItem">
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
using Ursa.Common;
|
using Ursa.Common;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
@@ -64,7 +65,11 @@ public class Form: ItemsControl
|
|||||||
|
|
||||||
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
||||||
{
|
{
|
||||||
if (item is not Control control) return new FormItem();
|
if (item is not Control control)
|
||||||
|
{
|
||||||
|
if (item is IFormGroup) return new FormGroup();
|
||||||
|
return new FormItem();
|
||||||
|
}
|
||||||
return new FormItem()
|
return new FormItem()
|
||||||
{
|
{
|
||||||
Content = control,
|
Content = control,
|
||||||
@@ -73,4 +78,17 @@ public class Form: ItemsControl
|
|||||||
[!FormItem.NoLabelProperty] = control[!FormItem.NoLabelProperty],
|
[!FormItem.NoLabelProperty] = control[!FormItem.NoLabelProperty],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.PrepareContainerForItemOverride(container, item, index);
|
||||||
|
if(container is FormItem formItem && !formItem.IsSet(ContentControl.ContentTemplateProperty))
|
||||||
|
{
|
||||||
|
formItem.ContentTemplate = ItemTemplate;
|
||||||
|
}
|
||||||
|
if (container is FormGroup group && !group.IsSet(FormGroup.ItemTemplateProperty))
|
||||||
|
{
|
||||||
|
group.ItemTemplate = ItemTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,5 +21,13 @@ public class FormGroup: HeaderedItemsControl
|
|||||||
[!FormItem.IsRequiredProperty] = control[!FormItem.IsRequiredProperty],
|
[!FormItem.IsRequiredProperty] = control[!FormItem.IsRequiredProperty],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.PrepareContainerForItemOverride(container, item, index);
|
||||||
|
if (container is FormItem formItem && !formItem.IsSet(ContentControl.ContentTemplateProperty))
|
||||||
|
{
|
||||||
|
formItem.ContentTemplate = ItemTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user