结合 BarChart 和 PointChart [英] Combine BarChart and PointChart

查看:25
本文介绍了结合 BarChart 和 PointChart的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个小问题",我想创建一个如下所示的图表:

所以基本上系列 1 = 正常条形图.如果它在最大时间"之前结束(系列 2),则为绿色颜色系列 2 = 只是系列 1 项目顶部的数据点/标记.

我正在为此而苦苦挣扎...

我的代码:

 chart_TimeChart.Series.Clear();string series_timeneeded = "所需时间";chart_TimeChart.Series.Add(series_timeneeded);chart_TimeChart.Series[series_timeneeded]["PixelPointWidth"] = "5";chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Size = 10;chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Enabled = true;chart_TimeChart.Series[series_timeneeded].BorderWidth = 2;chart_TimeChart.Series[series_timeneeded].ChartType = SeriesChartType.StackedBar;chart_TimeChart.Series[series_timeneeded].YValueType = ChartValueType.Time;chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";chart_TimeChart.Series[series_timeneeded].XValueType = ChartValueType.String;for (int i = 0; i 

但这仅在第一个系列的顶部显示第二个系列,但第一个不再可见.系列 2 的 Maker 没有显示,而是显示了条形图(尽管我将 borderwidth 设置为 0).在我看来/认为我只需要让系列 2 的条"不可见,只显示系列 2 的标记点.

有什么想法吗?

<小时>

更新:

string seriesname = Name+ i.ToString();chart_TimeChart.Series.Add(seriesname);chart_TimeChart.Series[seriesname].SetCustomProperty("DrawSideBySide", "false");chart_TimeChart.Series[seriesname].SetCustomProperty("StackedGroupName", seriesname);chart_TimeChart.Series[seriesname].ChartType = SeriesChartType.StackedBar;//Y和X交换chart_TimeChart.Series[seriesname].YValueType = ChartValueType.Time;chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";chart_TimeChart.Series[seriesname].XValueType = ChartValueType.String;DateTime TimeNeeded = DateTime.Now.Add(List_AllLiniengroupsTimespans[k][i]);DateTime TimeMax = GetFinishTime(k, i);TimeSpan TimeDifference = TimeNeeded - TimeMax;if (TimeNeeded > TimeMax)//一切顺利{chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded);//到完成的时间chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue;chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference));//剩下的时间chart_TimeChart.Series[seriesname].Points[1].Color = Color.Red;chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);}else if (TimeMax > TimeNeeded)//不会按时完成{chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded);//时间到还可以chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue;chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference));//时间太多chart_TimeChart.Series[seriesname].Points[1].Color = Color.Green;chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);}else if (TimeMax == TimeNeeded)//正好适合{chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded);chart_TimeChart.Series[seriesname].Points[0].Color = Color.DarkOrange;chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);}

代码将显示为:

但我希望它看起来像这样:

解决方案

!!请参阅下面的更新!!

<小时>

如果您真的想创建 StackedBar 图表,您的图表有两个问题:

  • 如果您想堆叠数据点,它们需要具有有意义的 x 值;没有它们,它怎么知道彼此叠加什么?

您添加字符串,看起来不错,但根本不起作用.那是因为 DataPoint.XValue 字段是 double 并且当您将 string 添加到其中时,它被设置为 0!!您的字符串被复制到 Label 但否则会丢失.

因此,您需要想出一个合适的数字值用于 x 值..

  • 而且您还需要分组要堆叠的系列.为此,有一个名为 StackedGroupName 的特殊属性,用于对应堆叠的系列进行分组.

使用方法如下:

yourSeries1.SetCustomProperty("StackedGroupName", "Group1");

有关完整示例,请参阅

一些注意事项:

  • 我建议对您重复引用的所有图表元素使用变量,而不是一直使用索引引用.更少的代码,更容易阅读,更容易维护,而且性能可能更好.

  • 由于您的代码要求可见数据点为红色或绿色,Legend 将无法很好地表示.请参阅此处以获取示例绘制多色图例项..

  • 我使用了图表高度;这不是真正推荐的,因为可能有 TitlesLegends 甚至更多 ChartAreas;相反,您应该使用 ChartArea 的高度,或者更准确地说,使用 InnerPlotPosition 的高度.您需要将它们从百分比转换为像素.不太难,见下文或见 这里此处了解更多示例!

  • 标记应该改编自 Resize 并且可能来自 AxisViewChanged 事件.把它放在一个很好的函数中调用(例如 void setMarkerImage(Chart chart, Series s, string name, int width, Color c))总是一个好主意.

  • 如果你需要反复调整marker图片的大小,你可能需要编写更好的代码来清除旧的;这应该包括处理之前使用的 Bitmap..

这是一个例子:

var oldni = chart.Images.FindByName("marker");if (oldni != null){oldni.Image.Dispose();chart.Images.Remove(oldni);oldni.Dispose();}

  • 在某些情况下,需要轻推图表以更新其某些属性;RecalculateAxesScale 就是这样一种推动.

计算合适标记高度的示例:

ChartArea ca = chart.ChartAreas[0];ca.RecalculateAxesScale();浮动卡 = ca.Position.Height;浮动 iph = ca.InnerPlotPosition.Height;浮动 h = chart3.ClientSize.Height * cah/100f * iph/100f;int mh = (int)(h/s.Points.Count);

  • 最后一点:原始答案强调了使用有意义的 x 值的重要性.字符串没用!这对于堆叠条很重要;但现在同样重要,当我们希望酒吧位于相同的垂直位置时!将 x 值添加为字符串再次导致无意义..

(因为我们有 Bars,x 值沿着垂直轴,反之亦然..)

i got a Little "Problem", i want to create a Chart looking like this:

So basically Series 1 = Normal bar Chart. Color green if it Ends before the "time max" (series2) Series 2 = just a DataPoint / Marker on top of series 1 items.

I am struggling with this though...

my Code:

        chart_TimeChart.Series.Clear();
        string series_timeneeded = "Time Needed";
        chart_TimeChart.Series.Add(series_timeneeded);
        chart_TimeChart.Series[series_timeneeded]["PixelPointWidth"] = "5";
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Size = 10;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.ButtonStyle =    ScrollBarButtonStyles.SmallScroll;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Enabled = true;
        chart_TimeChart.Series[series_timeneeded].BorderWidth = 2;
        chart_TimeChart.Series[series_timeneeded].ChartType = SeriesChartType.StackedBar; 
        chart_TimeChart.Series[series_timeneeded].YValueType = ChartValueType.Time;
        chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
        chart_TimeChart.Series[series_timeneeded].XValueType = ChartValueType.String;

            for (int i = 0; i < MaxNumber; i++) 
                {
                    chart_TimeChart.Series[series_timeneeded].Points.AddXY("item"+ " " + (i + 1).ToString(), DateTime.Now.Add(Timespans[i]));
                }

        chart_TimeChart.Series.Add(series_FinishTime);
        chart_TimeChart.Series[series_FinishTime].ChartType = SeriesChartType.StackedBar;
        chart_TimeChart.Series[series_FinishTime].BorderWidth = 0;
        chart_TimeChart.Series[series_FinishTime].MarkerSize = 15;
        chart_TimeChart.Series[series_FinishTime].MarkerStyle = MarkerStyle.Square;
        chart_TimeChart.Series[series_FinishTime].MarkerColor = Color.Black;

        chart_TimeChart.Series[series_FinishTime].YValueType = ChartValueType.DateTime;
        chart_TimeChart.Series[series_FinishTime].XValueType = ChartValueType.String;

        for (int i = 0; i < MaxNumber; i++) 
                {
                    DateTime YPosition = GetFinishTime(i);

                    chart_TimeChart.Series[series_FinishTime].Points.AddXY("item"+ " " +(i+1).ToString(), YPosition);
                }

but this only Displays the 2nd series on top of the first one but the first one isnt visible anymore. The Maker of series 2 isnt shown but instead the bar is (eventhough i made borderwidth to 0). In my opinion/thinking i just have to make the "bar" of series 2 invisible and just Show the marker Points for series 2.

Any ideas?


Update:

string seriesname = Name+ i.ToString();
                    chart_TimeChart.Series.Add(seriesname);
                    chart_TimeChart.Series[seriesname].SetCustomProperty("DrawSideBySide", "false");
                    chart_TimeChart.Series[seriesname].SetCustomProperty("StackedGroupName", seriesname);

                    chart_TimeChart.Series[seriesname].ChartType = SeriesChartType.StackedBar; //Y and X are exchanged
                    chart_TimeChart.Series[seriesname].YValueType = ChartValueType.Time;
                    chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
                    chart_TimeChart.Series[seriesname].XValueType = ChartValueType.String;

                    DateTime TimeNeeded = DateTime.Now.Add(List_AllLiniengroupsTimespans[k][i]); 
                    DateTime TimeMax = GetFinishTime(k, i); 
                    TimeSpan TimeDifference = TimeNeeded - TimeMax;

                    if (TimeNeeded > TimeMax) //All good
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //Time till finish
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //time left
                        chart_TimeChart.Series[seriesname].Points[1].Color = Color.Red;
                        chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);

                    }
                    else if (TimeMax > TimeNeeded) //wont make it in time
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //time till still okay
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //Time that is too much
                        chart_TimeChart.Series[seriesname].Points[1].Color = Color.Green; 
                        chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);

                    }
                    else if (TimeMax == TimeNeeded) //fits exactly
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); 
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.DarkOrange; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                    }

the Code will be displayed as:

but i want it to look like this:

解决方案

!! See the update below !!


If you really want to create a StackedBar chart, your chart has two issues:

  • If you want to stack datapoints they need to have meaningful x-values; without them how can it know what to stack on each other?

You add strings, which look fine but simply don't work. That is because the DataPoint.XValue field is double and when you add string into it it is set to 0 !! Your string is copied to the Label but otherwise lost.

So you need to come up with a suitable numeric value you use for the x-values..

  • And you also need to group the series you want to stack. For this there is a special property called StackedGroupName which serves to group those series that shall be stacked.

Here is how you can use it:

yourSeries1.SetCustomProperty("StackedGroupName", "Group1");

For a full example see this post !

It also shows one way of setting the Labels with string values of your choice..

This is the way to go for real StackedBar charts. Your workaround may or may not work. You could try to make the colors transparent or equal to the chart's backcolor; but it won't be more than a hack, imo.


Update

I guess I have misread the question. From what I see you do not really want to create a stacked chart.

Instead you struggle with these issues:

  • displaying bars at the same y-spot
  • making some bars invisible
  • displaying a vertical line as a marker

Let's tackle each:

  • Some column types including all Bars, Columns and then some have a little known special property called DrawSideBySide.

By default is is set to Auto, which will work like True. This is usually fine as we don't want bars to sit upon each other, effectively hiding all or part of the overlaid points.

But here we do want them to share the same y-position, so we need to set the property to false for at least one Series; the others (on Auto) will follow..:

You can do it either like this:

aSeries["DrawSideBySide"] = "false";

or like this:

aSeries.SetCustomProperty("DrawSideBySide", "false");


  • Next we hide the overlaid Series; this is simple:

aSeries.Color = Color.Transparent;

  • The last issue is displaying a line marker. There is no such MarkerStyle, so we need to use a custom style. For this we need to create a suitable bitmap and add it as a NamedImage to the chart's Images collection.

This sounds more complicated than it is; however the MarkerImage will not be scaled, so we need to created suitable sizes whenever we resize the Chart or add/remove points. I will ignore this complication for now..

int pointCount = 10;
Bitmap bmp = new Bitmap(2, chart.ClientSize.Height / pointCount - 5);
using (Graphics g = Graphics.FromImage(bmp)) g.Clear(Color.Black);
NamedImage marker = new NamedImage("marker", bmp);
chart.Images.Clear();  // quick & dirty
chart.Images.Add(marker);

Here is the result:

A few notes:

  • I would recommend to use variables for all chart elements you refer to repeatedly instead of using indexed references all the time. Less code, easier to read, a lot easier to maintain, and probably better performance.

  • Since your code called for the visible datapoints to be either red or green the Legend will not show a good representation. See here for an example of drawing a multi-colored legend item..

  • I used the chart height; this is not really recommended as there may be Titles or Legends or even more ChartAreas; instead you should use the height of the ChartArea, or even more precise, the height of the InnerPlotPosition. You would need to convert those from percentages to pixels. Not too hard, see below or see here or here for more examples!

  • The markers should be adapted from the Resize and probably from the AxisViewChanged events. Putting it in a nice function to call (e.g. void setMarkerImage(Chart chart, Series s, string name, int width, Color c)) is always a good idea.

  • If you need to adapt the size of the marker image repeatedly, you may want to write better code for clearing the old one; this should include disposing of the Bitmap that was used before..

Here is an example:

var oldni = chart.Images.FindByName("marker");
if (oldni != null)
{
    oldni.Image.Dispose();
    chart.Images.Remove(oldni);
    oldni.Dispose();
}

  • In some situations one needs to nudge the Chart to update some of its properties; RecalculateAxesScale is one such nudge.

Example for calculating a suitable marker height:

ChartArea ca = chart.ChartAreas[0];
ca.RecalculateAxesScale();
float cah = ca.Position.Height;
float iph = ca.InnerPlotPosition.Height;
float h = chart3.ClientSize.Height * cah / 100f * iph / 100f;
int mh = (int)(h / s.Points.Count);

  • Final note: The original answer stressed the importance of using meaningful x-values. Strings are useless! This was important for stacking bars; but it is equally important now, when we want bars to sit at the same vertical positions! Adding the x-values as strings is again resulting in nonsense..

(Since we have Bars the x-values go along the vertical axis and vice versa..)

这篇关于结合 BarChart 和 PointChart的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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