StackedBar 不同系列之间的边界
StackedBar borders between different series
我想要的是在 StackedBar 中设置两个系列之间的边界 就像这张图片 蓝色和绿色之间的粗黑线
我想不出任何指定边框的想法,我试图通过这段代码为系列设置边框
chart.Series["series0"].BorderWidth = 2;
chart.Series["series0"].BorderColor = Color.Black;
chart.Series["series0"].BorderDashStyle = ChartDashStyle.Solid;
但这是我得到的结果
这是我的代码
double l = Convert.ToDouble(query1[i - 1][0]) - 10;
string n = query1[i - 1][1];
int count = 0;
for (double t = l; t < l + 10; t++)
{
//Next line Calc. the occurence of character in a text file
count = n.Split('C').Length - 1;
//Multiple the occurence by 10 so it become percent
chart.Series["series0"].Points.AddXY(t, count * 10);
chart.Series["series0"]["PointWidth"] = "1";
chart.Series["series0"].BorderWidth = 2;
chart.Series["series0"].BorderColor = Color.Black;
chart.Series["series0"].BorderDashStyle = ChartDashStyle.Solid;
count = n.Split('o').Length - 1;
chart.Series["series1"].Points.AddXY(t, count * 10);
chart.Series["series1"]["PointWidth"] = "1";
}
如何使用StackedBar实现首图效果? ,如果我不能使用 StackedBar,你建议使用什么图表类型??
没有内置图表元素可以很容易地成为这两个 系列 之间的分界线。 (创建 LineAnnotations
来实现这一点将是一场噩梦..)
所以添加线条的方法是将它们绘制到Chart
的表面上。这在 PostPaint
事件中最自然地完成,仅提供此类装饰。
此处 Axes
具有方便的函数,可在数据值和像素位置之间进行转换。我们需要 ValueToPixelPosition
方法。
我将带您了解 Chart
绘图的变体,随着我们接近最终版本,这些变体会逐渐变得更加复杂......:[=40=]
让我们从一个简单的例子开始:让我们构建并装饰一个 StackedArea
图表;这是绘图代码:
private void chart2_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s = chart1.Series[0];
ChartArea ca = chart1.ChartAreas[0];
var pp = s.Points.Select(x=>
new PointF( (float)ca.AxisX.ValueToPixelPosition(x.XValue),
(float)ca.AxisY.ValueToPixelPosition(x.YValues[0]) ) );
if (s.Points.Count > 1)
using (Pen pen = new Pen(Color.DarkOliveGreen, 4f))
e.ChartGraphics.Graphics.DrawLines(pen, pp.ToArray());
}
Points.Select
实际上只是一个 shorthand for 循环;所以在创建像素点列表后我们简单地绘制它。
现在,如您所见,StackedArea
图表很尖,看起来不像 StackedBar
或 StackedColumn
图表。因此,让我们通过添加一些额外的点来作弊 'rectify' 面积图:
void rectifyArea(Series s)
{
for (int i = s.Points.Count - 1; i > 0; i--)
s.Points.InsertXY(i, i - 1, s.Points[i].YValues[0]);
}
结果:
现在这并不难;不幸的是,你不能将 StackedArea
从左到右而不是从下到上。所以我们最终需要将图表类型更改为 Bar
类型..
这里的挑战是找到这些柱状图的右上角和下角 角。我们确实有 DataPoint
值,但这些值位于条形的 中间 中。因此,我们需要 add/subtract 条形宽度的一半 以获得 角 。为此,我们需要 width。
虽然你已经设置了PointWidth
属性到1
,但我们真正需要的是像素宽度。我们最好通过减去两个相邻点的像素坐标来得到它。
这会使 PostPaint
事件稍微长一些,但仍然不会过于复杂;我们将从 StackedColumn
图表开始,为每个数据点添加两个角点:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s = chart1.Series[0];
ChartArea ca = chart1.ChartAreas[0];
if (s.Points.Count <= 0) return;
// calculate width of a column:
int pp1 = (int)ca.AxisX.ValueToPixelPosition(s.Points[0].XValue);
int pp2 = (int)ca.AxisX.ValueToPixelPosition(s.Points[1].XValue);
float w2 = Math.Abs(pp2 - pp1) / 2f;
List<PointF> points = new List<PointF>();
for (int i = 0; i < s.Points.Count; i++)
{
DataPoint dp = s.Points[i];
points.Add(new PointF( (int)ca.AxisX.ValueToPixelPosition(dp.XValue) - w2,
(int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]) ));
points.Add(new PointF( (int)ca.AxisX.ValueToPixelPosition(dp.XValue) + w2,
(int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]) ));
}
if (points.Count > 1)
using (Pen pen = new Pen(Color.DarkOliveGreen, 4f))
e.ChartGraphics.Graphics.DrawLines(pen, points.ToArray());
}
现在这看起来与我们的 'rectified area chart' 的假版本几乎一模一样。我们需要更改什么才能将其应用于 StackedBar
图表?几乎没有!我们需要注意的唯一两件事是
- y轴的方向。由于点向上移动但 GDI+ 图形的像素坐标向下移动,我们需要以相反的顺序创建两个角点。
- 我们需要反转 x 和 y 坐标,因为所有类型的
Bar
图表的轴都是反转的。
这是两个带边框的堆叠图表:
这是 StackBar
图表的循环:
for (int i = 0; i < s.Points.Count; i++)
{
points.Add(new PointF( (float)ca.AxisY.ValueToPixelPosition(s.Points[i].YValues[0]),
(float)ca.AxisX.ValueToPixelPosition(s.Points[i].XValue) + w2));
points.Add(new PointF( (float)ca.AxisY.ValueToPixelPosition(s.Points[i].YValues[0]),
(float)ca.AxisX.ValueToPixelPosition(s.Points[i].XValue) - w2));
}
请注意,我正在使用 4 像素的固定笔宽进行绘图。要使其随 Chart
缩放,您可能需要动态计算笔宽..
更新
要在多个系列之上绘制边框,您可以将代码放入这样的循环中:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Chart chart = chart1;
Series s0 = chart.Series[0];
ChartArea ca = chart.ChartAreas[0];
// calculate width of a bar:
int pp1 = (int)ca.AxisX.ValueToPixelPosition(s0.Points[0].XValue);
int pp2 = (int)ca.AxisX.ValueToPixelPosition(s0.Points[1].XValue);
float delta = Math.Abs(pp2 - pp1) / 2f;
for (int s = 0; s < chart.Series.Count; s++)
{
List<PointF> points = new List<PointF>();
for (int p = 0; p < chart.Series[s].Points.Count; p++)
{
DataPoint dp = chart.Series[s].Points[p];
double v = GetStackTopValue(chart, s, p);
points.Add(new PointF((float)ca.AxisY.ValueToPixelPosition(v),
(float)ca.AxisX.ValueToPixelPosition(dp.XValue) + delta));
points.Add(new PointF((float)ca.AxisY.ValueToPixelPosition(v),
(float)ca.AxisX.ValueToPixelPosition(dp.XValue) - delta));
}
using (Pen pen = new Pen(Color.DarkOliveGreen, 3f))
e.ChartGraphics.Graphics.DrawLines(pen, points.ToArray());
}
}
double GetStackTopValue(Chart chart, int series, int point)
{
double v = 0;
for (int i = 0; i < series + 1; i++)
v += chart.Series[i].Points[point].YValues[0];
return v;
}
我想要的是在 StackedBar 中设置两个系列之间的边界 就像这张图片 蓝色和绿色之间的粗黑线
我想不出任何指定边框的想法,我试图通过这段代码为系列设置边框
chart.Series["series0"].BorderWidth = 2;
chart.Series["series0"].BorderColor = Color.Black;
chart.Series["series0"].BorderDashStyle = ChartDashStyle.Solid;
但这是我得到的结果
这是我的代码
double l = Convert.ToDouble(query1[i - 1][0]) - 10;
string n = query1[i - 1][1];
int count = 0;
for (double t = l; t < l + 10; t++)
{
//Next line Calc. the occurence of character in a text file
count = n.Split('C').Length - 1;
//Multiple the occurence by 10 so it become percent
chart.Series["series0"].Points.AddXY(t, count * 10);
chart.Series["series0"]["PointWidth"] = "1";
chart.Series["series0"].BorderWidth = 2;
chart.Series["series0"].BorderColor = Color.Black;
chart.Series["series0"].BorderDashStyle = ChartDashStyle.Solid;
count = n.Split('o').Length - 1;
chart.Series["series1"].Points.AddXY(t, count * 10);
chart.Series["series1"]["PointWidth"] = "1";
}
如何使用StackedBar实现首图效果? ,如果我不能使用 StackedBar,你建议使用什么图表类型??
没有内置图表元素可以很容易地成为这两个 系列 之间的分界线。 (创建 LineAnnotations
来实现这一点将是一场噩梦..)
所以添加线条的方法是将它们绘制到Chart
的表面上。这在 PostPaint
事件中最自然地完成,仅提供此类装饰。
此处 Axes
具有方便的函数,可在数据值和像素位置之间进行转换。我们需要 ValueToPixelPosition
方法。
我将带您了解 Chart
绘图的变体,随着我们接近最终版本,这些变体会逐渐变得更加复杂......:[=40=]
让我们从一个简单的例子开始:让我们构建并装饰一个 StackedArea
图表;这是绘图代码:
private void chart2_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s = chart1.Series[0];
ChartArea ca = chart1.ChartAreas[0];
var pp = s.Points.Select(x=>
new PointF( (float)ca.AxisX.ValueToPixelPosition(x.XValue),
(float)ca.AxisY.ValueToPixelPosition(x.YValues[0]) ) );
if (s.Points.Count > 1)
using (Pen pen = new Pen(Color.DarkOliveGreen, 4f))
e.ChartGraphics.Graphics.DrawLines(pen, pp.ToArray());
}
Points.Select
实际上只是一个 shorthand for 循环;所以在创建像素点列表后我们简单地绘制它。
现在,如您所见,StackedArea
图表很尖,看起来不像 StackedBar
或 StackedColumn
图表。因此,让我们通过添加一些额外的点来作弊 'rectify' 面积图:
void rectifyArea(Series s)
{
for (int i = s.Points.Count - 1; i > 0; i--)
s.Points.InsertXY(i, i - 1, s.Points[i].YValues[0]);
}
结果:
现在这并不难;不幸的是,你不能将 StackedArea
从左到右而不是从下到上。所以我们最终需要将图表类型更改为 Bar
类型..
这里的挑战是找到这些柱状图的右上角和下角 角。我们确实有 DataPoint
值,但这些值位于条形的 中间 中。因此,我们需要 add/subtract 条形宽度的一半 以获得 角 。为此,我们需要 width。
虽然你已经设置了PointWidth
属性到1
,但我们真正需要的是像素宽度。我们最好通过减去两个相邻点的像素坐标来得到它。
这会使 PostPaint
事件稍微长一些,但仍然不会过于复杂;我们将从 StackedColumn
图表开始,为每个数据点添加两个角点:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s = chart1.Series[0];
ChartArea ca = chart1.ChartAreas[0];
if (s.Points.Count <= 0) return;
// calculate width of a column:
int pp1 = (int)ca.AxisX.ValueToPixelPosition(s.Points[0].XValue);
int pp2 = (int)ca.AxisX.ValueToPixelPosition(s.Points[1].XValue);
float w2 = Math.Abs(pp2 - pp1) / 2f;
List<PointF> points = new List<PointF>();
for (int i = 0; i < s.Points.Count; i++)
{
DataPoint dp = s.Points[i];
points.Add(new PointF( (int)ca.AxisX.ValueToPixelPosition(dp.XValue) - w2,
(int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]) ));
points.Add(new PointF( (int)ca.AxisX.ValueToPixelPosition(dp.XValue) + w2,
(int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]) ));
}
if (points.Count > 1)
using (Pen pen = new Pen(Color.DarkOliveGreen, 4f))
e.ChartGraphics.Graphics.DrawLines(pen, points.ToArray());
}
现在这看起来与我们的 'rectified area chart' 的假版本几乎一模一样。我们需要更改什么才能将其应用于 StackedBar
图表?几乎没有!我们需要注意的唯一两件事是
- y轴的方向。由于点向上移动但 GDI+ 图形的像素坐标向下移动,我们需要以相反的顺序创建两个角点。
- 我们需要反转 x 和 y 坐标,因为所有类型的
Bar
图表的轴都是反转的。
这是两个带边框的堆叠图表:
这是 StackBar
图表的循环:
for (int i = 0; i < s.Points.Count; i++)
{
points.Add(new PointF( (float)ca.AxisY.ValueToPixelPosition(s.Points[i].YValues[0]),
(float)ca.AxisX.ValueToPixelPosition(s.Points[i].XValue) + w2));
points.Add(new PointF( (float)ca.AxisY.ValueToPixelPosition(s.Points[i].YValues[0]),
(float)ca.AxisX.ValueToPixelPosition(s.Points[i].XValue) - w2));
}
请注意,我正在使用 4 像素的固定笔宽进行绘图。要使其随 Chart
缩放,您可能需要动态计算笔宽..
更新
要在多个系列之上绘制边框,您可以将代码放入这样的循环中:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Chart chart = chart1;
Series s0 = chart.Series[0];
ChartArea ca = chart.ChartAreas[0];
// calculate width of a bar:
int pp1 = (int)ca.AxisX.ValueToPixelPosition(s0.Points[0].XValue);
int pp2 = (int)ca.AxisX.ValueToPixelPosition(s0.Points[1].XValue);
float delta = Math.Abs(pp2 - pp1) / 2f;
for (int s = 0; s < chart.Series.Count; s++)
{
List<PointF> points = new List<PointF>();
for (int p = 0; p < chart.Series[s].Points.Count; p++)
{
DataPoint dp = chart.Series[s].Points[p];
double v = GetStackTopValue(chart, s, p);
points.Add(new PointF((float)ca.AxisY.ValueToPixelPosition(v),
(float)ca.AxisX.ValueToPixelPosition(dp.XValue) + delta));
points.Add(new PointF((float)ca.AxisY.ValueToPixelPosition(v),
(float)ca.AxisX.ValueToPixelPosition(dp.XValue) - delta));
}
using (Pen pen = new Pen(Color.DarkOliveGreen, 3f))
e.ChartGraphics.Graphics.DrawLines(pen, points.ToArray());
}
}
double GetStackTopValue(Chart chart, int series, int point)
{
double v = 0;
for (int i = 0; i < series + 1; i++)
v += chart.Series[i].Points[point].YValues[0];
return v;
}