StackedBar不同系列之间的边界 [英] StackedBar borders between different series
问题描述
我想要的是在StackedBar中的两个系列之间设置边界像这个图像蓝色和绿色之间的粗黑线
这里是我的代码
double l = Convert.ToDouble(query1 [i-1] [0]) - 10;
string n = query1 [i-1] [1];
int count = 0;
for(double t = 1; t <1 + 10; t ++)
{
//下一行Calc。文本文件中字符的出现
count = n.Split('C')。Length - 1;
//多次出现10,因此它变成百分比
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')。
chart.Series [series1]。Points.AddXY(t,count * 10);
chart.Series [series1] [PointWidth] =1;
}
如何使用StackedBar实现第一个pic效果? ,如果我不能使用StackedBar,你建议使用什么图表类型?
可以很容易地形成这两个系列之间的边界。 (创建 LineAnnotations
以实现这将是一场噩梦..)
所以添加行的方式是将它们绘制到图表
的表面上。这是在 PostPaint
事件中自然完成的,只为这样的装饰。
这里 Axes
具有方便的功能,可以在数据值和像素位置之间进行转换。我们需要 ValueToPixelPosition
方法。
code>绘图逐渐变得有点复杂,因为我们接近的最终版本:
让我们开始一个简单的例子:让我们建立和装饰一个 StackedArea
chart;这里是绘图代码:
private void chart2_PostPaint(object sender,ChartPaintEventArgs e)
{
系列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
是尖的,并且看起来不像 StackedBar
或 StackedColumn
图表。所以让我们通过添加一些额外的点来欺骗和纠正区域图:
void rectifyArea b {
for(int i = s.Points.Count -1; i> 0; i--)
s.Points.InsertXY(i,i-1,s.Points [i] .YValues [0]);
}
结果:
现在这不是那么难;不幸的是你不能把 StackedArea
从左到右而不是自下而上。因此,我们需要将图表类型更改为 Bar
类型。
这里的挑战是找到右上角和下角角。我们有 DataPoint
值,但这些值位于中间。因此,我们需要添加/减去条纹宽度的一半,以获得角点。为此,我们需要 width 。
将 PointWidth
属性设置为 1
,我们真正需要的是像素的宽度。我们最好通过减去两个相邻点的像素坐标得到它。
这使得 PostPaint
,但仍然不过分复杂;我们将从 StackedColumn
图表开始,为每个数据点添加两个角点:
private void graph1_PostPaint(object sender,ChartPaintEventArgs e)
{
系列s = chart1.Series [0];
ChartArea ca = chart1.ChartAreas [0];
if(s.Points.Count< = 0)return;
//计算列的宽度:
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 {
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 +图形的像素坐标向下移动,我们需要以相反的顺序创建两个角点。
- 我们需要反转x和y坐标,因为所有类型的
Bar
图表的轴都是相反的。 li>
以下是带有边框的两个堆叠图表:
这是 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 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像素的固定笔宽度绘制。要使其与图表
缩放,您可能需要动态计算笔宽度。
更新
在顶部绘制边框您可以将代码放入一个循环,如下所示:
private void chart1_PostPaint(object sender,ChartPaintEventArgs e)
{
Chart chart = chart1;
系列s0 = chart.Series [0];
ChartArea ca = chart.ChartAreas [0];
//计算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 {
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));
}
使用(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 v + = chart.Series [i] .Points [point] .YValues [0];
return v;
}
What i want is to set borders between two series in StackedBar Like this image The bold black line between blue and green
I can not figure out any idea to specify the border, i tried to set the borders to the series throuh this code
chart.Series["series0"].BorderWidth = 2;
chart.Series["series0"].BorderColor = Color.Black;
chart.Series["series0"].BorderDashStyle = ChartDashStyle.Solid;
but this the result i got
Here's my code
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";
}
How to achieve the first pic effect using StackedBar ? , if i can not using StackedBar, what chart type you suggest to use ??
There are no built-in chart elements that could easily be made into a borderline between those two Series. (Creating LineAnnotations
to achieve this would be a nightmare..)
So the way to add the lines is to draw them onto the surface of the Chart
. This is most naturally done in the PostPaint
event, provided just for such adornments.
Here the Axes
have handy functions to convert between the data values and the pixel positions. We need the ValueToPixelPosition
method.
I will take you through variations of Chart
drawing that gradually get a little more complicated as we approach the final version..:
Let's start with a simple example: Let's build and adorn a StackedArea
chart; here is the drawing code:
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());
}
The Points.Select
is really just a shorthand for a loop; so after creating the pixel point list we simply draw it.
Now, as you can see, as StackedArea
chart is pointy and doesn't look like a StackedBar
or StackedColumn
chart. So let's cheat and 'rectify' the area chart by adding a few extra points:
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]);
}
Results:
Now that was not so hard; unfortunately you just can't turn a StackedArea
to go from left to right instead of bottom-up. So we need to change the chart type to a Bar
type eventually..
Here the challenge is to find the right upper and lower corners of those bars. We do have the DataPoint
values, but these are in the middle of the bars. So we need to add/subtract half of the Bars' width to get the corners. For this we need the width.
While you have set it with the PointWidth
property to 1
, what we really need is the pixel width. We best get it by subtracting the pixel coordinates of two neighbouring points.
This makes the PostPaint
event a little longer, but still not overly complicated; we will start with a StackedColumn
chart, adding two corner points for each data point:
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());
}
Now this looks pretty much identical to our fake version of the 'rectified area chart'. What will we need to change to apply this to a StackedBar
chart? Almost nothing! The only two things we need to take care of are
- the direction of the y-axis. Since the points move upward but the pixel coordinates of GDI+ graphhics move downwards we need to create the two cornerpoints in the reverse order.
- And we need to reverse the x- and y coodinates, as the axes are reversed for all types of
Bar
charts.
Here are the two stacked charts with a border:
This is the loop for the StackBar
chart:
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));
}
Note that I am drawing with a fixed pen width of 4 pixels. To make it scale with the Chart
you may want to calculate the pen width dynamically..
Update
To draw borders on top of several series you can put the code into a loop like this:
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不同系列之间的边界的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!