MOVe.Razor 2.0.1

dotnet add package MOVe.Razor --version 2.0.1
                    
NuGet\Install-Package MOVe.Razor -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MOVe.Razor" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MOVe.Razor" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="MOVe.Razor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MOVe.Razor --version 2.0.1
                    
#r "nuget: MOVe.Razor, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MOVe.Razor@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MOVe.Razor&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=MOVe.Razor&version=2.0.1
                    
Install as a Cake Tool

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:

  1. Namespace Changes: All namespaces now use MOVe.Razor.* instead of MOVe.Shared.DTOs.*

    • MOVe.Shared.DTOs.Attributes → MOVe.Razor.Attributes
    • MOVe.Shared.DTOs.Enums → MOVe.Razor.Enums
    • MOVe.Shared.Contracts.Pagination → MOVe.Razor.Models
  2. Enum Renames (to avoid Syncfusion conflicts):

    • ColumnType → UIColumnType
    • FilterType → UIFilterType
  3. Self-Contained: No longer depends on MOVe.Shared.DTOs, MOVe.Shared.Contracts, or MOVe.Domain

  4. Removed Components: MOVe-specific components (ProjectSelector, RiskMatrix, etc.) are excluded from the package

  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 262 11/2/2025
2.0.0 228 11/2/2025

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.*