c# - 跨多个折线图的垂直线,并在 Winforms 中显示每个图表的值

标签 c# .net winforms charts data-visualization

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

这是我想要实现的目标的示例:

enter image description here

最佳答案

我建议将您的数据放入一个 MSChart 控件中,并带有四个独立的ChartAreas

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

然后添加一个VerticalLineAnnotation并使其可移动

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

这是一个例子:

enter image description here

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 时使用了一些“魔法”数字。它位于图表的百分比中,我在顶部和底部以及右侧的图例留了一点余量。

关于c# - 跨多个折线图的垂直线,并在 Winforms 中显示每个图表的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49872756/

相关文章:

c# - 如何在单独的文件中从 web.config 中提取一个部分

c# - R.NET 找不到函数 "cor"

c# - .NET 配置 (app.config/web.config/settings.settings)

c# - 如何转换为时间?

jquery - 使用 JQuery 在带有 Repeater 控件的 ASP .NET 页面中进行展开/折叠

C# 主线程被第二个线程使用信号阻塞?

c# - 将存储过程中的 Select Query 的结果返回到列表

c# 如何在不使用本地代理服务器的情况下跟踪来自客户端的所有 Web 事件和 Web URL?

c# - Windows 窗体 - 如何保存和加载游戏?

.net - 在 VB.NET Windows 窗体中显示桌面按钮