【问题标题】:Vertical line across multiple line charts with value display for each chart in Winforms跨多个折线图的垂直线,在 Winforms 中显示每个图表的值
【发布时间】:2018-04-17 07:59:57
【问题描述】:

我正在尝试创建一个应用程序,该应用程序将在一个表单上包含四个折线图。当用户将鼠标拖到这些图表上时,每个图表应该有一条垂直线,并且每个图表都会显示当前值。有什么方法可以在 C#/.NET 和 WinForms 中完成?

这是我想要实现的一个示例:

【问题讨论】:

  • 你看过 devexpress 吗? devexpress.com
  • 我有 Infragistics 包,但即使有了它,我也不知道该怎么做。 DevExpress 包应该可以吗?
  • 您当然可以使用 MSChart 做到这一点。您是否在同一个图表控件中添加了四个图表或(推荐)使用 4 个图表区域?这将有助于将鼠标事件保持在一个地方..

标签: c# .net winforms charts data-visualization


【解决方案1】:

我建议将您的数据放入具有四个独立 ChartAreas一个 MSChart 控件。

为此,您需要设置它们的位置,因为默认布局是 2x2。

然后添加VerticalLineAnnotation 并使其可移动

在其移动事件中,您触发图表的Paint 事件,您可以在其中计算必要的数据,即要显示的值和显示它们的位置。

这是一个例子:

Paint 事件编码如下:

private void chart_Paint(object sender, PaintEventArgs e)
{
    double xv = VL.X;   // get x-value of annotation
    for (int i = 0; i < chart.ChartAreas.Count; i++)
    {
        ChartArea ca = chart.ChartAreas[i];
        Series s = chart.Series[i];
        int px = (int )ca.AxisX.ValueToPixelPosition(xv);
        var dp = s.Points.Where(x => x.XValue >= xv).FirstOrDefault();
        if (dp != null)
        {
            int py = (int )ca.AxisY.ValueToPixelPosition(s.Points[0].YValues[0]) - 20;
            e.Graphics.DrawString(dp.YValues[0].ToString("0.00"), 
                                  Font, Brushes.Black, px, py);
        }
    }
}

注意使用两个轴函数在图表中的两个(三个)坐标系之间进行转换:我们从数据值开始,然后转到像素。第三个系统是百分比,我们将在下面设置图表区域时遇到它..

还要注意,为了简单起见,我假设每个 ChartArea 有一个系列;所以我可以使用相同的索引。您还可以通过搜索具有正确 ChartArea.Name 字段 (*) 的系列来找到相应的系列。

随意设置不同的 y 位置,当然还有字体、格式等。

为了实现它,我们编写了以下两个事件:

private void chart_AnnotationPositionChanging(object sender, 
                                              AnnotationPositionChangingEventArgs e)
{
    chart.Invalidate();
}

private void chart_AnnotationPositionChanged(object sender, EventArgs e)
{
    chart.Invalidate();
}

包括测试数据创建的图表设置有点长..:

首先我们为注解声明一个类级变量。当然,我们也可以从chart.Annotations 集合中获取它..:

VerticalLineAnnotation VL = null;

private void setupbutton_Click(object sender, EventArgs e)
{
    chart.ChartAreas.Clear();
    chart.Series.Clear();
    for (int i = 0; i < 4; i++)
    {
        ChartArea ca = chart.ChartAreas.Add("CA" + (i+1));
        ca.Position = new ElementPosition(0, i*23 + 5, 90, 25);
        Series s = chart.Series.Add("S" + (i+1));
        s.ChartType = SeriesChartType.Line;
        s.MarkerStyle = MarkerStyle.Circle;  // make the points stand out
        s.MarkerSize = 3;
        s.ChartArea = ca.Name;  // where each series belongs (*)

        for (int j = 0; j < 50; j++)  // a few test data
        {
            s.Points.AddXY(j, Math.Sin((( (j + 1) *(i + 1) ) / 55f) * 10f));
        }
    }

    VL = new VerticalLineAnnotation();  // the annotation
    VL.AllowMoving = true;              // make it interactive

    VL.AnchorDataPoint = chart.Series[0].Points[0];  // start at the 1st point
    VL.LineColor = Color.Red;
    VL.IsInfinitive = true;             // let it go all over the chart

    chart.Annotations.Add(VL);
}

如果你仔细观察动画,你会看到数值跳跃;那是因为我只有50分。如果你想显示插值,你可以通过找到另一个相邻点并做一些简单的数学运算来做到这一点。但在很多情况下,这是无稽之谈。

请注意,我在设置ChartArea.Position 时使用了一些“神奇”数字。它在图表的 百分比 中,我在顶部和底部以及右侧留下了一点松弛 Legend..

【讨论】:

  • 这看起来是一个很好的解决方案。等不及要测试它了。感谢所有代码和示例。
  • 刚刚开始工作。病了一个星期。会让你知道。谢谢!
  • 让它工作。现在我必须在此基础上构建我的应用程序的其余部分。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多