MOVe.Razor
2.0.1
dotnet add package MOVe.Razor --version 2.0.1
NuGet\Install-Package MOVe.Razor -Version 2.0.1
<PackageReference Include="MOVe.Razor" Version="2.0.1" />
<PackageVersion Include="MOVe.Razor" Version="2.0.1" />
<PackageReference Include="MOVe.Razor" />
paket add MOVe.Razor --version 2.0.1
#r "nuget: MOVe.Razor, 2.0.1"
#:package MOVe.Razor@2.0.1
#addin nuget:?package=MOVe.Razor&version=2.0.1
#tool nuget:?package=MOVe.Razor&version=2.0.1
MOVe.Razor - Reusable Blazor Component Library
MOVe.Razor is a self-contained, generic Blazor component library built for .NET 9.0 with Syncfusion UI components. It provides an attribute-based metadata system for automatic UI generation, enabling rapid development of data-driven applications.
Features
Core Capabilities
- Attribute-Based UI Generation: Define UI behavior declaratively using C# attributes on your model properties
- Generic Syncfusion Components: Pre-configured wrappers for Syncfusion Blazor components (grids, forms, cards, filters)
- Metadata Service: Automatic extraction and processing of UI metadata from model classes
- Zero External Dependencies: Fully self-contained with its own attributes, enums, and models
- Highly Reusable: Designed to work in any Blazor WebAssembly project
Included Components
- SfGridComponent<TItem>: Attribute-driven Syncfusion DataGrid with automatic column configuration
- SfFormComponent<TItem>: Dynamic form generator based on FormField attributes
- SfCardComponent<TItem>: Card layout with metadata-driven display sections
- SfFilterComponent<TItem>: Advanced filtering panel with quick and advanced filters
- PaginationControls: Customizable pagination with page size selection
- LoadingSpinnerComponent: Elegant loading states
- StatsCardComponent: KPI and metric display cards
- DataExportButton: Excel/PDF export functionality
- ConfirmDialog: Modal confirmation dialogs
- ToastContainer: Toast notification system
Attribute System
MOVe.Razor includes a complete set of UI attributes:
- GridColumnAttribute: Configure DataGrid columns (header, width, format, visibility, sorting, filtering, etc.)
- FormFieldAttribute: Define form fields (label, type, validation, placeholder, groups, sections, etc.)
- FilterFieldAttribute: Set up filter controls (filter type, operators, data sources, display order, etc.)
- CardDisplayAttribute: Control card display sections (header, content, footer)
- UIDisplayAttribute: General UI metadata (display name, format, visibility)
- NotMappedAttribute: Mark properties as non-persistent
Enums
- UIColumnType: Text, Number, Date, DateTime, Boolean, Dropdown, Image, Link, Custom
- UIFilterType: Auto, Text, Number, Date, DateRange, Dropdown, MultiSelect, Boolean, Custom
- FormFieldType: Auto, TextBox, TextArea, NumericTextBox, DatePicker, Dropdown, CheckBox, etc.
- FilterOperator: Equal, NotEqual, Contains, StartsWith, GreaterThan, LessThan, Between, In, etc.
- CardSection: Header, Content, Footer
Installation
NuGet Package
dotnet add package MOVe.Razor --version 2.0.0
Or via Package Manager:
Install-Package MOVe.Razor -Version 2.0.0
Prerequisites
- .NET 9.0 or higher
- Blazor WebAssembly project
- Syncfusion.Blazor (v31.2.3 or compatible) - Requires valid Syncfusion license
Quick Start
1. Register Services
In your Program.cs:
using MOVe.Razor.Extensions;
using Syncfusion.Blazor;
// Register Syncfusion license
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR_LICENSE_KEY");
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// Add Syncfusion Blazor services
builder.Services.AddSyncfusionBlazor();
// Add MOVe.Razor services (includes MetadataService)
builder.Services.AddMOVeRazorServices();
await builder.Build().RunAsync();
2. Import Namespaces
Add to your _Imports.razor:
@using MOVe.Razor.Components
@using MOVe.Razor.Attributes
@using MOVe.Razor.Enums
@using MOVe.Razor.Models
@using MOVe.Razor.Services
@using Syncfusion.Blazor
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.Data
3. Define Your Model with Attributes
using MOVe.Razor.Attributes;
using MOVe.Razor.Enums;
public class ProductDTO
{
[GridColumn(HeaderText = "ID", Visible = false, IsPrimaryKey = true)]
public Guid Id { get; set; }
[GridColumn(HeaderText = "Product Code", Width = 150, AllowSorting = true)]
[FormField(Label = "Product Code", Required = true, DisplayOrder = 1)]
[FilterField(FilterType = UIFilterType.Text, ShowInQuickFilter = true)]
[CardDisplay(IsPrimary = true, Section = CardSection.Header)]
public string ProductCode { get; set; } = string.Empty;
[GridColumn(HeaderText = "Name", Width = 250)]
[FormField(Label = "Product Name", Required = true, DisplayOrder = 2)]
[FilterField(FilterType = UIFilterType.Text, ShowInQuickFilter = true)]
[CardDisplay(Section = CardSection.Header)]
public string ProductName { get; set; } = string.Empty;
[GridColumn(HeaderText = "Price", Width = 120, Format = "C2", TextAlign = TextAlign.Right)]
[FormField(Label = "Unit Price", FieldType = FormFieldType.NumericTextBox, DisplayOrder = 3)]
[FilterField(FilterType = UIFilterType.Number)]
[CardDisplay(Section = CardSection.Content, Label = "Price")]
public decimal Price { get; set; }
[GridColumn(HeaderText = "Stock", Width = 100, TextAlign = TextAlign.Center)]
[FormField(Label = "In Stock", FieldType = FormFieldType.NumericTextBox, DisplayOrder = 4)]
[FilterField(FilterType = UIFilterType.Number)]
public int Stock { get; set; }
[GridColumn(HeaderText = "Active", Width = 100, Type = UIColumnType.Boolean)]
[FormField(Label = "Is Active", FieldType = FormFieldType.CheckBox, DisplayOrder = 5)]
[FilterField(FilterType = UIFilterType.Boolean, ShowInQuickFilter = true)]
public bool IsActive { get; set; }
[GridColumn(HeaderText = "Created", Width = 150, Format = "dd-MMM-yy")]
[FormField(Label = "Created Date", FieldType = FormFieldType.DatePicker, ReadOnly = true)]
[FilterField(FilterType = UIFilterType.DateRange)]
[CardDisplay(Section = CardSection.Footer, Label = "Created")]
public DateTime CreatedDate { get; set; }
}
4. Use Components in Your Page
@page "/products"
@inject HttpClient Http
<PageTitle>Products</PageTitle>
<div class="container-fluid">
<h3>Product Catalog</h3>
@* Filter Panel *@
<SfFilterComponent TItem="ProductDTO"
DataSources="@filterDataSources"
OnFiltersApplied="OnFiltersApplied"
AutoApply="true" />
@* Data Grid *@
<SfGridComponent TItem="ProductDTO"
Data="@products"
AllowPaging="true"
AllowSorting="true"
AllowFiltering="true"
OnRowSelected="OnProductSelected"
OnAdd="OnAddProduct"
OnEdit="OnEditProduct"
OnDelete="OnDeleteProduct" />
@* Pagination *@
<PaginationControls PagedResult="@pagedResult"
OnPageChanged="OnPageChanged"
InitialPageSize="20" />
</div>
@code {
private List<ProductDTO> products = new();
private PagedResult<ProductDTO> pagedResult = new();
private Dictionary<string, object> filterDataSources = new();
protected override async Task OnInitializedAsync()
{
await LoadProducts();
}
private async Task LoadProducts()
{
var response = await Http.GetFromJsonAsync<List<ProductDTO>>("api/products");
products = response ?? new();
pagedResult = new PagedResult<ProductDTO>(
products.Take(20).ToList(),
products.Count,
1,
20
);
}
private async Task OnFiltersApplied(Dictionary<string, SfFilterComponent<ProductDTO>.FilterValue> filters)
{
// Apply filters to your data source
await LoadProducts();
}
private async Task OnPageChanged((int PageNumber, int PageSize) pageInfo)
{
// Handle page change
pagedResult = new PagedResult<ProductDTO>(
products.Skip((pageInfo.PageNumber - 1) * pageInfo.PageSize).Take(pageInfo.PageSize).ToList(),
products.Count,
pageInfo.PageNumber,
pageInfo.PageSize
);
}
private void OnProductSelected(ProductDTO product)
{
// Handle row selection
}
private async Task OnAddProduct()
{
// Handle add
}
private async Task OnEditProduct(ProductDTO product)
{
// Handle edit
}
private async Task OnDeleteProduct(ProductDTO product)
{
// Handle delete
}
}
Advanced Usage
Custom Filter Data Sources
private Dictionary<string, object> filterDataSources = new()
{
{ "Categories", new List<object>
{
new { Value = "Electronics", Text = "Electronics" },
new { Value = "Clothing", Text = "Clothing" },
new { Value = "Food", Text = "Food" }
}
},
{ "Suppliers", suppliersList }
};
Metadata Service
Access metadata programmatically:
@inject MetadataService MetadataService
@code {
private void InspectMetadata()
{
var metadata = MetadataService.GetMetadata<ProductDTO>();
foreach (var prop in metadata.Properties)
{
Console.WriteLine($"{prop.PropertyName}: {prop.GridColumn.HeaderText}");
}
}
}
Breaking Changes from v1.x
v2.0.0 brings significant architectural changes:
Namespace Changes: All namespaces now use
MOVe.Razor.*instead ofMOVe.Shared.DTOs.*MOVe.Shared.DTOs.Attributes→MOVe.Razor.AttributesMOVe.Shared.DTOs.Enums→MOVe.Razor.EnumsMOVe.Shared.Contracts.Pagination→MOVe.Razor.Models
Enum Renames (to avoid Syncfusion conflicts):
ColumnType→UIColumnTypeFilterType→UIFilterType
Self-Contained: No longer depends on
MOVe.Shared.DTOs,MOVe.Shared.Contracts, orMOVe.DomainRemoved Components: MOVe-specific components (ProjectSelector, RiskMatrix, etc.) are excluded from the package
Removed Services: Azure Storage, Microsoft Graph, and Excel services are not included (add separately if needed)
Migration Guide
Update your using statements:
// Old (v1.x)
using MOVe.Shared.DTOs.Attributes;
using MOVe.Shared.DTOs.Enums;
// New (v2.0)
using MOVe.Razor.Attributes;
using MOVe.Razor.Enums;
Update enum usages:
// Old
[GridColumn(Type = ColumnType.Number)]
[FilterField(FilterType = FilterType.Date)]
// New
[GridColumn(Type = UIColumnType.Number)]
[FilterField(FilterType = UIFilterType.Date)]
Update PagedResult:
// Old
using MOVe.Shared.Contracts.Pagination;
// New
using MOVe.Razor.Models;
License
MIT License - See LICENSE file for details.
Dependencies
- Microsoft.AspNetCore.Components.Web (9.0.10)
- Syncfusion.Blazor (31.2.3)
- Syncfusion.Blazor.Themes (31.2.3)
- Microsoft.Extensions.Logging.Abstractions (9.0.10)
- Microsoft.Extensions.Options (9.0.10)
- Microsoft.Extensions.Http (9.0.10)
Support
For issues, questions, or contributions, please visit the GitHub repository.
Changelog
See CHANGELOG.md for version history and detailed changes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Options (>= 9.0.10)
- Syncfusion.Blazor (>= 31.2.3)
- Syncfusion.Blazor.Themes (>= 31.2.3)
- System.ComponentModel.Annotations (>= 5.0.0)
- System.Linq.Expressions (>= 4.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v2.0.1: Corrección de descripción del paquete. Sin cambios en código. | v2.0.0: Paquete completamente desacoplado, auto-contenido con atributos y enums propios. Breaking changes: namespaces actualizados a MOVe.Razor.*