Add corner radius and exploded slices to pie and donut series

This commit is contained in:
yordanov
2026-06-21 16:21:29 +03:00
parent 1c7af74e1e
commit f2ec0428ad
9 changed files with 411 additions and 10 deletions

View File

@@ -109,5 +109,195 @@ namespace Radzen.Blazor.Tests
// Donut arcs have both outer and inner radius in the path
Assert.Contains("A 118 118", chart.Markup); // outer
}
[Fact]
public async System.Threading.Tasks.Task PieSeries_DefaultCornerRadius_UsesSharpPath()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<DataItem>>(s => s
.Add(x => x.CategoryProperty, nameof(DataItem.Category))
.Add(x => x.ValueProperty, nameof(DataItem.Value))
.Add(x => x.Data, SampleData)));
await chart.InvokeAsync(() => chart.Instance.Resize(400, 300));
// A sharp pie slice closes through the center with a zero-radius inner arc.
Assert.Contains("A 0 0", chart.Markup);
}
[Fact]
public async System.Threading.Tasks.Task PieSeries_CornerRadius_RoundsThreeCornersPerSlice()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<DataItem>>(s => s
.Add(x => x.CategoryProperty, nameof(DataItem.Category))
.Add(x => x.ValueProperty, nameof(DataItem.Value))
.Add(x => x.CornerRadius, 10.0)
.Add(x => x.Data, SampleData)));
await chart.InvokeAsync(() => chart.Instance.Resize(400, 300));
// No sharp apex (no zero-radius inner arc) once corners are rounded.
Assert.DoesNotContain("A 0 0", chart.Markup);
// Three fillet arcs of radius 10 per slice (two outer corners + apex) across three slices.
Assert.Equal(9, Count(chart.Markup, "A 10 10"));
Assert.DoesNotContain("NaN", chart.Markup);
}
[Fact]
public async System.Threading.Tasks.Task DonutSeries_CornerRadius_RoundsFourCornersPerSlice()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenDonutSeries<DataItem>>(s => s
.Add(x => x.CategoryProperty, nameof(DataItem.Category))
.Add(x => x.ValueProperty, nameof(DataItem.Value))
.Add(x => x.InnerRadius, 50)
.Add(x => x.CornerRadius, 10.0)
.Add(x => x.Data, SampleData)));
await chart.InvokeAsync(() => chart.Instance.Resize(400, 300));
// Main outer and inner arcs survive.
Assert.Contains("A 118 118", chart.Markup);
Assert.Contains("A 50 50", chart.Markup);
// Four fillet arcs of radius 10 per slice across three slices.
Assert.Equal(12, Count(chart.Markup, "A 10 10"));
Assert.DoesNotContain("NaN", chart.Markup);
}
[Fact]
public async System.Threading.Tasks.Task PieSeries_LargeCornerRadius_ClampedWithoutNaN()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<DataItem>>(s => s
.Add(x => x.CategoryProperty, nameof(DataItem.Category))
.Add(x => x.ValueProperty, nameof(DataItem.Value))
.Add(x => x.CornerRadius, 1000.0)
.Add(x => x.Data, SampleData)));
await chart.InvokeAsync(() => chart.Instance.Resize(400, 300));
Assert.DoesNotContain("NaN", chart.Markup);
}
[Fact]
public async System.Threading.Tasks.Task PieSeries_SingleItem_CornerRadiusFallsBackToSharpRing()
{
using var ctx = CreateChartContext();
var single = new[] { new DataItem { Category = "A", Value = 10 } };
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<DataItem>>(s => s
.Add(x => x.CategoryProperty, nameof(DataItem.Category))
.Add(x => x.ValueProperty, nameof(DataItem.Value))
.Add(x => x.CornerRadius, 10.0)
.Add(x => x.Data, single)));
await chart.InvokeAsync(() => chart.Instance.Resize(400, 300));
// A full-circle slice has no corners; it renders as the sharp ring (zero-radius inner arc).
Assert.Contains("A 0 0", chart.Markup);
Assert.DoesNotContain("A 10 10", chart.Markup);
}
class ExplodeItem
{
public string Category { get; set; } = "";
public double Value { get; set; }
public bool Exploded { get; set; }
}
static ExplodeItem[] ExplodeData => new[]
{
new ExplodeItem { Category = "A", Value = 10, Exploded = false },
new ExplodeItem { Category = "B", Value = 20, Exploded = true },
new ExplodeItem { Category = "C", Value = 15, Exploded = false },
};
[Fact]
public void PieSeries_ExplodedProperty_MarksFlaggedSlices()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<ExplodeItem>>(s => s
.Add(x => x.CategoryProperty, nameof(ExplodeItem.Category))
.Add(x => x.ValueProperty, nameof(ExplodeItem.Value))
.Add(x => x.ExplodeOffset, 15.0)
.Add(x => x.ExplodedProperty, nameof(ExplodeItem.Exploded))
.Add(x => x.Data, ExplodeData)));
// Only the single flagged slice carries the static-explode class, and the offset vars are emitted.
Assert.Equal(1, Count(chart.Markup, "rz-state-exploded"));
Assert.Contains("--rz-explode-x", chart.Markup);
}
[Fact]
public void PieSeries_NoExplodedProperty_NoStaticExplodeClass()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<ExplodeItem>>(s => s
.Add(x => x.CategoryProperty, nameof(ExplodeItem.Category))
.Add(x => x.ValueProperty, nameof(ExplodeItem.Value))
.Add(x => x.ExplodeOffset, 15.0)
.Add(x => x.Data, ExplodeData)));
Assert.DoesNotContain("rz-state-exploded", chart.Markup);
}
[Fact]
public void PieSeries_ExplodedProperty_WithoutOffset_NoStaticExplodeClass()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenPieSeries<ExplodeItem>>(s => s
.Add(x => x.CategoryProperty, nameof(ExplodeItem.Category))
.Add(x => x.ValueProperty, nameof(ExplodeItem.Value))
.Add(x => x.ExplodedProperty, nameof(ExplodeItem.Exploded))
.Add(x => x.Data, ExplodeData)));
// ExplodeOffset defaults to 0, which gates static explode off.
Assert.DoesNotContain("rz-state-exploded", chart.Markup);
}
[Fact]
public void DonutSeries_ExplodedProperty_MarksFlaggedSlices()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(p => p
.AddChildContent<RadzenDonutSeries<ExplodeItem>>(s => s
.Add(x => x.CategoryProperty, nameof(ExplodeItem.Category))
.Add(x => x.ValueProperty, nameof(ExplodeItem.Value))
.Add(x => x.ExplodeOffset, 15.0)
.Add(x => x.ExplodedProperty, nameof(ExplodeItem.Exploded))
.Add(x => x.Data, ExplodeData)));
Assert.Equal(1, Count(chart.Markup, "rz-state-exploded"));
}
private static int Count(string haystack, string needle)
{
var count = 0;
var index = 0;
while ((index = haystack.IndexOf(needle, index, System.StringComparison.Ordinal)) >= 0)
{
count++;
index += needle.Length;
}
return count;
}
}
}

View File

@@ -63,7 +63,8 @@
startAngle = endAngle;
var index = Items.IndexOf(data);
var arcClassName = $"rz-series-item-{index}";
var exploded = ExplodeOffset > 0 && IsExploded(data);
var arcClassName = $"rz-series-item-{index}" + (exploded ? " rz-state-exploded" : "");
var fill = PickColor(index, Fills);
var stroke = PickColor(index, Strokes);
var gradId = $"rzPieGradient{chart.UniqueID}-{seriesIndex}-{index}";

View File

@@ -42,7 +42,8 @@
startAngle = endAngle;
var index = Items.IndexOf(data);
var arcClassName = $"rz-series-item-{index}";
var exploded = ExplodeOffset > 0 && IsExploded(data);
var arcClassName = $"rz-series-item-{index}" + (exploded ? " rz-state-exploded" : "");
var fill = PickColor(index, Fills);
var stroke = PickColor(index, Strokes);
var gradId = $"rzPieGradient{chart.UniqueID}-{seriesIndex}-{index}";

View File

@@ -63,6 +63,15 @@ namespace Radzen.Blazor
[Parameter]
public double? Radius { get; set; }
/// <summary>
/// Gets or sets the corner radius in pixels used to round the corners of each segment.
/// A pie segment has its two outer corners and its center apex rounded; a donut segment has all four corners rounded.
/// The value is clamped per segment so that adjacent corners never overlap.
/// </summary>
/// <value>The corner radius in pixels. Default is <c>0</c> (sharp corners).</value>
[Parameter]
public double CornerRadius { get; set; }
/// <summary>
/// Gets or sets the name of the property that provides a per-item radius value.
/// When set, each pie/donut segment can have a different outer radius, allowing visual differentiation by size.
@@ -74,12 +83,23 @@ namespace Radzen.Blazor
/// <summary>
/// Gets or sets the distance in pixels that a segment moves outward from the center when hovered.
/// Set to a value greater than 0 to enable the explode-on-hover effect.
/// Set to a value greater than 0 to enable the explode-on-hover effect. Also sets the distance for
/// segments pulled out permanently via <see cref="ExplodedProperty"/>.
/// </summary>
/// <value>The explode offset in pixels. Default is 0 (disabled).</value>
[Parameter]
public double ExplodeOffset { get; set; }
/// <summary>
/// Gets or sets the name of a boolean property that marks segments as exploded (pulled out from the
/// center) in their normal state, not just on hover. Requires <see cref="ExplodeOffset"/> greater than 0,
/// which sets the distance. Exploded segments stay pulled out and move a little further on hover; other
/// segments still explode on hover.
/// </summary>
/// <value>The boolean property name on <typeparamref name="TItem"/>, or null to disable static explode.</value>
[Parameter]
public string? ExplodedProperty { get; set; }
/// <summary>
/// Gets or sets a collection of fill colors applied to individual pie segments in sequence.
/// Each segment gets the color at its index position. If fewer colors than segments, colors are reused cyclically.
@@ -222,6 +242,20 @@ namespace Radzen.Blazor
return minRadius + (value - minValue) / (maxValue - minValue) * (maxRadius - minRadius);
}
/// <summary>
/// Returns whether a specific data item is exploded in its normal state, as determined by
/// <see cref="ExplodedProperty"/>. Always <c>false</c> when <see cref="ExplodedProperty"/> is not set.
/// </summary>
internal bool IsExploded(TItem item)
{
if (string.IsNullOrEmpty(ExplodedProperty))
{
return false;
}
return PropertyAccess.Getter<TItem, bool>(ExplodedProperty)(item);
}
/// <summary>
/// Gets the current X coordinate of the center.
/// </summary>
@@ -511,7 +545,7 @@ namespace Radzen.Blazor
}
/// <summary>
/// Creates SVG path that renders the specified segment.
/// Creates SVG path that renders the specified segment, rounding its corners by <see cref="CornerRadius"/>.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
@@ -520,6 +554,14 @@ namespace Radzen.Blazor
/// <param name="startAngle">The start angle.</param>
/// <param name="endAngle">The end angle.</param>
protected string Segment(double x, double y, double radius, double innerRadius, double startAngle, double endAngle)
{
return Segment(x, y, radius, innerRadius, startAngle, endAngle, CornerRadius);
}
/// <summary>
/// Creates SVG path that renders the specified segment with sharp corners.
/// </summary>
private string SharpSegment(double x, double y, double radius, double innerRadius, double startAngle, double endAngle)
{
var largeArcFlag = 0;
@@ -549,6 +591,135 @@ namespace Radzen.Blazor
return $"M {startX} {startY} A {r} {r} 0 {largeArcFlag} 1 {endX} {endY} L {innerEndX} {innerEndY} A {innerR} {innerR} 0 {largeArcFlag} 0 {innerStartX} {innerStartY} Z";
}
/// <summary>
/// Creates SVG path that renders the specified segment with corners rounded by <paramref name="cornerRadius"/>.
/// A pie segment (<paramref name="innerRadius"/> of <c>0</c>) rounds its two outer corners and its center apex;
/// a donut segment rounds all four corners. The corner radius is clamped per segment so corners never overlap.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="radius">The radius.</param>
/// <param name="innerRadius">The inner radius.</param>
/// <param name="startAngle">The start angle.</param>
/// <param name="endAngle">The end angle.</param>
/// <param name="cornerRadius">The corner radius in pixels.</param>
protected string Segment(double x, double y, double radius, double innerRadius, double startAngle, double endAngle, double cornerRadius)
{
var sweep = Math.Abs(startAngle - endAngle);
// No rounding requested, a full ring (no corners to round), or a degenerate radius: use the sharp path.
if (cornerRadius <= 0.01 || sweep >= 359.99 || radius <= 0)
{
return SharpSegment(x, y, radius, innerRadius, startAngle, endAngle);
}
var isDonut = innerRadius > 0;
// Clamp the corner radius so adjacent fillets never overlap.
var s = Math.Sin(DegToRad(sweep / 2));
var rc = Math.Min(cornerRadius, (radius - innerRadius) / 2);
rc = Math.Min(rc, radius * s / (1 + s)); // outer arc angular limit
if (isDonut && s < 1)
{
rc = Math.Min(rc, innerRadius * s / (1 - s)); // inner arc angular limit
}
// Too thin to round visibly - fall back to sharp corners.
if (rc < 0.01)
{
return SharpSegment(x, y, radius, innerRadius, startAngle, endAngle);
}
// The slice spans from startAngle (a0) down to endAngle (a1), drawn clockwise.
var a0 = startAngle;
var a1 = endAngle;
// Outer fillets are tangent to the outer circle from inside (center at radius - rc).
var eOut = Math.Asin(rc / (radius - rc));
var eOutDeg = eOut * 180 / Math.PI;
var outerEdgeRadius = (radius - rc) * Math.Cos(eOut);
var outerArcStart = ToCartesian(x, y, radius, a0 - eOutDeg);
var outerArcEnd = ToCartesian(x, y, radius, a1 + eOutDeg);
var outerEdgeStart = ToCartesian(x, y, outerEdgeRadius, a0);
var outerEdgeEnd = ToCartesian(x, y, outerEdgeRadius, a1);
var outerCenterStart = ToCartesian(x, y, radius - rc, a0 - eOutDeg);
var outerCenterEnd = ToCartesian(x, y, radius - rc, a1 + eOutDeg);
var largeOuter = (sweep - 2 * eOutDeg) >= 180 ? 1 : 0;
var path = $"M {Format(outerArcStart)}" +
$" {Arc(radius, largeOuter, 1, outerArcEnd)}" +
$" {Arc(rc, 0, Sweep(outerArcEnd, outerEdgeEnd, outerCenterEnd), outerEdgeEnd)}";
if (isDonut)
{
// Inner fillets are tangent to the inner circle from outside (center at innerRadius + rc).
var eIn = Math.Asin(rc / (innerRadius + rc));
var eInDeg = eIn * 180 / Math.PI;
var innerEdgeRadius = (innerRadius + rc) * Math.Cos(eIn);
var innerArcStart = ToCartesian(x, y, innerRadius, a0 - eInDeg);
var innerArcEnd = ToCartesian(x, y, innerRadius, a1 + eInDeg);
var innerEdgeStart = ToCartesian(x, y, innerEdgeRadius, a0);
var innerEdgeEnd = ToCartesian(x, y, innerEdgeRadius, a1);
var innerCenterStart = ToCartesian(x, y, innerRadius + rc, a0 - eInDeg);
var innerCenterEnd = ToCartesian(x, y, innerRadius + rc, a1 + eInDeg);
var largeInner = (sweep - 2 * eInDeg) >= 180 ? 1 : 0;
path += $" L {Format(innerEdgeEnd)}" +
$" {Arc(rc, 0, Sweep(innerEdgeEnd, innerArcEnd, innerCenterEnd), innerArcEnd)}" +
$" {Arc(innerRadius, largeInner, 0, innerArcStart)}" +
$" {Arc(rc, 0, Sweep(innerArcStart, innerEdgeStart, innerCenterStart), innerEdgeStart)}" +
$" L {Format(outerEdgeStart)}" +
$" {Arc(rc, 0, Sweep(outerEdgeStart, outerArcStart, outerCenterStart), outerArcStart)} Z";
}
else
{
// Pie: round the center apex where the two radial edges meet.
var half = DegToRad(sweep / 2);
var apexEdgeRadius = rc / Math.Tan(half);
var apexStart = ToCartesian(x, y, apexEdgeRadius, a0);
var apexEnd = ToCartesian(x, y, apexEdgeRadius, a1);
var apexCenter = ToCartesian(x, y, rc / Math.Sin(half), (a0 + a1) / 2);
path += $" L {Format(apexEnd)}" +
$" {Arc(rc, 0, Sweep(apexEnd, apexStart, apexCenter), apexStart)}" +
$" L {Format(outerEdgeStart)}" +
$" {Arc(rc, 0, Sweep(outerEdgeStart, outerArcStart, outerCenterStart), outerArcStart)} Z";
}
return path;
}
/// <summary>
/// Formats a point as "x y" using the invariant culture.
/// </summary>
private static string Format((double X, double Y) point)
{
return $"{point.X.ToInvariantString()} {point.Y.ToInvariantString()}";
}
/// <summary>
/// Builds an SVG elliptical-arc command to the given end point.
/// </summary>
private static string Arc(double radius, int largeArcFlag, int sweepFlag, (double X, double Y) end)
{
var r = radius.ToInvariantString();
return $"A {r} {r} 0 {largeArcFlag} {sweepFlag} {Format(end)}";
}
/// <summary>
/// Returns the SVG sweep flag for the minor arc from <paramref name="start"/> to <paramref name="end"/>
/// about <paramref name="center"/>. In SVG (y-down) coordinates a positive cross product is drawn clockwise.
/// </summary>
private static int Sweep((double X, double Y) start, (double X, double Y) end, (double X, double Y) center)
{
var cross = (start.X - center.X) * (end.Y - center.Y) - (start.Y - center.Y) * (end.X - center.X);
return cross > 0 ? 1 : 0;
}
/// <summary>
/// Returns the inner radius used for data label placement - <c>0</c> for pie, the hole radius for donut.
/// </summary>

View File

@@ -337,6 +337,17 @@ $chart-color-schemes: (
}
}
}
// Segments exploded in their normal state move a little further on hover.
> g.rz-state-exploded {
&:hover,
&.rz-state-hover {
> path:not(.rz-hit-area),
> .rz-path {
transform: translate(calc(var(--rz-explode-x, 0) * 1.2), calc(var(--rz-explode-y, 0) * 1.2));
}
}
}
}
.rz-pie-series:has(> .rz-state-hover),
@@ -367,6 +378,19 @@ $chart-color-schemes: (
}
}
// Segments pulled out permanently via ExplodedProperty. Applied outside the hover wrapper so static
// explode works even when AllowSeriesHover is false. The hit-area moves too so it tracks the slice.
.rz-pie-series,
.rz-donut-series {
> g.rz-state-exploded {
> path,
> .rz-path {
transform: translate(var(--rz-explode-x, 0), var(--rz-explode-y, 0));
transition: transform var(--rz-transition);
}
}
}
@each $scheme, $colors in $chart-color-schemes {
@each $color in $colors {
$index: index($colors, $color);

View File

@@ -19,12 +19,17 @@
</Items>
</RadzenSelectBar>
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem">
<RadzenLabel Text="Corner Radius" Component="cornerRadius" />
<RadzenSlider @bind-Value="@cornerRadius" TValue="double" Min="0" Max="30" Step="1" Name="cornerRadius" Style="width: 120px;" />
<RadzenText TextStyle="TextStyle.Body2" class="rz-mb-0">@cornerRadius px</RadzenText>
</RadzenStack>
</RadzenStack>
</RadzenCard>
<RadzenStack Style="width: 100%; max-width: 600px;">
<RadzenChart Animate="true">
<RadzenDonutSeries FillMode="@fillMode" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" TotalAngle="180" StartAngle="180" ShowTooltipOnLegend="@showTooltipOnLegend">
<RadzenDonutSeries FillMode="@fillMode" CornerRadius="@cornerRadius" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" TotalAngle="180" StartAngle="180" ShowTooltipOnLegend="@showTooltipOnLegend">
<ChildContent>
<RadzenSeriesDataLabels Visible="@showDataLabels" />
</ChildContent>
@@ -43,6 +48,7 @@
bool showDataLabels = false;
bool showTooltipOnLegend = true;
FillMode fillMode = FillMode.Gradient;
double cornerRadius = 0;
class DataItem
{

View File

@@ -1,7 +1,7 @@
<RadzenStack class="rz-p-0 rz-p-md-6 rz-p-lg-12" AlignItems="AlignItems.Center">
<RadzenStack Style="width: 100%; max-width: 600px;">
<RadzenChart>
<RadzenDonutSeries FillMode="FillMode.Gradient" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ExplodeOffset="15">
<RadzenDonutSeries FillMode="FillMode.Gradient" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ExplodeOffset="15" ExplodedProperty="Highlight">
<ChildContent>
<RadzenSeriesDataLabels Visible="true" />
</ChildContent>
@@ -16,6 +16,7 @@
{
public string Quarter { get; set; }
public double Revenue { get; set; }
public bool Highlight { get; set; }
}
DataItem[] revenue = new DataItem[]
@@ -23,6 +24,6 @@
new DataItem { Quarter = "Q1", Revenue = 30000 },
new DataItem { Quarter = "Q2", Revenue = 40000 },
new DataItem { Quarter = "Q3", Revenue = 50000 },
new DataItem { Quarter = "Q4", Revenue = 80000 },
new DataItem { Quarter = "Q4", Revenue = 80000, Highlight = true },
};
}

View File

@@ -19,12 +19,17 @@
</Items>
</RadzenSelectBar>
</RadzenStack>
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem">
<RadzenLabel Text="Corner Radius" Component="cornerRadius" />
<RadzenSlider @bind-Value="@cornerRadius" TValue="double" Min="0" Max="30" Step="1" Name="cornerRadius" Style="width: 120px;" />
<RadzenText TextStyle="TextStyle.Body2" class="rz-mb-0">@cornerRadius px</RadzenText>
</RadzenStack>
</RadzenStack>
</RadzenCard>
<RadzenStack Style="width: 100%; max-width: 600px;">
<RadzenChart Animate="true" SeriesClick=@OnSeriesClick>
<RadzenPieSeries FillMode="@fillMode" Data="@revenue" Title="Revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ShowTooltipOnLegend="@showTooltipOnLegend">
<RadzenPieSeries FillMode="@fillMode" CornerRadius="@cornerRadius" Data="@revenue" Title="Revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ShowTooltipOnLegend="@showTooltipOnLegend">
<RadzenSeriesDataLabels Visible="@showDataLabels" />
</RadzenPieSeries>
</RadzenChart>
@@ -38,6 +43,7 @@
bool showDataLabels = false;
bool showTooltipOnLegend = true;
FillMode fillMode = FillMode.Gradient;
double cornerRadius = 0;
void OnSeriesClick(SeriesClickEventArgs args)
{

View File

@@ -1,7 +1,7 @@
<RadzenStack class="rz-p-0 rz-p-md-6 rz-p-lg-12" AlignItems="AlignItems.Center">
<RadzenStack Style="width: 100%; max-width: 600px;">
<RadzenChart>
<RadzenPieSeries FillMode="FillMode.Gradient" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ExplodeOffset="15">
<RadzenPieSeries FillMode="FillMode.Gradient" Data="@revenue" CategoryProperty="Quarter" ValueProperty="Revenue" ExplodeOffset="15" ExplodedProperty="Highlight">
<RadzenSeriesDataLabels Visible="true" />
</RadzenPieSeries>
<RadzenLegend Position="LegendPosition.Bottom" />
@@ -14,6 +14,7 @@
{
public string Quarter { get; set; }
public double Revenue { get; set; }
public bool Highlight { get; set; }
}
DataItem[] revenue = new DataItem[]
@@ -21,6 +22,6 @@
new DataItem { Quarter = "Q1", Revenue = 30000 },
new DataItem { Quarter = "Q2", Revenue = 40000 },
new DataItem { Quarter = "Q3", Revenue = 50000 },
new DataItem { Quarter = "Q4", Revenue = 80000 },
new DataItem { Quarter = "Q4", Revenue = 80000, Highlight = true },
};
}