图表:在时间图中将布尔标志显示为填充的矩形(而不是线条)

Chart : displaying boolean flags in a time plot as filled rectangles (instead of as lines)

我使用 WinForms 中的 C# Chart 使用 "line" 图表类型实时绘制各种变量。这对模拟值很有效,但对 on/off 标志不太理想。

我想将多个标志绘制为水平条,当值为“1”时填充,当值为“0”时清除。

在我从头开始编写解决方案之前,您对我如何利用 "chart" 对象的任何功能来更有效地实现它有什么建议吗?

编辑:我正在玩 Area 类型,它似乎很有前途。 编辑 2:那没有用,因为 Area 类型中的区域总是从图表底部开始,隐藏了其他行。我现在正在尝试范围列类型

有几种方法可以解决这个问题:StackedBarsAreaChartAnnotations,但我认为到目前为止最简单的方法是使用 LineChartType.

第一个问题是:如何创造差距?最简单的方法是将它们绘制为线,但使用 Color.Transparent。因此,我们没有使用标志值作为我们的 y 值,而是使用它来设置颜色..

所以我们可以使用这样的函数:

void AddFlagLine(Chart chart, int series, int flag, int x)
{
    Series s = chart.Series[series];
    int px = s.Points.AddXY(x, series);
    s.Points[px].Color = s.Color;
    if (px > 0) s.Points[px - 1].Color = flag == 1 ? s.Color : Color.Transparent;
}

它获取您的 Series 的索引并使用 flag 来确定颜色;请注意,线段的颜色由端点的颜色控制

所以如果你想让从新点出来的线有它的标志颜色,你需要在添加下一个时设置它..

这很简单,对于粗至 1-10 的行,它工作正常。但是如果你想要更大的宽度,事情就会变得有点难看..:[=​​25=]

圆帽开始变得越来越大,直到它们实际接触,或多或少地填补了空隙。

不幸的是,似乎没有办法控制线条的大写样式。有很多 CustomAttributes 包括 DashStyles 但不是这个。所以我们不得不求助于业主绘图。这对于折线图来说相当简单。这是一个例子:

xxxPaint 事件如下所示:

private void chart_PostPaint(object sender, ChartPaintEventArgs e)
{
    Graphics g = e.ChartGraphics.Graphics;
    Axis ax = chart.ChartAreas[0].AxisX;
    Axis ay = chart.ChartAreas[0].AxisY;

    for (int si = 0; si < chart.Series.Count; si++ )
    {
        Series s = chart.Series[si];
        for (int pi = 1; pi < s.Points.Count - 1; pi++)
        {
            DataPoint dp = s.Points[pi];
            int y = (int) ay.ValueToPixelPosition(dp.YValues[0]+1);       ///*1*
            int x0 = (int)ax.ValueToPixelPosition(ax.Minimum);
            int x1 = (int)ax.ValueToPixelPosition(s.Points[pi-1].XValue); ///*2*
            int x2 = (int)ax.ValueToPixelPosition(dp.XValue);
            x1 = Math.Max(x1, x0);
            x2 = Math.Max(x2, x0);

            using (Pen pen = new Pen(dp.Color, 40)                        ///*3*
            { StartCap = System.Drawing.Drawing2D.LineCap.Flat,
                EndCap = System.Drawing.Drawing2D.LineCap.Flat })
            {
                g.DrawLine(pen, x1, y, x2, y);
            }
        }
    }

一些注意事项:

  • 1 : 我决定将系列向上移动一个;这取决于您,就像使用或关闭 y 轴标签或用自定义标签替换它们一样..

  • 2 :这里我们使用前一个点的 x 位置!

  • 3 :请注意,不是硬编码 40 像素的宽度,您实际上应该决定计算出的宽度。这是一个几乎填满区域的例子:

    int 宽度 = (int)( ( ay.ValueToPixelPosition(ay.Minimum) - ay.ValueToPixelPosition(ay.Maximum)) / (chart7.Series.Count + 2));

你可以扭曲是通过添加少于或多于 2 来填充更多或更少。

我把所有的BorderWidths都变成了0所以只有画出来的线显示了。

我知道了:

事实证明这很容易;我使用了范围列类型。

A) 设置(完成一次):

plotChart.Series[chanNo].ChartType = SeriesChartType.RangeColumn;
plotChart.Series[chanNo].CustomProperties = "PointWidth=" + noOfFlags;

PointWidth 需要设置每个矩形的相对宽度,使其填满一个数据点的整个宽度(如果太小,则横条有空隙;如果太大,则有重叠)。 noOfFlags 是显示的标志数(在上面显示的示例中,noOfFlags = 4)。 (顺便提一下MSDN documentation is wrong:PointWidth不限于2。)

B) 绘图(为每个新数据点完成):

baseLine--;
int barHeight = flagHigh ? 1 : 0;
plotChart.Series[chanNo].Points.AddXY(pointX, baseLine, baseLine + barHeight);

flagHigh 是一个布尔值,等于被监控的标志。

baseLine 对每条轨迹递减。在上面的示例中,baseLine 从 4 开始,递减到 0。

请注意,对于每个数据点,RangeColumn 需要 2 个 "Y" 值:一个用于矩形底部,一个用于顶部;在代码中,我将底部 Y 设置为我用于该特定标志的行的底部,并将顶部设置为底部上方的 1,使高度为 1。