65.9K
CodeProject 正在变化。 阅读更多。
Home

多平台雷达图

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2018 年 7 月 23 日

MIT

4分钟阅读

viewsIcon

19185

downloadIcon

720

为各种 .NET 平台实现的雷达图

引言

市面上有许多商业和免费的图表实现。雷达图(有时也称为网络图)是一种不太常见的图表类型。这种图表可以用于展示数据,同时也非常适合作为用户控件来输入各种类型的变量(例如:https://multi-sense.renault.pl/megane/pl_PL/)。

本文介绍的雷达图是开源库 Manufaktura.Controlshttp://musicengravingcontrols.com/)的一部分。该库尤其以其音乐记谱组件(本文中有介绍:https://codeproject.org.cn/Articles/1252423/Music-Notation-in-NET)而闻名,但也包含在其他领域有用的工具和控件。

与库中的所有内容一样,雷达图采用跨平台方式编写。其实现方式类似于乐谱渲染器(参见此处:https://codeproject.org.cn/Articles/1252423/Music-Notation-in-NET)——基类处理大部分计算,以正确地定位图表元素,如坐标轴、样本点等。然后,特定平台的类负责在先前计算好的坐标上进行绘制。

本示例包含一个功能完整的 WPF 控件和一个用于 ASP.NET MVC(作为 Razor 扩展)的简单概念验证实现。

架构

基类和 WPF 实现

基类 RadarChartRenderer 包含一个公共方法 RedrawChart,用于绘制给定样本集的图表。它还包含以下抽象方法:

  • ClearCanvas – 清除画布
  • DrawAxisLabel  - 在给定坐标处绘制坐标轴标签
  • DrawAxisLine – 在给定坐标处绘制坐标轴线
  • DrawPolygon – 在给定坐标处绘制多边形
  • DrawSample – 在给定坐标处绘制样本点(通常表示为圆)
  • DrawTick – 仅当轴的数量小于三个时调用。绘制刻度线
  • DrawWebLine – 仅当轴的数量等于或大于三个时调用——绘制刻度之间的线

RadarChartRenderer 负责在画布表面上正确放置元素所需的所有计算。然后,它会调用具有特定平台实现的抽象方法。它还需要两个泛型参数:

  • TControl – 代表图表控件或包含图表设置的对象
  • TCanvas – 将执行渲染的画布对象。它可以是任何对象,如控件、XML 元素、StringBuilder 等。

让我们来看一下 WPFRadarChart 渲染器。它在 RadarChart 控件的 Canvas 元素上进行绘制。它还包含了 RadarChartRenderer 抽象类的实现。这是一个绘制坐标轴线的示例:

protected override void DrawAxisLine(Primitives.Point start, Primitives.Point end)
{
    var line = new Line();
    line.SetBinding(Shape.StrokeProperty, 
        new Binding(nameof(RadialChart.AxisStroke)) { Source = Control });
    line.SetBinding(Shape.StrokeThicknessProperty, 
        new Binding(nameof(RadialChart.AxisStrokeThickness)) { Source = Control });
    line.X1 = start.X;
    line.Y1 = start.Y;
    line.X2 = end.X;
    line.Y2 = end.Y;
    Canvas.Children.Add(line);
}

正如我们所见,创建了一个 Line 对象并将其添加到画布中。此示例使用数据绑定机制将线条参数(如粗细)绑定到控件属性。

由于基于数据绑定的实现,该图表具有完全的交互性。用户可以在图表上拖动样本点,值的变化会立即反映在模型中,该模型是绑定到 RadarChart 控件的 Samples 属性的 RadarChartSamples 集合。

RadarChartSample 类具有以下属性:

  • AxisDisplayName, AxisShortName - 轴的短名称和全称
  • Value - 样本值
  • ValidationMinValue, ValidationMaxValue - 定义有效值范围的边界,在上面的示例中显示为绿色多边形
  • ValidationCompartments - 功能类似于 ValidationMinValueValidationMaxValue,但允许程序员在同一轴上指定多个有效值范围(例如 3-10、15-20 等)。
  • Scale - 如果您想创建一个在不同轴上展示不同数量级值的图表,这个属性会很有用。例如:图表的 MaxValue 属性值为 100,在一个轴上,您有一个变量取值范围是 0100。另一个轴展示一个变量,其值范围是 0 到 10。您可能希望为另一个轴设置 Scale10,以获得更好的用户体验(用户将有完整的轴用于拖动样本点)。

Razor 实现

该项目的架构允许程序员使用单一代码库快速地在其他平台上实现雷达图。我创建了一个 HtmlSvgRadarChartRenderer 类作为该架构的概念验证。此类将图表渲染为 HTML SVG 元素。

HtmlSvgRadarChartRenderer : RadarChartRenderer<HtmlRadarChartRendererSettings, XElement>

我只实现了最重要的几个方法,例如 DrawAxisLine

protected override void DrawAxisLine(Point start, Point end)
{
    var element = new XElement("line",
    new XAttribute("x1", start.X.ToStringInvariant()),
    new XAttribute("y1", start.Y.ToStringInvariant()),
    new XAttribute("x2", end.X.ToStringInvariant()),
    new XAttribute("y2", end.Y.ToStringInvariant()),
    new XAttribute("style", Control.AxisLinePen.ToCss()));
    Canvas.Add(element);
}

我们还需要编写 Razor 扩展才能在视图中使用该图表。

[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", 
Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString RadialChartFor<TModel>(this HtmlHelper<TModel> htmlHelper, 
Expression<Func<TModel, RadarChartSample[]>> expression, HtmlRadarChartRendererSettings settings)
{
    if (expression == null) throw new ArgumentNullException(nameof(expression));
    if (settings == null) throw new ArgumentNullException(nameof(settings));

    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    RadarChartSample[] samples = metadata.Model == null ? null : metadata.Model as RadarChartSample[];
    return RadialChartHelper(htmlHelper, samples, settings);
}

private static MvcHtmlString RadialChartHelper
(HtmlHelper helper, RadarChartSample[] samples, HtmlRadarChartRendererSettings settings)
{
    var xElement = new XElement("svg");
    xElement.Add(new XAttribute("style", 
    $"width:{settings.Width.ToStringInvariant()}px; height:{settings.Height.ToStringInvariant()}px;"));
    new HtmlSvgRadarChartRenderer(settings, xElement).RedrawChart(samples);
    return MvcHtmlString.Create(xElement.ToString());
}

现在我们可以将控件添加到视图中了。

<div>
    @Html.RadialChartFor(x => x.Samples, Model.RadialChartSettings)
</div>

在没有过多样式的情况下,效果如下:

现在,程序员只需正确设置控件样式,并根据需要处理用户交互逻辑。

© . All rights reserved.