StackedBar不同系列之间的边界 [英] StackedBar borders between different series

查看:229
本文介绍了StackedBar不同系列之间的边界的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要的是在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆