diff --git a/Demos/Blazorise.Demo/Pages/Tests/DataGrid/AutoGenerateColumnsPage.razor b/Demos/Blazorise.Demo/Pages/Tests/DataGrid/AutoGenerateColumnsPage.razor index c7bf03d847..e787f5ea5b 100644 --- a/Demos/Blazorise.Demo/Pages/Tests/DataGrid/AutoGenerateColumnsPage.razor +++ b/Demos/Blazorise.Demo/Pages/Tests/DataGrid/AutoGenerateColumnsPage.razor @@ -23,17 +23,39 @@ public class Example { - [DisplayAttribute(Name = "Name")] + [Order( DisplayOrder = 1, EditOrder = 2 )] + [Display(Name = "Name")] public string FirstName { get; set; } + [Order( DisplayOrder = 2, EditOrder = 3 )] public string LastName { get; set; } - + [Order( DisplayOrder = 3, EditOrder = 4 )] + [Numeric(EnableStep = true, ShowStepButtons = true, Step = 1)] public int Age { get; set; } + [Order( DisplayOrder = 5, EditOrder = 0 )] + public Status Status { get; set; } + + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 100 )] + [Order( DisplayOrder = 4, EditOrder = 1 )] public decimal Balance { get; set; } - public Status Status { get; set; } + [IgnoreField] + public string FieldToBeIgnored { get; set; } + + [Order( DisplayOrder = 5, EditOrder = 0 )] + [Display( Name = "Gender" )] + [Select( GetDataFunction = "GetGenders", TextField = nameof( Blazorise.Shared.Data.Gender.Description ), ValueField = nameof( Blazorise.Shared.Data.Gender.Code ) )] + public string Gender { get; set; } + + [Order( DisplayOrder = 6, EditOrder = 6 )] + [Display( Name = "DOB" )] + [Date( InputMode = DateInputMode.Date )] + public DateOnly DateOfBirth { get; set; } + + public IEnumerable GetGenders() + => EmployeeData.Genders; } public enum Status @@ -44,12 +66,12 @@ private IEnumerable data = new List() { - new(){ FirstName = "John", LastName = "Doe", Age = 30, Balance = 1000, Status = Status.Active }, - new(){ FirstName = "Jane", LastName = "Doe", Age = 28, Balance = 2000, Status = Status.Active }, - new(){ FirstName = "Joe", LastName = "Doe", Age = 26, Balance = 3000, Status = Status.Inactive }, - new(){ FirstName = "Jill", LastName = "Doe", Age = 24, Balance = 4000, Status = Status.Inactive }, - new(){ FirstName = "Jack", LastName = "Doe", Age = 22, Balance = 5000, Status = Status.Active }, - new(){ FirstName = "Jen", LastName = "Doe", Age = 20, Balance = 6000, Status = Status.Active }, + new(){ FirstName = "John", LastName = "Doe", Gender = "M", Age = 30, Balance = 1000, Status = Status.Active, FieldToBeIgnored = "4a92b1ea-e82d-4920-8d22-198a2385945e", DateOfBirth = new DateOnly(1992,03,05) }, + new(){ FirstName = "Jane", LastName = "Doe", Gender = "F",Age = 28, Balance = 2000, Status = Status.Active, FieldToBeIgnored = "cb85ede4-4a66-4ab5-813d-6f09b4781489", DateOfBirth = new DateOnly(1972,03,03) }, + new(){ FirstName = "Joe", LastName = "Doe", Gender = "M",Age = 26, Balance = 3000, Status = Status.Inactive, FieldToBeIgnored = "0725a26f-1b5c-4659-be06-2b4b108a2fb4", DateOfBirth = new DateOnly(1981,12,05) }, + new(){ FirstName = "Jill", LastName = "Doe", Gender = "F",Age = 24, Balance = 4000, Status = Status.Inactive, FieldToBeIgnored = "bb85d60c-96fa-4137-a9f1-e09ec0497f5d", DateOfBirth = new DateOnly(1980,05,29) }, + new(){ FirstName = "Jack", LastName = "Doe", Gender = "M",Age = 22, Balance = 5000, Status = Status.Active, FieldToBeIgnored = "76471dfe-2efd-4ec5-b192-82abc1b05c72", DateOfBirth = new DateOnly(1990,09,10) }, + new(){ FirstName = "Jen", LastName = "Doe",Gender = "F", Age = 20, Balance = 6000, Status = Status.Active, FieldToBeIgnored = "be83a3c0-9636-4ebd-acca-08e6ffb5c469", DateOfBirth = new DateOnly(2000,01,01) }, }; } \ No newline at end of file diff --git a/Documentation/Blazorise.Docs/Models/Snippets.generated.cs b/Documentation/Blazorise.Docs/Models/Snippets.generated.cs index aa5f0b3b89..cfd6d0b4c3 100644 --- a/Documentation/Blazorise.Docs/Models/Snippets.generated.cs +++ b/Documentation/Blazorise.Docs/Models/Snippets.generated.cs @@ -6761,10 +6761,10 @@ private Task OnPredefinedClicked() => dataGrid.ApplySorting( public const string DataGridAutoGenerateColumnsExample = @"@using System.ComponentModel.DataAnnotations + Data=""data"" + Responsive + ShowPager + ShowPageSizes Editable> @@ -6772,17 +6772,39 @@ ShowPageSizes Editable> public class Example { + [Order( DisplayOrder = 1, EditOrder = 2 )] [Display( Name = ""Name"" )] public string FirstName { get; set; } + [Order( DisplayOrder = 2, EditOrder = 3 )] public string LastName { get; set; } - + [Order( DisplayOrder = 3, EditOrder = 4 )] + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 1 )] public int Age { get; set; } + [Order( DisplayOrder = 5, EditOrder = 0 )] + public Status Status { get; set; } + + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 100 )] + [Order( DisplayOrder = 4, EditOrder = 1 )] public decimal Balance { get; set; } - public Status Status { get; set; } + [IgnoreField] + public string FieldToBeIgnored { get; set; } + + [Order( DisplayOrder = 5, EditOrder = 0 )] + [Display( Name = ""Gender"" )] + [Select( GetDataFunction = ""GetGenders"", TextField = nameof( Blazorise.Shared.Data.Gender.Description ), ValueField = nameof( Blazorise.Shared.Data.Gender.Code ) )] + public string Gender { get; set; } + + [Order( DisplayOrder = 6, EditOrder = 6 )] + [Display( Name = ""DOB"" )] + [Date( InputMode = DateInputMode.Date )] + public DateOnly DateOfBirth { get; set; } + + public IEnumerable GetGenders() + => EmployeeData.Genders; } public enum Status @@ -6793,12 +6815,12 @@ public enum Status private IEnumerable data = new List() { - new(){ FirstName = ""John"", LastName = ""Doe"", Age = 30, Balance = 1000, Status = Status.Active }, - new(){ FirstName = ""Jane"", LastName = ""Doe"", Age = 28, Balance = 2000, Status = Status.Active }, - new(){ FirstName = ""Joe"", LastName = ""Doe"", Age = 26, Balance = 3000, Status = Status.Inactive }, - new(){ FirstName = ""Jill"", LastName = ""Doe"", Age = 24, Balance = 4000, Status = Status.Inactive }, - new(){ FirstName = ""Jack"", LastName = ""Doe"", Age = 22, Balance = 5000, Status = Status.Active }, - new(){ FirstName = ""Jen"", LastName = ""Doe"", Age = 20, Balance = 6000, Status = Status.Active }, + new(){ FirstName = ""John"", LastName = ""Doe"", Gender = ""M"", Age = 30, Balance = 1000, Status = Status.Active, FieldToBeIgnored = ""4a92b1ea-e82d-4920-8d22-198a2385945e"", DateOfBirth = new DateOnly(1992,03,05) }, + new(){ FirstName = ""Jane"", LastName = ""Doe"", Gender = ""F"",Age = 28, Balance = 2000, Status = Status.Active, FieldToBeIgnored = ""cb85ede4-4a66-4ab5-813d-6f09b4781489"", DateOfBirth = new DateOnly(1972,03,03) }, + new(){ FirstName = ""Joe"", LastName = ""Doe"", Gender = ""M"",Age = 26, Balance = 3000, Status = Status.Inactive, FieldToBeIgnored = ""0725a26f-1b5c-4659-be06-2b4b108a2fb4"", DateOfBirth = new DateOnly(1981,12,05) }, + new(){ FirstName = ""Jill"", LastName = ""Doe"", Gender = ""F"",Age = 24, Balance = 4000, Status = Status.Inactive, FieldToBeIgnored = ""bb85d60c-96fa-4137-a9f1-e09ec0497f5d"", DateOfBirth = new DateOnly(1980,05,29) }, + new(){ FirstName = ""Jack"", LastName = ""Doe"", Gender = ""M"",Age = 22, Balance = 5000, Status = Status.Active, FieldToBeIgnored = ""76471dfe-2efd-4ec5-b192-82abc1b05c72"", DateOfBirth = new DateOnly(1990,09,10) }, + new(){ FirstName = ""Jen"", LastName = ""Doe"",Gender = ""F"", Age = 20, Balance = 6000, Status = Status.Active, FieldToBeIgnored = ""be83a3c0-9636-4ebd-acca-08e6ffb5c469"", DateOfBirth = new DateOnly(2000,01,01) }, }; }"; diff --git a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Code/DataGridAutoGenerateColumnsExampleCode.html b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Code/DataGridAutoGenerateColumnsExampleCode.html index 3a9867b31e..a6b614415e 100644 --- a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Code/DataGridAutoGenerateColumnsExampleCode.html +++ b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Code/DataGridAutoGenerateColumnsExampleCode.html @@ -3,10 +3,10 @@ @using System.ComponentModel.DataAnnotations <DataGrid TItem="Example" - Data="data" - Responsive - ShowPager - ShowPageSizes Editable> + Data="data" + Responsive + ShowPager + ShowPageSizes Editable> <DataGridCommandColumn TItem="Example" /> </DataGrid> @@ -15,17 +15,39 @@ public class Example { + [Order( DisplayOrder = 1, EditOrder = 2 )] [Display( Name = "Name" )] public string FirstName { get; set; } + [Order( DisplayOrder = 2, EditOrder = 3 )] public string LastName { get; set; } - + [Order( DisplayOrder = 3, EditOrder = 4 )] + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 1 )] public int Age { get; set; } + [Order( DisplayOrder = 5, EditOrder = 0 )] + public Status Status { get; set; } + + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 100 )] + [Order( DisplayOrder = 4, EditOrder = 1 )] public decimal Balance { get; set; } - public Status Status { get; set; } + [IgnoreField] + public string FieldToBeIgnored { get; set; } + + [Order( DisplayOrder = 5, EditOrder = 0 )] + [Display( Name = "Gender" )] + [Select( GetDataFunction = "GetGenders", TextField = nameof( Blazorise.Shared.Data.Gender.Description ), ValueField = nameof( Blazorise.Shared.Data.Gender.Code ) )] + public string Gender { get; set; } + + [Order( DisplayOrder = 6, EditOrder = 6 )] + [Display( Name = "DOB" )] + [Date( InputMode = DateInputMode.Date )] + public DateOnly DateOfBirth { get; set; } + + public IEnumerable<Gender> GetGenders() + => EmployeeData.Genders; } public enum Status @@ -36,12 +58,12 @@ private IEnumerable<Example> data = new List<Example>() { - new(){ FirstName = "John", LastName = "Doe", Age = 30, Balance = 1000, Status = Status.Active }, - new(){ FirstName = "Jane", LastName = "Doe", Age = 28, Balance = 2000, Status = Status.Active }, - new(){ FirstName = "Joe", LastName = "Doe", Age = 26, Balance = 3000, Status = Status.Inactive }, - new(){ FirstName = "Jill", LastName = "Doe", Age = 24, Balance = 4000, Status = Status.Inactive }, - new(){ FirstName = "Jack", LastName = "Doe", Age = 22, Balance = 5000, Status = Status.Active }, - new(){ FirstName = "Jen", LastName = "Doe", Age = 20, Balance = 6000, Status = Status.Active }, + new(){ FirstName = "John", LastName = "Doe", Gender = "M", Age = 30, Balance = 1000, Status = Status.Active, FieldToBeIgnored = "4a92b1ea-e82d-4920-8d22-198a2385945e", DateOfBirth = new DateOnly(1992,03,05) }, + new(){ FirstName = "Jane", LastName = "Doe", Gender = "F",Age = 28, Balance = 2000, Status = Status.Active, FieldToBeIgnored = "cb85ede4-4a66-4ab5-813d-6f09b4781489", DateOfBirth = new DateOnly(1972,03,03) }, + new(){ FirstName = "Joe", LastName = "Doe", Gender = "M",Age = 26, Balance = 3000, Status = Status.Inactive, FieldToBeIgnored = "0725a26f-1b5c-4659-be06-2b4b108a2fb4", DateOfBirth = new DateOnly(1981,12,05) }, + new(){ FirstName = "Jill", LastName = "Doe", Gender = "F",Age = 24, Balance = 4000, Status = Status.Inactive, FieldToBeIgnored = "bb85d60c-96fa-4137-a9f1-e09ec0497f5d", DateOfBirth = new DateOnly(1980,05,29) }, + new(){ FirstName = "Jack", LastName = "Doe", Gender = "M",Age = 22, Balance = 5000, Status = Status.Active, FieldToBeIgnored = "76471dfe-2efd-4ec5-b192-82abc1b05c72", DateOfBirth = new DateOnly(1990,09,10) }, + new(){ FirstName = "Jen", LastName = "Doe",Gender = "F", Age = 20, Balance = 6000, Status = Status.Active, FieldToBeIgnored = "be83a3c0-9636-4ebd-acca-08e6ffb5c469", DateOfBirth = new DateOnly(2000,01,01) }, }; } diff --git a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Examples/DataGridAutoGenerateColumnsExample.razor b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Examples/DataGridAutoGenerateColumnsExample.razor index f78238ff13..61be1c08bc 100644 --- a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Examples/DataGridAutoGenerateColumnsExample.razor +++ b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Examples/DataGridAutoGenerateColumnsExample.razor @@ -2,10 +2,10 @@ @using System.ComponentModel.DataAnnotations + Data="data" + Responsive + ShowPager + ShowPageSizes Editable> @@ -13,17 +13,39 @@ public class Example { + [Order( DisplayOrder = 1, EditOrder = 2 )] [Display( Name = "Name" )] public string FirstName { get; set; } + [Order( DisplayOrder = 2, EditOrder = 3 )] public string LastName { get; set; } - + [Order( DisplayOrder = 3, EditOrder = 4 )] + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 1 )] public int Age { get; set; } + [Order( DisplayOrder = 5, EditOrder = 0 )] + public Status Status { get; set; } + + [Numeric( EnableStep = true, ShowStepButtons = true, Step = 100 )] + [Order( DisplayOrder = 4, EditOrder = 1 )] public decimal Balance { get; set; } - public Status Status { get; set; } + [IgnoreField] + public string FieldToBeIgnored { get; set; } + + [Order( DisplayOrder = 5, EditOrder = 0 )] + [Display( Name = "Gender" )] + [Select( GetDataFunction = "GetGenders", TextField = nameof( Blazorise.Shared.Data.Gender.Description ), ValueField = nameof( Blazorise.Shared.Data.Gender.Code ) )] + public string Gender { get; set; } + + [Order( DisplayOrder = 6, EditOrder = 6 )] + [Display( Name = "DOB" )] + [Date( InputMode = DateInputMode.Date )] + public DateOnly DateOfBirth { get; set; } + + public IEnumerable GetGenders() + => EmployeeData.Genders; } public enum Status @@ -34,12 +56,12 @@ private IEnumerable data = new List() { - new(){ FirstName = "John", LastName = "Doe", Age = 30, Balance = 1000, Status = Status.Active }, - new(){ FirstName = "Jane", LastName = "Doe", Age = 28, Balance = 2000, Status = Status.Active }, - new(){ FirstName = "Joe", LastName = "Doe", Age = 26, Balance = 3000, Status = Status.Inactive }, - new(){ FirstName = "Jill", LastName = "Doe", Age = 24, Balance = 4000, Status = Status.Inactive }, - new(){ FirstName = "Jack", LastName = "Doe", Age = 22, Balance = 5000, Status = Status.Active }, - new(){ FirstName = "Jen", LastName = "Doe", Age = 20, Balance = 6000, Status = Status.Active }, + new(){ FirstName = "John", LastName = "Doe", Gender = "M", Age = 30, Balance = 1000, Status = Status.Active, FieldToBeIgnored = "4a92b1ea-e82d-4920-8d22-198a2385945e", DateOfBirth = new DateOnly(1992,03,05) }, + new(){ FirstName = "Jane", LastName = "Doe", Gender = "F",Age = 28, Balance = 2000, Status = Status.Active, FieldToBeIgnored = "cb85ede4-4a66-4ab5-813d-6f09b4781489", DateOfBirth = new DateOnly(1972,03,03) }, + new(){ FirstName = "Joe", LastName = "Doe", Gender = "M",Age = 26, Balance = 3000, Status = Status.Inactive, FieldToBeIgnored = "0725a26f-1b5c-4659-be06-2b4b108a2fb4", DateOfBirth = new DateOnly(1981,12,05) }, + new(){ FirstName = "Jill", LastName = "Doe", Gender = "F",Age = 24, Balance = 4000, Status = Status.Inactive, FieldToBeIgnored = "bb85d60c-96fa-4137-a9f1-e09ec0497f5d", DateOfBirth = new DateOnly(1980,05,29) }, + new(){ FirstName = "Jack", LastName = "Doe", Gender = "M",Age = 22, Balance = 5000, Status = Status.Active, FieldToBeIgnored = "76471dfe-2efd-4ec5-b192-82abc1b05c72", DateOfBirth = new DateOnly(1990,09,10) }, + new(){ FirstName = "Jen", LastName = "Doe",Gender = "F", Age = 20, Balance = 6000, Status = Status.Active, FieldToBeIgnored = "be83a3c0-9636-4ebd-acca-08e6ffb5c469", DateOfBirth = new DateOnly(2000,01,01) }, }; } \ No newline at end of file diff --git a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Features/AutoGenerateColumnsPage.razor b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Features/AutoGenerateColumnsPage.razor index 9d84ff421f..aa271c63ff 100644 --- a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Features/AutoGenerateColumnsPage.razor +++ b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/DataGrid/Features/AutoGenerateColumnsPage.razor @@ -17,6 +17,29 @@ If no columns are defined, the DataGrid will automatically generate columns based on the TItem type. + + An attribute based API is provided to specify additional metadata that is used to specify how the columns will be built. + + + + [DisplayAttribute] - Represents an attribute that can be applied to properties or fields to specify the caption. + + + [OrderAttribute] - Represents an attribute that can be applied to properties or fields to specify the order in which they should be displayed or edited. + + + [IgnoreFieldAttribute] - Represents an attribute that can be applied to properties or fields to specify they should not be automatically generated. + + + [SelectAttribute] - Represents an attribute that can be applied to properties or fields to specify additional metadata for a select component. + + + [NumericAttribute] - Represents an attribute that can be applied to numeric properties or fields to specify additional metadata. + + + [DateAttribute] - Represents an attribute that can be applied to date properties or fields to specify additional metadata. + + Example diff --git a/Documentation/Blazorise.Docs/Pages/News/2024-07-15-release-notes-160.razor b/Documentation/Blazorise.Docs/Pages/News/2024-07-15-release-notes-160.razor index eb611d762d..8e6a190fd6 100644 --- a/Documentation/Blazorise.Docs/Pages/News/2024-07-15-release-notes-160.razor +++ b/Documentation/Blazorise.Docs/Pages/News/2024-07-15-release-notes-160.razor @@ -20,18 +20,18 @@ Here's a summary of what's new in this release: - - + + : - - + + : - - + + Upgrading from 1.5.x to 1.6 👨‍🔧 @@ -59,13 +59,34 @@ - + DataGrid - + In the DataGrid AutoGenerateColumns feature, we've added new attributes to the attribute based API to be able to further configure the way that the columns are rendered. + + + [DisplayAttribute] - Represents an attribute that can be applied to properties or fields to specify the caption. + + + [OrderAttribute] - Represents an attribute that can be applied to properties or fields to specify the order in which they should be displayed or edited. + + + [IgnoreFieldAttribute] - Represents an attribute that can be applied to properties or fields to specify they should not be automatically generated. + + + [SelectAttribute] - Represents an attribute that can be applied to properties or fields to specify additional metadata for a select component. + + + [NumericAttribute] - Represents an attribute that can be applied to numeric properties or fields to specify additional metadata. + + + [DateAttribute] - Represents an attribute that can be applied to date properties or fields to specify additional metadata. + + + Wrap Up diff --git a/Source/Blazorise/Attributes/DateAttribute.cs b/Source/Blazorise/Attributes/DateAttribute.cs new file mode 100644 index 0000000000..0a339a28ed --- /dev/null +++ b/Source/Blazorise/Attributes/DateAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Blazorise; + +/// +/// Represents an attribute that can be applied to date properties or fields to specify additional metadata. +/// +[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )] +public sealed class DateAttribute : Attribute +{ + /// + /// Hints at the type of data that might be entered by the user while editing the element or its contents. + /// + public DateInputMode InputMode { get; set; } +} diff --git a/Source/Blazorise/Attributes/IgnoreFieldAttribute.cs b/Source/Blazorise/Attributes/IgnoreFieldAttribute.cs new file mode 100644 index 0000000000..a7557ab5c7 --- /dev/null +++ b/Source/Blazorise/Attributes/IgnoreFieldAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Blazorise; + +/// +/// Represents an attribute that can be applied to properties or fields to specify they should not be automatically generated. +/// +[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )] +public sealed class IgnoreFieldAttribute : Attribute +{ +} diff --git a/Source/Blazorise/Attributes/NumericAttribute.cs b/Source/Blazorise/Attributes/NumericAttribute.cs new file mode 100644 index 0000000000..cbb05b8f65 --- /dev/null +++ b/Source/Blazorise/Attributes/NumericAttribute.cs @@ -0,0 +1,43 @@ +using System; + +namespace Blazorise; + +/// +/// Represents an attribute that can be applied to numeric properties or fields to specify additional metadata. +/// +[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )] +public sealed class NumericAttribute : Attribute +{ + /// + /// Specifies the interval between valid values. + /// + public int Step { get; set; } = 1; + + /// + /// Maximum number of decimal places after the decimal separator. + /// + public int Decimals { get; set; } = 2; + + /// + /// String to use as the decimal separator in numeric values. + /// + public string DecimalSeparator { get; set; } = "."; + + /// + /// Helps define the language of an element. + /// + /// + /// https://www.w3schools.com/tags/ref_language_codes.asp + /// + public string Culture { get; set; } + + /// + /// If true, step buttons will be visible. + /// + public bool ShowStepButtons { get; set; } = true; + + /// + /// If true, enables change of numeric value by pressing on step buttons or by keyboard up/down keys. + /// + public bool EnableStep { get; set; } = true; +} diff --git a/Source/Blazorise/Attributes/OrderAttribute.cs b/Source/Blazorise/Attributes/OrderAttribute.cs new file mode 100644 index 0000000000..c26a023d2c --- /dev/null +++ b/Source/Blazorise/Attributes/OrderAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Blazorise; + +/// +/// Represents an attribute that can be applied to properties or fields to specify the order in which they should be displayed or edited. +/// +[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )] +public sealed class OrderAttribute : Attribute +{ + /// + /// The order in which the field should be displayed. + /// + public int DisplayOrder { get; set; } + + /// + /// The order in which the field should be edited. + /// + public int EditOrder { get; set; } +} \ No newline at end of file diff --git a/Source/Blazorise/Attributes/SelectAttribute.cs b/Source/Blazorise/Attributes/SelectAttribute.cs new file mode 100644 index 0000000000..1347af77b3 --- /dev/null +++ b/Source/Blazorise/Attributes/SelectAttribute.cs @@ -0,0 +1,35 @@ +using System; + +namespace Blazorise; + +/// +/// Represents an attribute that can be applied to properties or fields to specify additional metadata for a select component. +/// +[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )] +public sealed class SelectAttribute : Attribute +{ + /// + /// The name of the function that will be used to get the data source. + /// By default it's set to "GetSelectData". + /// + public string GetDataFunction { get; set; } = "GetSelectData"; + + /// + /// Used to get the display field from the supplied data source. + /// Defaults to "Text". + /// + public string TextField { get; set; } = "Text"; + + /// + /// Used to get the value field from the supplied data source. + /// Defaults to "Value". + /// + public string ValueField { get; set; } = "Value"; + + /// + /// Specifies how many options should be shown at once. + /// 0 means that all options will be shown. + /// Defaults to 0. + /// + public int MaxVisibleItems { get; set; } = 0; +} diff --git a/Source/Blazorise/Utilities/Code/ReflectionHelper.cs b/Source/Blazorise/Utilities/Code/ReflectionHelper.cs index 13a6668599..3b83adf9ff 100644 --- a/Source/Blazorise/Utilities/Code/ReflectionHelper.cs +++ b/Source/Blazorise/Utilities/Code/ReflectionHelper.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Reflection; @@ -20,6 +21,48 @@ public static PropertyInfo[] GetPublicProperties() return properties; } + /// + /// Gets a method by name. + /// + /// + /// + /// + public static MethodInfo GetStaticMethod( Type type, string methodName ) + { + return type.GetMethod( methodName, BindingFlags.Public | BindingFlags.Static ); + } + + /// + /// Gets a method by name. + /// + /// + /// + public static MethodInfo GetStaticMethod( string methodName ) + { + return GetStaticMethod( typeof( T ), methodName ); + } + + /// + /// Gets a method by name. + /// + /// + /// + /// + public static MethodInfo GetMethod( Type type, string methodName ) + { + return type.GetMethod( methodName, BindingFlags.Public | BindingFlags.Instance ); + } + + /// + /// Gets a method by name. + /// + /// + /// + public static MethodInfo GetMethod( string methodName ) + { + return GetMethod( typeof( T ), methodName ); + } + /// /// Based on this particular property, tries to resolve a caption out of the attributes. /// @@ -41,4 +84,71 @@ public static string ResolveCaption( PropertyInfo propertyInfo ) return Formaters.PascalCaseToFriendlyName( propertyInfo.Name ); } + + /// + /// Based on this particular property, resolves whether this property should be ignored. + /// + /// + /// + public static bool ResolveIsIgnore( PropertyInfo propertyInfo ) + { + var ignoreAttribute = propertyInfo.GetCustomAttribute(); + return ignoreAttribute is not null; + } + + /// + /// Based on this particular property, resolves the display order. + /// + /// + /// + public static int ResolveDisplayOrder( PropertyInfo propertyInfo ) + { + var orderAttribute = propertyInfo.GetCustomAttribute(); + return orderAttribute?.DisplayOrder ?? default; + } + + /// + /// Based on this particular property, resolves the edit order. + /// + /// + /// + public static int? ResolveEditOrder( PropertyInfo propertyInfo ) + { + var orderAttribute = propertyInfo.GetCustomAttribute(); + return orderAttribute?.EditOrder; + } + + /// + /// Based on this particular property, resolves a numeric attribute. + /// + /// + /// + public static NumericAttribute ResolveNumericAttribute( PropertyInfo propertyInfo ) + { + var attribute = propertyInfo.GetCustomAttribute(); + return attribute; + } + + /// + /// Based on this particular property, resolves a select attribute. + /// + /// + /// + public static SelectAttribute ResolveSelectAttribute( PropertyInfo propertyInfo ) + { + var attribute = propertyInfo.GetCustomAttribute(); + return attribute; + } + + /// + /// Based on this particular property, resolves a date attribute. + /// + /// + /// + public static DateAttribute ResolveDateAttribute( PropertyInfo propertyInfo ) + { + var attribute = propertyInfo.GetCustomAttribute(); + return attribute; + } + } diff --git a/Source/Blazorise/Utilities/Expressions/ExpressionCompiler.cs b/Source/Blazorise/Utilities/Expressions/ExpressionCompiler.cs index 1a1865f7d4..32ec4f14db 100644 --- a/Source/Blazorise/Utilities/Expressions/ExpressionCompiler.cs +++ b/Source/Blazorise/Utilities/Expressions/ExpressionCompiler.cs @@ -28,9 +28,19 @@ public static T GetProperty( object instance, string propertyName ) /// /// public static Func CreatePropertyGetter( object instance, string propertyName ) + => CreatePropertyGetter( instance.GetType(), propertyName ); + + /// + /// Generates a function getter for a property in an unknown instance. + /// + /// + /// + /// + /// + public static Func CreatePropertyGetter( Type instanceType, string propertyName ) { var parameterExp = Expression.Parameter( typeof( object ), "instance" ); - var castExp = Expression.TypeAs( parameterExp, instance.GetType() ); + var castExp = Expression.TypeAs( parameterExp, instanceType ); var property = Expression.Property( castExp, propertyName ); return Expression.Lambda>( Expression.Convert( property, typeof( T ) ), parameterExp ).Compile(); diff --git a/Source/Extensions/Blazorise.DataGrid/DataGrid.razor.cs b/Source/Extensions/Blazorise.DataGrid/DataGrid.razor.cs index 0d2be41032..905dd2e6e1 100644 --- a/Source/Extensions/Blazorise.DataGrid/DataGrid.razor.cs +++ b/Source/Extensions/Blazorise.DataGrid/DataGrid.razor.cs @@ -518,14 +518,18 @@ protected override async Task OnAfterRenderAsync( bool firstRender ) /// private void AutomaticallyGenerateColumns() { - var properties = ReflectionHelper.GetPublicProperties(); - foreach ( var property in properties ) + foreach ( var property in ReflectionHelper.GetPublicProperties() ) { if ( !( property.PropertyType.IsValueType || property.PropertyType == typeof( string ) ) ) { continue; } + if ( ReflectionHelper.ResolveIsIgnore( property ) ) + { + continue; + } + DataGridColumn column; if ( property.PropertyType.IsEnum ) @@ -539,11 +543,46 @@ private void AutomaticallyGenerateColumns() ValueField = x => x, }; } + else if ( ReflectionHelper.ResolveNumericAttribute( property ) is NumericAttribute numeric ) + { + var numericColumn = new DataGridNumericColumn(); + numericColumn.Step = numeric.Step; + numericColumn.Decimals = numeric.Decimals; + numericColumn.DecimalSeparator = numeric.DecimalSeparator; + numericColumn.Culture = numeric.Culture; + numericColumn.ShowStepButtons = numeric.ShowStepButtons; + numericColumn.EnableStep = numeric.EnableStep; + column = numericColumn; + } + else if ( ReflectionHelper.ResolveSelectAttribute( property ) is SelectAttribute select ) + { + var selectColumn = new DataGridSelectColumn(); + var selectGetDataStatic = ReflectionHelper.GetStaticMethod( select.GetDataFunction ); + var selectGetData = selectGetDataStatic is null ? ReflectionHelper.GetMethod( select.GetDataFunction ) : null; + var data = selectGetDataStatic?.Invoke( null, null ) ?? selectGetData?.Invoke( CreateNewItem(), null ); + selectColumn.Data = (IEnumerable)data; + var genericType = data?.GetType()?.GenericTypeArguments.Length > 0 ? data?.GetType()?.GenericTypeArguments[0] : null; + if ( genericType is not null ) + { + selectColumn.TextField = ExpressionCompiler.CreatePropertyGetter( genericType, select.TextField ); + selectColumn.ValueField = ExpressionCompiler.CreatePropertyGetter( genericType, select.ValueField ); + } + selectColumn.MaxVisibleItems = select.MaxVisibleItems == 0 ? null : select.MaxVisibleItems; + column = selectColumn; + } + else if ( ReflectionHelper.ResolveDateAttribute( property ) is DateAttribute date ) + { + var dateColumn = new DataGridDateColumn(); + dateColumn.InputMode = date.InputMode; + column = dateColumn; + } else { column = new DataGridColumn(); } + column.DisplayOrder = ReflectionHelper.ResolveDisplayOrder( property ); + column.EditOrder = ReflectionHelper.ResolveEditOrder( property ); column.Editable = property.SetMethod is not null; column.Caption = ReflectionHelper.ResolveCaption( property ); column.Field = property.Name; @@ -953,7 +992,7 @@ public Task New() VirtualizeScrollToTop(); } - TItem newItem = NewItemCreator != null ? NewItemCreator.Invoke() : CreateNewItem(); + TItem newItem = CreateNewItem(); NewItemDefaultSetter?.Invoke( newItem ); @@ -1675,7 +1714,7 @@ public ValueTask ScrollToRow( int row ) /// /// Return new instance of TItem. private TItem CreateNewItem() - => newItemCreator.Value(); + => NewItemCreator is not null ? NewItemCreator.Invoke() : newItemCreator.Value(); /// /// Prepares edit item and it's cell values for editing. diff --git a/Source/Extensions/Blazorise.DataGrid/GlobalSuppressions.cs b/Source/Extensions/Blazorise.DataGrid/GlobalSuppressions.cs index 9aed776af8..97f7302ede 100644 --- a/Source/Extensions/Blazorise.DataGrid/GlobalSuppressions.cs +++ b/Source/Extensions/Blazorise.DataGrid/GlobalSuppressions.cs @@ -5,4 +5,4 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage( "Usage", "BL0005:Component parameter should not be set outside of its component.", Justification = "Internal setting, not critical.", Scope = "member", Target = "~M:Blazorise.DataGrid.DataGrid`1.AutoGenerateColumns" )] +[assembly: SuppressMessage( "Usage", "BL0005:Component parameter should not be set outside of its component.", Justification = "Internal setting, not critical.", Scope = "member", Target = "~M:Blazorise.DataGrid.DataGrid`1.AutomaticallyGenerateColumns" )]