StackedBar不同系列之间的边界

我想要的是在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实现第一个pic效果? ,如果我不能使用StackedBar,你建议使用什么图表类型?

没有内置的图表元素可以很容易地成为这两个系列之间的边界线。 (创建LineAnnotations来实现这一目标将是一场噩梦..)

因此添加线条的方法是将它们绘制到Chart的表面上。 这在PostPaint活动中最自然地完成,仅为此类装饰提供。

Axes具有在数据值和像素位置之间进行转换的便利function。 我们需要ValueToPixelPosition方法。

我将带您了解Chart绘制的变化,随着我们接近最终版本,逐渐变得更加复杂..:

让我们从一个简单的例子开始:让我们构建并装饰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实际上只是一个循环的简写; 所以在创建像素点列表后我们只需绘制它。

现在,正如您所看到的, StackedArea图表是尖的,看起来不像StackedBarStackedColumn图表。 因此,让我们通过添加一些额外的点来欺骗并“纠正”区域图表:

 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值,但这些值都在条形图的中间 。 所以我们需要加上/减去一半条的宽度来获得 。 为此,我们需要宽度

虽然您已将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 points = new List(); 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()); } 

现在这看起来与我们假装的“整顿区域图表”完全相同。 我们需要更改什么来将其应用于StackedBar图表? 几乎没有! 我们需要照顾的唯一两件事是

  • y轴的方向。 由于点向上移动但GDI + graphhics的像素坐标向下移动,我们需要以相反的顺序创建两个角点。
  • 我们需要反转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 points = new List(); 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; }