图表:在时间图中将布尔标志显示为填充的矩形(而不是线条)
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 类型中的区域总是从图表底部开始,隐藏了其他行。我现在正在尝试范围列类型
有几种方法可以解决这个问题:StackedBars
、AreaChart
、Annotations
,但我认为到目前为止最简单的方法是使用 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。
我使用 WinForms 中的 C# Chart 使用 "line" 图表类型实时绘制各种变量。这对模拟值很有效,但对 on/off 标志不太理想。
我想将多个标志绘制为水平条,当值为“1”时填充,当值为“0”时清除。
在我从头开始编写解决方案之前,您对我如何利用 "chart" 对象的任何功能来更有效地实现它有什么建议吗?
编辑:我正在玩 Area 类型,它似乎很有前途。 编辑 2:那没有用,因为 Area 类型中的区域总是从图表底部开始,隐藏了其他行。我现在正在尝试范围列类型
有几种方法可以解决这个问题:StackedBars
、AreaChart
、Annotations
,但我认为到目前为止最简单的方法是使用 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。