【问题标题】:Combine BarChart and PointChart组合条形图和点图
【发布时间】:2018-04-20 09:04:44
【问题描述】:

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

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

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

我的代码:

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

但这仅在第一个系列之上显示第二个系列,但第一个系列不再可见。系列 2 的制造商没有显示,而是条形图显示(尽管我将边框宽度设置为 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 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);

                    }

代码将显示为:

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

【问题讨论】:

  • 顺便说一句。我试过“ chart_TimeChart.Series[series_FinishTime]["PixelPointWidth"] = "0"; 但显然这不起作用=/因为我不能将宽度设置为零。
  • 看看更新的答案。我一开始看错了你的问题,对不起!
  • 您的图表类型仍然是堆叠条形图。我不认为它实际上是你想要的。而且您仍然将 x 值添加为字符串。不过,你需要数字。您需要想出一个方案来使用简单的数字将点分组到各自的插槽中。

标签: c# charts bar-chart series


【解决方案1】:

!!请参阅下面的更新!


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

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

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

所以你需要想出一个合适的数字值用于 x 值..

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

这里是你如何使用它:

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

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

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

这是获得真正的StackedBar 图表的方法。您的解决方法可能有效,也可能无效。您可以尝试使颜色透明或等于图表的背景色;但这只不过是一个 hack,imo。


更新

我想我误读了这个问题。据我所知,您并不想创建堆叠图表。

相反,您会为这些问题苦苦挣扎:

  • 在同一 y 点显示条形
  • 使一些条不可见
  • 将垂直线显示为标记

让我们解决每个问题:

  • 一些列类型包括所有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);

结果如下:

几点说明:

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

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

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

  • 标记应改编自 Resize 并且可能改编自 AxisViewChanged 事件。把它放在一个很好的函数中调用(例如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 值沿垂直轴移动,反之亦然..)

【讨论】:

  • 您好,很抱歉回复延迟!我尝试了很多,我明白你的意思。我更新了我原来的帖子。我的“x 轴”上的字符串仍然存在问题,尽管我现在只添加应该堆叠到同一个系列的点并创建多个系列,每个项目一个。为什么会这样?我该如何解决这个问题?非常感谢您的努力!真的很有帮助!
  • 是的,并且制作所有系列 Bars,而不是 StackedBars,因此 DrawSideBside 可以让它们覆盖..
  • 使用 double 它工作正常,只是顺序翻转(点 0 放在点 1 后面),但这没问题。我想我明白了,现在明白了!谢谢!我会在几分钟后再次回答=)
  • 不确定你想要什么,但是有函数可以在 DateTime 和 Double 之间转换:DateTime dt = DateTime.Now; double dd = dt.ToOADate(); DateTime ddt = DateTime.FromOADate(dd); 但是 TimeSpan 只是一个相对的东西,不能转换为绝对的 datetetime。
  • 抱歉延迟回复。一切正常!我玩了一下你说的,你说得对!非常感谢!对此,我真的非常感激!祝你有美好的一天!
猜你喜欢
  • 1970-01-01
  • 2013-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多