结合BarChart和PointChart

时间:2018-04-20 09:04:44

标签: c# charts bar-chart series

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

enter image description here

所以基本上 系列1 =正常条形图。如果它在" time max"之前结束,则显示绿色。 (series2)Series 2 =系列1项目之上的DataPoint / Marker。

我正在努力解决这个问题......

我的代码:

MPI_Irecv

但这仅显示第一个系列的第二个系列,但第一个系列不再可见。系列2的制作者没有显示,但是栏是(尽管我将边界宽度设为0)。在我看来/我认为我只需要制作" bar" 2系列看不见,只显示系列2的标记点。

有什么想法吗?

更新

        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);
                }

代码将显示为:

enter image description here

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

enter image description here

1 个答案:

答案 0 :(得分:3)

<强> !!请参阅下面的更新!!

如果您真的想要创建StckedBar图表,那么您的图表会出现两个问题:

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

你添加字符串,看起来很好,但根本不起作用。这是因为DataPoint.XValue字段为double,当您将string添加到其中时,它会设置为0 !!您的字符串将复制到Label,但会丢失。

所以需要来提供适用于x值的数字值。

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

以下是如何使用它:

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

有关完整示例,请参阅this post

它还显示了一种使用您选择的字符串值设置标签的方法..

这是实际StackedBar图表的方法。您的解决方法可能有效,也可能无效。您可以尝试使颜色透明或等于图表的背景颜色;但它不会只是一个黑客,imo。

<强>更新

我想我误解了这个问题。从我看到你真的不想创建堆积图表。

相反,你要解决这些问题:

  • 在同一个y-spot显示条
  • 使一些酒吧不可见
  • 将垂直线显示为标记

让我们解决每个问题:

  • 某些列类型包括所有BarsColumns,然后有些列有一些名为DrawSideBySide特殊属性

默认设置为Auto,其效果与True相同。这通常很好,因为我们不希望条形图相互叠加,有效地隐藏了全部或部分重叠点。

但是我们确实希望他们共享相同的y位置,因此我们需要将属性设置为false至少一个Series;其他人(Auto)将跟随..:

你可以这样做:

aSeries["DrawSideBySide"] = "false";

或者像这样:

aSeries.SetCustomProperty("DrawSideBySide", "false");
  • 接下来,我们隐藏重叠的Series;这很简单:
aSeries.Color = Color.Transparent;
  • 最后一个问题是显示行标记。没有这样的MarkerStyle,所以我们需要使用自定义样式。为此,我们需要创建一个合适的位图,并将其作为NamedImage添加到图表的Images集合中。

听起来比这复杂得多;但是MarkerImage不会缩放,所以每当我们调整图表大小或添加/删除点时,我们都需要创建合适的大小。我暂时不理会这个并发症。

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);

结果如下:

enter image description here

一些注意事项:

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

  • 由于您的代码要求可见数据点为红色或绿色,Legend将无法显示良好的表示形式。有关绘制多色图例项的示例,请参阅here

  • 我使用了图表高度;建议不要这样做,因为可能有TitlesLegends或更多ChartAreas;相反,你应该使用ChartArea的高度,或更精确的InnerPlotPosition的高度。您需要将百分比转换为像素。不太难,请参阅下文或herehere了解更多示例!

  • 标记应根据ResizeAxisViewChanged事件进行调整。把它放在一个很好的函数中(例如void setMarkerImage(Chart chart, Series s, string name, int width, Color c))总是一个好主意。

  • 如果您需要重复调​​整标记图像的大小,您可能需要编写更好的代码来清除旧标记;这应该包括处理之前使用的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();
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);
  • 最后的注释:原始答案强调了使用有意义的x值的重要性。字符串没用!这对于堆叠条很重要;但是,当我们希望酒吧坐在相同的垂直位置时,同样重要!将x值作为字符串添加再次导致无意义..

(因为我们有Bars x值沿垂直轴移动,反之亦然。)