Compare commits

...

8 Commits

Author SHA1 Message Date
Vladimir Enchev
0e03c4377f PivotDataGrid set filter value should invalidate filter 2025-11-13 10:32:16 +02:00
Vladimir Enchev
7497ea1262 PivotDataGrid dynamic data support added 2025-11-13 10:19:37 +02:00
Atanas Korchev
d68bb34f6f Update getting started to include .NET 10. 2025-11-12 12:38:43 +02:00
Vladimir Enchev
732a6f4942 DataGrid sort ambiguous match found 2025-11-12 09:40:55 +02:00
Atanas Korchev
70fb896ae1 Update CI workflow to use .NET 10 2025-11-12 08:04:17 +02:00
Vladimir Enchev
034eae6722 Version updated 2025-11-11 17:50:22 +02:00
Vladimir Enchev
3472949bf0 sdk updated 2025-11-11 17:46:25 +02:00
Vladimir Enchev
c333b8ca30 Net10 support added (#2353)
* NET10 support added

* DataGrid Paging Disappears on Click in .NET Core 10.0.0 RC

Fix #2286

* various fixes

* KnownIPNetworks used instead KnownNetworks

* build fixed
2025-11-11 17:43:29 +02:00
19 changed files with 283 additions and 48 deletions

View File

@@ -20,7 +20,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Build
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0
FROM mcr.microsoft.com/dotnet/sdk:10.0
COPY Radzen.Blazor /app/Radzen.Blazor
COPY Radzen.DocFX /app/Radzen.DocFX

View File

@@ -22,7 +22,7 @@ namespace Radzen.Blazor.Tests
public Guid Id { get; set; }
public TimeOnly StartTime { get; set; }
public DateOnly BirthDate { get; set; }
public int[] Scores { get; set; }
public List<int> Scores { get; set; }
public List<string> Tags { get; set; }
public List<TestEntity> Children { get; set; }
public Address Address { get; set; }

View File

@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="bunit.web" Version="1.36.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

View File

@@ -87,10 +87,9 @@ public class AIChatService(IServiceProvider serviceProvider, IOptions<AIChatServ
var assistantResponse = new StringBuilder();
while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested)
string line;
while ((line = await reader.ReadLineAsync()) is not null && !cancellationToken.IsCancellationRequested)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:"))
{
continue;

View File

@@ -385,7 +385,8 @@ namespace Radzen
}
else
{
member = Expression.PropertyOrField(expression, currentPart);
var p = expression.Type.GetProperty(currentPart, BindingFlags.Public | BindingFlags.Instance);
member = p != null ? Expression.Property(expression, p) : Expression.PropertyOrField(expression, currentPart);
}
if (expression.Type.IsValueType && Nullable.GetUnderlyingType(expression.Type) == null)

View File

@@ -3,14 +3,14 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<NoWarn>BL9993;BL0007;BL0005</NoWarn>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<RazorLangVersion>7.0</RazorLangVersion>
<LangVersion>latest</LangVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<PackageId>Radzen.Blazor</PackageId>
<Product>Radzen.Blazor</Product>
<Version>8.2.2</Version>
<Version>8.3.0</Version>
<Copyright>Radzen Ltd.</Copyright>
<Authors>Radzen Ltd.</Authors>
<Description>Radzen Blazor is the most sophisticated free UI component library for Blazor, featuring 100+ native components including DataGrid, Scheduler, Charts, and advanced theming with full support for Material Design and Fluent UI.</Description>
@@ -34,6 +34,8 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components" Condition="'$(TargetFramework)' == 'net10.0'" Version="10.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Condition="'$(TargetFramework)' == 'net10.0'" Version="10.*-*" />
<PackageReference Include="Radzen.Terser.MSBuild" Version="0.0.4" PrivateAssets="All" />
</ItemGroup>
@@ -55,10 +57,10 @@
</PropertyGroup>
<ItemGroup>
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net9.0'" />
<Sass Include="$(MSBuildProjectDirectory)/themes/*.scss" Exclude="$(MSBuildProjectDirectory)/themes/_*.scss" Condition="'$(TargetFramework)' == 'net10.0'" />
</ItemGroup>
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net9.0'">
<Target Name="Sass" BeforeTargets="BeforeBuild" Condition="'$(TargetFramework)' == 'net10.0'">
<PropertyGroup>
<_SassFileList>@(Sass->'&quot;%(FullPath)&quot;', ' ')</_SassFileList>
<DartSassBuilderArgs>files $(_SassFileList) --outputstyle $(DartSassOutputStyle) --level $(DartSassOutputLevel)</DartSassBuilderArgs>
@@ -67,14 +69,14 @@
<Message Text="Converted SassFile list to argument" Importance="$(DartSassMessageLevel)" />
</Target>
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net9.0'">
<Target Name="MoveCss" AfterTargets="AfterCompile" Condition="'$(TargetFramework)' == 'net10.0'">
<ItemGroup>
<CssFile Include="$(MSBuildProjectDirectory)/themes/*.css" />
</ItemGroup>
<Move SourceFiles="@(CssFile)" DestinationFolder="$(MSBuildProjectDirectory)/wwwroot/css/" />
</Target>
<Target Name="MinifyTest" BeforeTargets="Build" Condition="'$(TargetFramework)' == 'net9.0'">
<Target Name="MinifyTest" BeforeTargets="Build" Condition="'$(TargetFramework)' == 'net10.0'">
<TerserMinify InputFile="wwwroot\Radzen.Blazor.js" OutputFile="wwwroot\Radzen.Blazor.min.js" />
</Target>

View File

@@ -2183,7 +2183,15 @@ namespace Radzen.Blazor
if (Data != null && !LoadData.HasDelegate)
{
#if NET10_0_OR_GREATER
var count = View.Count();
if (count != Count)
{
Count = count;
}
#else
Count = 1;
#endif
}
if (AllowVirtualization)

View File

@@ -950,7 +950,14 @@ namespace Radzen.Blazor
for (int i = 0; i < colPath.Count; i++)
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
if (pivotColumns[i].Property.Contains("it["))
{
items = items.Where($"it => {pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
else
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
}
var total = GetAggregateValue(items, aggregate);
@@ -1023,7 +1030,14 @@ namespace Radzen.Blazor
continue; // Skip padding cells added to align depths
}
items = items.Where($@"i => i.{pivotRows[i].Property} == {ExpressionSerializer.FormatValue(cell.Value)}");
if (pivotRows[i].Property.Contains("it["))
{
items = items.Where($@"it => {pivotRows[i].Property} == {ExpressionSerializer.FormatValue(cell.Value)}");
}
else
{
items = items.Where($@"i => i.{pivotRows[i].Property} == {ExpressionSerializer.FormatValue(cell.Value)}");
}
}
return items;
@@ -1131,7 +1145,7 @@ namespace Radzen.Blazor
}
else
{
values = items.Select(aggregate.Property).Cast(propertyType);
values = propertyType != null ? items.Select(aggregate.Property).Cast(propertyType) : items.Select(aggregate.Property);
}
switch (aggregate.Aggregate)
@@ -1249,7 +1263,14 @@ namespace Radzen.Blazor
var items = node.Items;
for (int i = 0; i < colPath.Count; i++)
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
if (pivotColumns[i].Property.Contains("it["))
{
items = items.Where($"it => {pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
else
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
}
row.ValueCells.Add(items.Count() > 0 ? GetAggregateValue(items, aggregate) : null);
}
@@ -1289,12 +1310,26 @@ namespace Radzen.Blazor
// Filter by row path
for (int i = 0; i < newPrefix.Count && i < pivotRows.Count; i++)
{
items = items.Where($@"i => i.{pivotRows[i].Property} == {ExpressionSerializer.FormatValue(newPrefix[i].Value)}");
if (pivotRows[i].Property.Contains("it["))
{
items = items.Where($@"it => {pivotRows[i].Property} == {ExpressionSerializer.FormatValue(newPrefix[i].Value)}");
}
else
{
items = items.Where($@"i => i.{pivotRows[i].Property} == {ExpressionSerializer.FormatValue(newPrefix[i].Value)}");
}
}
// Filter by column path
for (int i = 0; i < colPath.Count; i++)
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
if (pivotColumns[i].Property.Contains("it["))
{
items = items.Where($"it => {pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
else
{
items = items.Where($"i => i.{pivotColumns[i].Property} == {ExpressionSerializer.FormatValue(colPath[i])}");
}
}
collapsedRow.ValueCells.Add(items.Count() > 0 ? GetAggregateValue(items, aggregate) : null);
}
@@ -1752,7 +1787,7 @@ namespace Radzen.Blazor
{
field.SetSecondFilterValueInternal(value);
}
StateHasChanged();
InvokeAsync(OnFilterChanged);
}
}
@@ -1960,12 +1995,12 @@ namespace Radzen.Blazor
var innerFilterExpressions = new List<CompositeFilterDescriptor>();
var filterExpression = BuildFilterExpression(property, filterValue, filterOperator);
var filterExpression = BuildFilterExpression(property, filterValue, filterOperator, column.Type);
innerFilterExpressions.Add(filterExpression);
if (secondFilterValue != null)
{
var secondFilterExpression = BuildFilterExpression(property, secondFilterValue, secondFilterOperator);
var secondFilterExpression = BuildFilterExpression(property, secondFilterValue, secondFilterOperator, column.Type);
innerFilterExpressions.Add(secondFilterExpression);
}
@@ -1992,12 +2027,12 @@ namespace Radzen.Blazor
var innerFilterExpressions = new List<CompositeFilterDescriptor>();
var filterExpression = BuildFilterExpression(property, filterValue, filterOperator);
var filterExpression = BuildFilterExpression(property, filterValue, filterOperator, row.Type);
innerFilterExpressions.Add(filterExpression);
if (secondFilterValue != null)
{
var secondFilterExpression = BuildFilterExpression(property, secondFilterValue, secondFilterOperator);
var secondFilterExpression = BuildFilterExpression(property, secondFilterValue, secondFilterOperator, row.Type);
innerFilterExpressions.Add(secondFilterExpression);
}
@@ -2016,13 +2051,14 @@ namespace Radzen.Blazor
/// <summary>
/// Builds a filter expression for a property and value.
/// </summary>
private CompositeFilterDescriptor BuildFilterExpression(string property, object value, FilterOperator filterOperator)
private CompositeFilterDescriptor BuildFilterExpression(string property, object value, FilterOperator filterOperator, Type type)
{
return new CompositeFilterDescriptor
{
Property = property,
FilterValue = value,
FilterOperator = filterOperator
FilterOperator = filterOperator,
Type = type
};
}

View File

@@ -74,7 +74,7 @@ var forwardingOptions = new ForwardedHeadersOptions()
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardingOptions.KnownNetworks.Clear();
forwardingOptions.KnownIPNetworks.Clear();
forwardingOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardingOptions);

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ErrorOnDuplicatePublishOutputFiles>False</ErrorOnDuplicatePublishOutputFiles>
<UserSecretsId>d4f5f92a-c1c5-47b2-bb94-becc7f09133c</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RadzenBlazorDemos\RadzenBlazorDemos.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.*-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.*-*" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.*-*" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.*-*" />
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
</ItemGroup>

View File

@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>cf316844-9d34-42f9-a2a7-72a167736c64</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RadzenBlazorDemos.Host\RadzenBlazorDemos.Host.csproj" />
<ProjectReference Include="..\RadzenBlazorDemos\RadzenBlazorDemos.csproj" />
</ItemGroup>
</Project>

View File

@@ -19,7 +19,7 @@
<RadzenTabs SelectedIndex=@selectedIndex Change="@OnTabChange">
<Tabs>
<RadzenTabsItem Text=".NET 8 &amp; 9">
<RadzenTabsItem Text=".NET 10 &amp; 9 &amp; 8">
<div class="rz-py-0 rz-py-sm-4 rz-px-0 rz-px-sm-2 rz-px-lg-12">
<RadzenAlert AlertStyle="AlertStyle.Info" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false" class="rz-mb-12">
All interactive features of the Radzen Blazor components require interactivity of the container <code>.razor</code> file to be enabled or the <code>@@rendermode</code>
@@ -27,7 +27,7 @@ attribute of the component to be set to one of the following values: <code>Inter
More info is available in the <RadzenLink Text="rendering mode article" Path="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0" target="_blank" /> from the official Blazor documentation.
</RadzenAlert>
<RadzenAlert AlertStyle="AlertStyle.Info" Variant="Variant.Flat" Shade="Shade.Lighter" AllowClose="false" class="rz-mb-12">
If you have upgraded your application to .NET 8 or .NET 9 follow the getting started instructions for the version you initially used - e.g.
If you have upgraded your application to .NET 8 .NET 9 or .NET 10 follow the getting started instructions for the version you initially used - e.g.
<RadzenLink Path="get-started/net7-server" Text=".NET 7" /> or <RadzenLink Path="get-started/net6-server" Text=".NET 6"/>.
These instructions assume the new application layout which uses rendering modes and was introduced with .NET 8.
</RadzenAlert>

View File

@@ -0,0 +1,161 @@
@using System
@using System.Collections.Generic
@using System.Linq
@using Microsoft.EntityFrameworkCore
@using Radzen
@using Radzen.Blazor
@inherits RadzenBlazorDemos.Shared.DbContextPage
<RadzenCard Variant="Variant.Outlined" class="rz-my-4">
<RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center" Style="margin:1rem">
<RadzenSwitch @bind-Value="@showColumnTotals" Name="ShowColumnTotals" />
<RadzenLabel Text="Show column totals" Component="ShowColumnTotals" />
<RadzenSwitch @bind-Value="@showRowTotals" Name="ShowRowTotals" />
<RadzenLabel Text="Show row totals" Component="ShowRowTotals" />
<RadzenSwitch @bind-Value="@allowDrillDown" Name="AllowDrillDown" />
<RadzenLabel Text="Allow drill-down" Component="AllowDrillDown" />
<RadzenSwitch @bind-Value="@allowPaging" Name="AllowPaging" />
<RadzenLabel Text="Allow paging" Component="AllowPaging" />
<RadzenDropDown @bind-Value="@pagerPosition"
Visible="@allowPaging"
TextProperty="Text"
ValueProperty="Value"
Data="@(Enum.GetValues(typeof(PagerPosition)).Cast<PagerPosition>().Select(t => new { Text = $"{t}", Value = t }))" />
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center" Style="margin:1rem">
<RadzenSwitch @bind-Value="@allowFieldsPicking" Name="AllowFieldsPicking" />
<RadzenLabel Text="Allow fields picking" Component="AllowFieldsPicking" />
<RadzenSwitch @bind-Value="@allowSorting" Name="AllowSorting" Visible="@allowFieldsPicking" />
<RadzenLabel Text="Allow sorting" Component="AllowSorting" Visible="@allowFieldsPicking" />
<RadzenSwitch @bind-Value="@allowFiltering" Name="AllowFiltering" Visible="@allowFieldsPicking" />
<RadzenLabel Text="Allow filtering" Component="AllowFiltering" Visible="@allowFieldsPicking" />
</RadzenStack>
</RadzenCard>
<RadzenPivotDataGrid Data="@data"
TItem="IDictionary<string, object>"
AllowSorting="@allowSorting"
AllowFiltering="@allowFiltering"
AllowDrillDown="@allowDrillDown"
AllowFieldsPicking="@allowFieldsPicking"
ShowColumnsTotals="@showColumnTotals"
ShowRowsTotals="@showRowTotals"
AllowPaging="@allowPaging"
PagerPosition="@pagerPosition"
PageSize="20"
AllowAlternatingRows="true"
GridLines="Radzen.DataGridGridLines.Default"
Style="height: 700px;">
<Columns>
@foreach (var field in columnFields)
{
<RadzenPivotColumn @key="@field.Property"
Title="@field.Title"
Width="@field.Width"
Type="@field.Type"
Property="@PropertyAccess.GetDynamicPropertyExpression(field.Property, field.Type)" />
}
</Columns>
<Rows>
@foreach (var field in rowFields)
{
<RadzenPivotRow @key="@field.Property"
Title="@field.Title"
Type="@field.Type"
Property="@PropertyAccess.GetDynamicPropertyExpression(field.Property, field.Type)" />
}
</Rows>
<Aggregates>
@foreach (var aggregate in aggregateFields)
{
<RadzenPivotAggregate @key="@aggregate.Property"
Title="@aggregate.Title"
Type="@aggregate.Type"
Property="@PropertyAccess.GetDynamicPropertyExpression(aggregate.Property, aggregate.Type)"
Aggregate="@aggregate.Aggregate"
FormatString="@aggregate.FormatString"
TextAlign="@aggregate.TextAlign" />
}
</Aggregates>
</RadzenPivotDataGrid>
@code {
private readonly IReadOnlyList<PivotFieldDescriptor> columnFields = new[]
{
new PivotFieldDescriptor("OrderYear", "Order Year", typeof(int?), "140px"),
new PivotFieldDescriptor("ShipCountry", "Ship Country", typeof(string), "180px")
};
private readonly IReadOnlyList<PivotFieldDescriptor> rowFields = new[]
{
new PivotFieldDescriptor("Category", "Category", typeof(string)),
new PivotFieldDescriptor("Product", "Product", typeof(string))
};
private readonly IReadOnlyList<PivotAggregateDescriptor> aggregateFields = new[]
{
new PivotAggregateDescriptor("TotalSales", "Total Sales", typeof(double), AggregateFunction.Sum, "{0:C}", TextAlign.Right),
new PivotAggregateDescriptor("Quantity", "Quantity", typeof(int), AggregateFunction.Sum, "{0:N0}", TextAlign.Right),
new PivotAggregateDescriptor("Discount", "Average Discount", typeof(double), AggregateFunction.Average, "{0:P1}", TextAlign.Right),
new PivotAggregateDescriptor("UnitPrice", "Average Unit Price", typeof(double), AggregateFunction.Average, "{0:C}", TextAlign.Right)
};
private List<IDictionary<string, object>> data = new List<IDictionary<string, object>>();
private bool allowDrillDown = true;
private bool allowFieldsPicking = true;
private bool allowSorting = true;
private bool allowFiltering = true;
private bool allowPaging = true;
private bool showColumnTotals = true;
private bool showRowTotals = true;
private PagerPosition pagerPosition = PagerPosition.Bottom;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
var sales = await (from od in dbContext.OrderDetails
join o in dbContext.Orders on od.OrderID equals o.OrderID
join p in dbContext.Products on od.ProductID equals p.ProductID
join c in dbContext.Categories on p.CategoryID equals c.CategoryID
select new
{
CategoryName = c.CategoryName,
ProductName = p.ProductName,
OrderYear = o.OrderDate.HasValue ? o.OrderDate.Value.Year : 0,
ShipCountry = o.ShipCountry,
UnitPrice = od.UnitPrice ?? 0,
Quantity = od.Quantity ?? 0,
Discount = od.Discount ?? 0,
TotalAmount = (od.UnitPrice ?? 0) * (od.Quantity ?? 0) * (1 - (od.Discount ?? 0))
}).ToListAsync();
data = sales.Select(result =>
{
var row = new Dictionary<string, object>
{
["OrderYear"] = result.OrderYear,
["ShipCountry"] = string.IsNullOrEmpty(result.ShipCountry) ? "Unknown" : result.ShipCountry,
["Category"] = result.CategoryName ?? "(no category)",
["Product"] = result.ProductName ?? "(no product)",
["Quantity"] = result.Quantity,
["UnitPrice"] = result.UnitPrice,
["Discount"] = result.Discount,
["TotalSales"] = result.TotalAmount
};
return (IDictionary<string, object>)row;
}).ToList();
}
private sealed record PivotFieldDescriptor(string Property, string Title, Type Type, string? Width = null);
private sealed record PivotAggregateDescriptor(string Property,
string Title,
Type Type,
AggregateFunction Aggregate,
string? FormatString,
TextAlign TextAlign);
}

View File

@@ -0,0 +1,20 @@
@page "/pivot-data-grid-dynamic"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
PivotDataGrid <strong>dynamic</strong> data support
</RadzenText>
<RadzenText TextStyle="TextStyle.Subtitle1" TagName="TagName.P" class="rz-pb-4">
Schema-less datasets are common when data comes from external APIs. This demo shows how to build a pivot table on top of
<code>IDictionary&lt;string, object&gt;</code> records without defining C# models.
</RadzenText>
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.P" class="rz-pb-4">
The example generates 200 rows with dynamic columns (year, region, category, product, quantity, unit price, discount, total sales) and uses
<code>PropertyAccess.GetDynamicPropertyExpression</code> to configure pivot rows, columns, and aggregates at runtime while enabling totals, drill-down,
filtering, sorting, and field picking.
</RadzenText>
<RadzenExample ComponentName="PivotDataGrid" Example="PivotDataGridDynamicData">
<PivotDataGridDynamicData />
</RadzenExample>

View File

@@ -7,23 +7,23 @@
<RadzenCard Variant="Variant.Outlined" class="rz-my-4">
<RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center" Style="margin:1rem">
<RadzenSwitch @bind-Value=@showColumnsTotals Name="ShowColumnsTotals" TValue="bool" />
<RadzenSwitch @bind-Value=@showColumnsTotals Name="ShowColumnsTotals" />
<RadzenLabel Text="Show columns totals" Component="ShowColumnsTotals" />
<RadzenSwitch @bind-Value=@showRowsTotals Name="ShowRowsTotals" TValue="bool" />
<RadzenSwitch @bind-Value=@showRowsTotals Name="ShowRowsTotals" />
<RadzenLabel Text="Show rows totals" Component="ShowRowsTotals" />
<RadzenSwitch @bind-Value=@allowDrillDown Name="AllowDrillDown" TValue="bool" />
<RadzenSwitch @bind-Value=@allowDrillDown Name="AllowDrillDown" />
<RadzenLabel Text="Allow drill-down" Component="AllowDrillDown" />
<RadzenSwitch @bind-Value=@allowPaging Name="AllowPaging" TValue="bool" />
<RadzenSwitch @bind-Value=@allowPaging Name="AllowPaging" />
<RadzenLabel Text="AllowPaging" Component="AllowPaging" />
<RadzenDropDown @bind-Value="@pagerPosition" Visible="@allowPaging" TextProperty="Text" Name="PagerPosition" ValueProperty="Value"
Data="@(Enum.GetValues(typeof(PagerPosition)).Cast<PagerPosition>().Select(t => new { Text = $"{t}", Value = t }))" />
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center" Style="margin:1rem">
<RadzenSwitch @bind-Value=@allowFieldsPicking Name="AllowFieldsPicking" TValue="bool" />
<RadzenSwitch @bind-Value=@allowFieldsPicking Name="AllowFieldsPicking" />
<RadzenLabel Text="Allow fields picking" Component="AllowFieldsPicking" />
<RadzenSwitch @bind-Value=@allowSorting Name="AllowSorting" TValue="bool" Visible=@allowFieldsPicking />
<RadzenSwitch @bind-Value=@allowSorting Name="AllowSorting" Visible=@allowFieldsPicking />
<RadzenLabel Text="Allow sorting" Component="AllowSorting" Visible=@allowFieldsPicking />
<RadzenSwitch @bind-Value=@allowFiltering Name="AllowFiltering" TValue="bool" Visible=@allowFieldsPicking />
<RadzenSwitch @bind-Value=@allowFiltering Name="AllowFiltering" Visible=@allowFieldsPicking />
<RadzenLabel Text="Allow filtering" Component="AllowFiltering" Visible=@allowFieldsPicking />
</RadzenStack>
</RadzenCard>

View File

@@ -25,7 +25,7 @@ The Radzen.Blazor library provides a built-in service that persists the current
</RadzenText>
<RadzenTabs SelectedIndex=@selectedIndex Change="@OnTabChange">
<Tabs>
<RadzenTabsItem Text=".NET 8">
<RadzenTabsItem Text=".NET 8 &amp; 9 &amp; 10">
@RegisterService
@MainLayout
<RadzenText TextStyle="TextStyle.H6" TagName="TagName.H3" class="rz-mt-12 rz-mb-4">3. Open the <code>App.razor</code> file of your application and add this code:</RadzenText>

View File

@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Radzen.Blazor" Version="*" Condition="'$(Configuration)' == 'Release'" />
<ProjectReference Include="..\Radzen.Blazor\Radzen.Blazor.csproj" Condition="'$(Configuration)' != 'Release'" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.*-*" PrivateAssets="all" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.*-*" PrivateAssets="all" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.*-*" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="6.0.10" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Razor" Version="6.0.10" />

View File

@@ -1036,6 +1036,14 @@ namespace RadzenBlazorDemos
Tags = new [] { "pivot", "crosstab", "analysis", "aggregation", "drill-down", "datagrid", "table", "query", "IQueryable" }
},
new Example
{
Name = "Dynamic data",
Path = "/pivot-data-grid-dynamic",
Title = "Blazor Pivot DataGrid Component - dynamic data sets | Free UI Components by Radzen",
Description = "Bind RadzenPivotDataGrid to schema-less IDictionary<string, object> records and configure fields dynamically.",
Tags = new [] { "pivot", "dynamic", "dictionary", "analysis", "aggregation", "drill-down", "datagrid", "table" }
},
new Example
{
Name = "OData",
Path = "/pivot-data-grid-odata",