Fix multiple value axes with RadzenBarSeries behaving like category axes. Fixes #2597

This commit is contained in:
Vladimir Enchev
2026-07-03 16:51:53 +03:00
parent 233596fd69
commit efd92eb051
7 changed files with 403 additions and 41 deletions

View File

@@ -0,0 +1,176 @@
using System;
using System.Linq;
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Radzen.Blazor.Tests;
public class BarSeriesMultipleAxesTests
{
private class Item
{
public string Month { get; set; } = "";
public double Revenue { get; set; }
public double Count { get; set; }
}
private static readonly Item[] Data =
{
new() { Month = "Jan", Revenue = 234000, Count = 320 },
new() { Month = "Feb", Revenue = 269000, Count = 358 },
new() { Month = "Mar", Revenue = 233000, Count = 302 },
};
private static TestContext CreateChartContext()
{
var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.Setup<Radzen.Blazor.Rendering.Rect>("Radzen.createChart", _ => true)
.SetResult(new Radzen.Blazor.Rendering.Rect { Left = 0, Top = 0, Width = 600, Height = 400 });
ctx.Services.AddScoped<TooltipService>();
return ctx;
}
private static IRenderedComponent<RadzenChart> RenderChart(TestContext ctx)
{
return ctx.RenderComponent<RadzenChart>(parameters => parameters
.AddChildContent<RadzenBarSeries<Item>>(series => series
.Add(p => p.Title, "Revenue")
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Revenue))
.Add(p => p.Data, Data))
.AddChildContent<RadzenBarSeries<Item>>(series => series
.Add(p => p.Title, "Order Count")
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Count))
.Add(p => p.ValueAxisName, "count")
.Add(p => p.Data, Data))
.AddChildContent<RadzenValueAxis>(a => a
.Add(p => p.Name, "count")));
}
[Fact]
public void Named_Axis_Scale_Is_Numeric_And_Horizontal()
{
using var ctx = CreateChartContext();
var chart = RenderChart(ctx);
var instance = chart.Instance;
var additionalScale = instance.GetValueScale("count");
Assert.IsNotType<OrdinalScale>(additionalScale);
Assert.True(additionalScale.Input.End <= 500, $"Expected a numeric scale fitted to the count values but input was {additionalScale.Input.Start}..{additionalScale.Input.End}");
Assert.Equal(instance.CategoryScale.Output.Start, additionalScale.Output.Start);
Assert.Equal(instance.CategoryScale.Output.End, additionalScale.Output.End);
}
[Fact]
public void Primary_Scale_Does_Not_Include_Named_Series_Values()
{
using var ctx = CreateChartContext();
var chart = RenderChart(ctx);
var instance = chart.Instance;
var revenue = instance.Series.First(s => (s as IChartValueAxisSeries)?.ValueAxisName == null);
var count = instance.Series.First(s => (s as IChartValueAxisSeries)?.ValueAxisName == "count");
var revenueX = revenue.GetTooltipPosition(Data[1]).X;
var countX = count.GetTooltipPosition(Data[1]).X;
Assert.True(countX > revenueX * 0.8, $"Expected the count bar (value 358) to scale on its own axis near the revenue bar (value 269000) but got countX={countX}, revenueX={revenueX}");
}
[Fact]
public void Named_Axis_Renders_Horizontally_On_Top_With_Value_Labels()
{
using var ctx = CreateChartContext();
var chart = RenderChart(ctx);
Assert.Empty(chart.FindAll("g.rz-value-axis-right"));
var topAxisLabels = chart.FindAll("g.rz-value-axis-top .rz-tick-text")
.Select(t => t.TextContent.Trim())
.Where(t => !string.IsNullOrEmpty(t))
.ToList();
Assert.NotEmpty(topAxisLabels);
Assert.All(topAxisLabels, label => Assert.True(double.TryParse(label, out _), $"Expected numeric tick label but got '{label}'"));
var categories = Data.Select(d => d.Month);
Assert.DoesNotContain(topAxisLabels, label => categories.Contains(label));
}
[Fact]
public void Shared_Category_Axis_Contains_All_Categories()
{
using var ctx = CreateChartContext();
var chart = RenderChart(ctx);
var instance = chart.Instance;
Assert.IsType<OrdinalScale>(instance.ValueScale);
var leftAxisLabels = chart.FindAll("g.rz-value-axis:not(.rz-value-axis-top) .rz-tick-text")
.Select(t => t.TextContent.Trim())
.ToList();
foreach (var month in Data.Select(d => d.Month))
{
Assert.Contains(month, leftAxisLabels);
}
}
[Fact]
public void Named_Axis_Tooltip_Formats_With_Named_Axis()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(parameters => parameters
.AddChildContent<RadzenBarSeries<Item>>(series => series
.Add(p => p.Title, "Revenue")
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Revenue))
.Add(p => p.Data, Data))
.AddChildContent<RadzenBarSeries<Item>>(series => series
.Add(p => p.Title, "Order Count")
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Count))
.Add(p => p.ValueAxisName, "count")
.Add(p => p.Data, Data))
.AddChildContent<RadzenValueAxis>(a => a
.Add(p => p.Name, "count")
.Add(p => p.FormatString, "{0} orders")));
var count = chart.Instance.Series.First(s => (s as IChartValueAxisSeries)?.ValueAxisName == "count");
var dataLabels = count.GetDataLabels(0, 0).ToList();
Assert.NotEmpty(dataLabels);
Assert.All(dataLabels, label => Assert.EndsWith("orders", label.Text));
}
[Fact]
public void Column_Series_Multiple_Axes_Still_Render_On_Right()
{
using var ctx = CreateChartContext();
var chart = ctx.RenderComponent<RadzenChart>(parameters => parameters
.AddChildContent<RadzenColumnSeries<Item>>(series => series
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Revenue))
.Add(p => p.Data, Data))
.AddChildContent<RadzenColumnSeries<Item>>(series => series
.Add(p => p.CategoryProperty, nameof(Item.Month))
.Add(p => p.ValueProperty, nameof(Item.Count))
.Add(p => p.ValueAxisName, "count")
.Add(p => p.Data, Data))
.AddChildContent<RadzenValueAxis>(a => a
.Add(p => p.Name, "count")));
Assert.NotEmpty(chart.FindAll("g.rz-value-axis-right"));
Assert.Empty(chart.FindAll("g.rz-value-axis-top"));
var additionalScale = chart.Instance.GetValueScale("count");
Assert.Equal(chart.Instance.ValueScale.Output.Start, additionalScale.Output.Start);
Assert.Equal(chart.Instance.ValueScale.Output.End, additionalScale.Output.End);
}
}

View File

@@ -788,9 +788,10 @@ namespace Radzen.Blazor
var vs = chart.GetValueScale(ValueAxisName);
if (chart.ShouldInvertAxes())
{
var categoryAccessor = Category(vs);
X = e => chart.CategoryScale.Scale(Value(e));
Y = e => vs.Scale(categoryAccessor(e));
var invertedValueScale = chart.GetInvertedValueScale(ValueAxisName);
var categoryAccessor = Category(chart.ValueScale);
X = e => invertedValueScale.Scale(Value(e));
Y = e => chart.ValueScale.Scale(categoryAccessor(e));
}
else
{
@@ -833,11 +834,12 @@ namespace Radzen.Blazor
var vs = chart.GetValueScale(ValueAxisName);
if (chart.ShouldInvertAxes())
{
var categoryAccessor = Category(vs);
var invertedValueScale = chart.GetInvertedValueScale(ValueAxisName);
var categoryAccessor = Category(chart.ValueScale);
foreach (var item in Items)
{
var px = chart.CategoryScale.Scale(Value(item));
var py = vs.Scale(categoryAccessor(item));
var px = invertedValueScale.Scale(Value(item));
var py = chart.ValueScale.Scale(categoryAccessor(item));
result.Add(new Point { X = px, Y = py });
}
}

View File

@@ -18,10 +18,12 @@
return _ => { };
}
var value = ComposeValue(categoryScale);
var category = ComposeCategory(valueScale);
var ticks = chart.CategoryScale.Ticks(chart.ValueAxis.TickDistance);
var x0 = chart.CategoryScale.Scale(Math.Max(0, ticks.Start));
var vs = chart.GetInvertedValueScale(ValueAxisName);
var va = chart.GetValueAxis(ValueAxisName);
var value = ComposeValue(vs);
var category = ComposeCategory(chart.ValueScale);
var ticks = vs.Ticks(va.TickDistance);
var x0 = vs.Scale(Math.Max(0, ticks.Start));
var style = $"clip-path: url(#{chart.ClipPath}); -webkit-clip-path: url(#{chart.ClipPath});";
var barSeries = VisibleBarSeries;

View File

@@ -218,7 +218,7 @@ namespace Radzen.Blazor
return 0;
}
var value = chart.CategoryScale.Compose(Value);
var value = chart.GetInvertedValueScale(ValueAxisName).Compose(Value);
var x = value(item);
return x;
@@ -233,7 +233,8 @@ namespace Radzen.Blazor
return string.Empty;
}
return chart.ValueAxis.Format(chart.CategoryScale, chart.CategoryScale.Value(Value(item)));
var scale = chart.GetInvertedValueScale(ValueAxisName);
return chart.GetValueAxis(ValueAxisName).Format(scale, scale.Value(Value(item)));
}
/// <inheritdoc />
@@ -258,10 +259,11 @@ namespace Radzen.Blazor
return (default!, new Point());
}
var value = ComposeValue(chart.CategoryScale);
var vs = chart.GetInvertedValueScale(ValueAxisName);
var value = ComposeValue(vs);
var category = ComposeCategory(chart.ValueScale);
var ticks = chart.CategoryScale.Ticks(chart.ValueAxis.TickDistance);
var x0 = chart.CategoryScale.Scale(Math.Max(0, ticks.Start));
var ticks = vs.Ticks(chart.GetValueAxis(ValueAxisName).TickDistance);
var x0 = vs.Scale(Math.Max(0, ticks.Start));
var barSeries = VisibleBarSeries;
var index = barSeries.IndexOf(this);
@@ -336,8 +338,9 @@ namespace Radzen.Blazor
{
const double gap = 8;
const double verticalGap = 16;
var ticks = chart.CategoryScale.Ticks(chart.ValueAxis.TickDistance);
var x0 = chart.CategoryScale.Scale(Math.Max(0, ticks.Start));
var vs = chart.GetInvertedValueScale(ValueAxisName);
var ticks = vs.Ticks(chart.GetValueAxis(ValueAxisName).TickDistance);
var x0 = vs.Scale(Math.Max(0, ticks.Start));
foreach (var d in Data)
{
@@ -361,7 +364,7 @@ namespace Radzen.Blazor
Anchor = new Point() { X = end, Y = anchorY },
Value = value,
TextAnchor = textAnchor,
Text = chart.ValueAxis.Format(chart.CategoryScale, value)
Text = chart.GetValueAxis(ValueAxisName).Format(vs, value)
});
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using System.Linq;
@@ -475,6 +476,17 @@ namespace Radzen.Blazor
return AdditionalValueAxes.TryGetValue(name, out var axis) ? axis : ValueAxis;
}
internal ScaleBase GetInvertedValueScale(string? name)
{
if (string.IsNullOrEmpty(name))
{
return CategoryScale;
}
return AdditionalValueScales.TryGetValue(name, out var scale) ? scale : CategoryScale;
}
internal void AddSeries(IChartSeries series)
{
if (!Series.Contains(series))
@@ -557,6 +569,8 @@ namespace Radzen.Blazor
visibleSeries.Add(invisibleSeries.Last());
}
var invertAxes = ShouldInvertAxes();
// Partition series: primary axis vs named axes
var primarySeries = new List<IChartSeries>();
var namedAxisSeries = new Dictionary<string, List<IChartSeries>>();
@@ -579,16 +593,30 @@ namespace Radzen.Blazor
}
}
// All series contribute to category scale
// All series contribute to the shared category scale (bar-family series swap their transforms when axes are inverted)
foreach (var series in visibleSeries)
{
CategoryScale = series.TransformCategoryScale(CategoryScale);
if (invertAxes)
{
ValueScale = series.TransformValueScale(ValueScale);
}
else
{
CategoryScale = series.TransformCategoryScale(CategoryScale);
}
}
// Primary series contribute to primary value scale
foreach (var series in primarySeries)
{
ValueScale = series.TransformValueScale(ValueScale);
if (invertAxes)
{
CategoryScale = series.TransformCategoryScale(CategoryScale);
}
else
{
ValueScale = series.TransformValueScale(ValueScale);
}
}
// Named-axis series contribute to their own scales
@@ -630,7 +658,7 @@ namespace Radzen.Blazor
foreach (var series in entry.Value)
{
additionalScale = series.TransformValueScale(additionalScale);
additionalScale = invertAxes ? series.TransformCategoryScale(additionalScale) : series.TransformValueScale(additionalScale);
}
AdditionalValueScales[axisName] = additionalScale;
@@ -648,7 +676,7 @@ namespace Radzen.Blazor
AxisBase xAxis = CategoryAxis;
AxisBase yAxis = ValueAxis;
if (ShouldInvertAxes())
if (invertAxes)
{
xAxis = ValueAxis;
yAxis = CategoryAxis;
@@ -699,20 +727,29 @@ namespace Radzen.Blazor
var valueAxisSize = ValueAxis.Measure(this);
var categoryAxisSize = CategoryAxis.Measure(this);
// Measure additional axes for right margin
// Measure additional axes for the right margin (top margin when axes are inverted)
double additionalAxesWidth = 0;
double additionalAxesHeight = 0;
foreach (var entry in AdditionalValueAxes)
{
additionalAxesWidth += entry.Value.Measure(this, GetValueScale(entry.Key));
if (invertAxes)
{
additionalAxesHeight += entry.Value.MeasureHorizontal(GetValueScale(entry.Key));
}
else
{
additionalAxesWidth += entry.Value.Measure(this, GetValueScale(entry.Key));
}
}
if (!ShouldRenderAxes())
{
valueAxisSize = categoryAxisSize = 0;
additionalAxesWidth = 0;
additionalAxesHeight = 0;
}
MarginTop = 32;
MarginTop = 32 + additionalAxesHeight;
if (IsRTL)
{
@@ -746,7 +783,7 @@ namespace Radzen.Blazor
{
if (legendPosition == LegendPosition.Top)
{
MarginTop = legendSize + 16;
MarginTop = legendSize + 16 + additionalAxesHeight;
}
else
{
@@ -816,11 +853,23 @@ namespace Radzen.Blazor
foreach (var entry in AdditionalValueScales)
{
var axis = GetValueAxis(entry.Key);
entry.Value.Output = new ScaleRange
if (invertAxes)
{
Start = axis.Inverted ? valueEnd : valueStart,
End = axis.Inverted ? valueStart : valueEnd
};
var reversed = axis.Inverted != IsRTL;
entry.Value.Output = new ScaleRange
{
Start = reversed ? categoryEnd : categoryStart,
End = reversed ? categoryStart : categoryEnd
};
}
else
{
entry.Value.Output = new ScaleRange
{
Start = axis.Inverted ? valueEnd : valueStart,
End = axis.Inverted ? valueStart : valueEnd
};
}
entry.Value.Fit(axis.TickDistance);
}
@@ -857,6 +906,20 @@ namespace Radzen.Blazor
Start = catReversed ? ce : cs,
End = catReversed ? cs : ce
};
if (ShouldInvertAxes())
{
foreach (var entry in AdditionalValueScales)
{
var axis = GetValueAxis(entry.Key);
var reversed = axis.Inverted != IsRTL;
entry.Value.Output = new ScaleRange
{
Start = reversed ? ce : cs,
End = reversed ? cs : ce
};
}
}
}
if (height != Height)
@@ -873,14 +936,17 @@ namespace Radzen.Blazor
End = valReversed ? vs : ve
};
foreach (var entry in AdditionalValueScales)
if (!ShouldInvertAxes())
{
var axis = GetValueAxis(entry.Key);
entry.Value.Output = new ScaleRange
foreach (var entry in AdditionalValueScales)
{
Start = axis.Inverted ? ve : vs,
End = axis.Inverted ? vs : ve
};
var axis = GetValueAxis(entry.Key);
entry.Value.Output = new ScaleRange
{
Start = axis.Inverted ? ve : vs,
End = axis.Inverted ? vs : ve
};
}
}
}
@@ -1615,17 +1681,48 @@ namespace Radzen.Blazor
/// The plot area and axes are included; legends and tooltips are HTML overlays and are not included.
/// </summary>
/// <param name="fileName">The download file name. Default is <c>chart.png</c>.</param>
public async Task ToPng(string fileName = "chart.png")
/// <param name="width">The PNG width in pixels. When omitted the rendered size scaled by the device pixel ratio is used.</param>
/// <param name="height">The PNG height in pixels. When omitted it is derived from <paramref name="width"/> preserving the aspect ratio.</param>
public async Task ToPng(string fileName = "chart.png", int? width = null, int? height = null)
{
ArgumentNullException.ThrowIfNull(fileName);
if (IsJSRuntimeAvailable && JSRuntime != null)
{
var svg = await ToSvg();
await JSRuntime.InvokeVoidAsync("Radzen.downloadSvgAsPng", svg, fileName);
await JSRuntime.InvokeVoidAsync("Radzen.downloadSvgAsPng", svg, fileName, width, height);
}
}
/// <summary>
/// Renders the chart as a PNG image and returns the image data.
/// The plot area and axes are included; legends and tooltips are HTML overlays and are not included.
/// Use this to store the chart in a database, embed it in documents, or send it to an API instead of downloading it.
/// </summary>
/// <param name="width">The PNG width in pixels. When <c>null</c> the rendered size scaled by the device pixel ratio is used.</param>
/// <param name="height">The PNG height in pixels. When <c>null</c> it is derived from <paramref name="width"/> preserving the aspect ratio.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> representing the asynchronous operation. The task result contains the PNG image data.
/// </returns>
public async Task<byte[]> ToPng(int? width, int? height = null)
{
if (IsJSRuntimeAvailable && JSRuntime != null)
{
var svg = await ToSvg();
if (!string.IsNullOrEmpty(svg))
{
await using var png = await JSRuntime.InvokeAsync<IJSStreamReference>("Radzen.svgToPng", svg, width, height);
using var stream = await png.OpenReadStreamAsync(maxAllowedSize: 32 * 1024 * 1024);
using var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
return Array.Empty<byte>();
}
/// <inheritdoc />
public override void Dispose()
{
@@ -1882,7 +1979,7 @@ namespace Radzen.Blazor
var categoryCrosshair = CategoryAxis?.Crosshair;
var hoveredValueAxis = GetValueAxis((HoveredSeries as IChartValueAxisSeries)?.ValueAxisName);
var hoveredValueScale = GetValueScale((HoveredSeries as IChartValueAxisSeries)?.ValueAxisName);
var hoveredValueScale = ShouldInvertAxes() ? ValueScale : GetValueScale((HoveredSeries as IChartValueAxisSeries)?.ValueAxisName);
var valueCrosshair = hoveredValueAxis?.Crosshair;
var snapX = categoryCrosshair?.Snap ?? true;

View File

@@ -35,6 +35,16 @@
return AxisMeasurer.YAxis(scale, this, Title);
}
internal double MeasureHorizontal(ScaleBase scale)
{
if (Visible == false)
{
return 0;
}
return AxisMeasurer.XAxis(scale, this, Title);
}
internal override double Size
{
get

View File

@@ -1,6 +1,40 @@
@inherits RadzenChartComponentBase
@if (YAxis?.Visible == true)
@if (YAxis?.Visible == true && renderHorizontal)
{
<g class="rz-axis rz-value-axis rz-value-axis-top">
<Line class="rz-line" X1="@Math.Min(x1, x2)" Y1="@axisY" X2="@Math.Max(x1, x2)" Y2="@axisY" Stroke="@YAxis.Stroke" StrokeWidth="@YAxis.StrokeWidth" LineType="@YAxis.LineType" />
@foreach (var idx in tickValues)
{
var value = scale?.Value(idx);
var x = scale?.Scale(idx) ?? 0;
var text = value != null && YAxis != null && scale != null ? YAxis.Format(scale, value) : "";
if (YAxis?.Ticks?.Template != null)
{
var context = new TickTemplateContext { Text = text, Value = value, X = x, Y = axisY };
<CategoryAxisTick X="@x" Y="@axisY" Text="@text" Stroke="@(YAxis.Ticks.Stroke ?? YAxis.Stroke)" StrokeWidth="@YAxis.Ticks.StrokeWidth" LineType="@YAxis.Ticks.LineType" TopSide="true">
@YAxis.Ticks.Template(context)
</CategoryAxisTick>
}
else if (!String.IsNullOrEmpty(text))
{
<CategoryAxisTick X="@x" Y="@axisY" Text="@text" Stroke="@(YAxis?.Ticks?.Stroke ?? YAxis?.Stroke)" StrokeWidth="@(YAxis?.Ticks?.StrokeWidth ?? 0)" LineType="@(YAxis?.Ticks?.LineType ?? LineType.Solid)" TopSide="true" />
}
if (YAxis?.GridLines?.Visible == true && idx > start)
{
<Line class="rz-grid-line" X1="@x" Y1="@Math.Min(gridY1, gridY2)" X2="@x" Y2="@Math.Max(gridY1, gridY2)" Stroke="@(YAxis.GridLines.Stroke)" StrokeWidth="@(YAxis.GridLines.StrokeWidth)" LineType="@(YAxis.GridLines.LineType)" />
}
}
@if (!String.IsNullOrEmpty(YAxis?.Title?.Text))
{
<CategoryAxisTitle X="@(Math.Abs(x2 - x1) / 2)" Y="@(axisY - horizontalSize + (YAxis?.Title?.Size ?? 0))" Text="@(YAxis?.Title?.Text ?? "")" />
}
</g>
}
else if (YAxis?.Visible == true)
{
<g class="rz-axis rz-value-axis @(RenderAsRightSide ? "rz-value-axis-right" : "")">
<Line class="rz-line" X1="@axisX" Y1="@Math.Min(y1, y2)" X2="@axisX" Y2="@Math.Max(y1, y2)" Stroke="@YAxis.Stroke" StrokeWidth="@YAxis.StrokeWidth" LineType="@YAxis.LineType" />
@@ -57,12 +91,17 @@
double y1;
double y2;
double axisX;
double axisY;
double gridX1;
double gridX2;
double gridY1;
double gridY2;
double titleX;
double horizontalSize;
ScaleBase? scale;
bool isLogarithmic;
bool isFlippedToRight;
bool renderHorizontal;
private AxisBase? XAxis { get; set; }
private AxisBase? YAxis { get; set; }
@@ -73,6 +112,39 @@
return;
}
renderHorizontal = IsRightSide && Chart.ShouldInvertAxes();
if (renderHorizontal)
{
var axis = Chart.GetValueAxis(AxisName);
YAxis = axis;
scale = Chart.GetValueScale(AxisName);
var horizontalTicks = scale.Ticks(axis.TickDistance);
start = horizontalTicks.Start;
end = horizontalTicks.End;
step = horizontalTicks.Step;
tickValues = scale.TickValues(axis.TickDistance).ToList();
x1 = scale.Scale(start);
x2 = scale.Scale(end);
var verticalTicks = Chart.ValueScale.Ticks(Chart.CategoryAxis.TickDistance);
gridY1 = Chart.ValueScale.Scale(verticalTicks.Start);
gridY2 = Chart.ValueScale.Scale(verticalTicks.End);
axisY = 0;
foreach (var entry in Chart.AdditionalValueAxes)
{
if (entry.Key == AxisName) break;
axisY -= entry.Value.MeasureHorizontal(Chart.GetValueScale(entry.Key));
}
horizontalSize = axis.MeasureHorizontal(scale);
return;
}
if (IsRightSide)
{
YAxis = Chart.GetValueAxis(AxisName);