【问题标题】:C# WinForms Chart Control: get Size,Region,Position of BarC# WinForms 图表控件:获取条形图的大小、区域、位置
【发布时间】:2017-07-09 23:16:59
【问题描述】:

有没有办法获取stackcolumn图表栏的矩形?

这段代码 sn-p 是如何工作的,但它非常难看:

            var points = new List<Point>();
            for (int x = 0; x < chart.Size.Width; x++)
            {
                for (int y = 0; y < chart.Size.Height; y++)
                {
                    var hp = chart.HitTest(x, y, false, ChartElementType.DataPoint);
                    var result = hp.Where(h => h.Series?.Name == "Cats");
                    if (result.Count() > 0)
                    {
                        points.Add(new Point(x, y));
                    }
                }
            }
            var bottomright = points.First();
            var topleft = points.Last();

我将尝试描述我的目的: 我想从各种测试结果创建一个图表,并将其作为 HTML 文件提供。此生成的图表作为图像文件插入 HTML 文档中。现在,我想将图表中的条形区域的每个部分链接到外部文档。由于图形是静态的,我只能使用“MAP 区域”元素将任何区域作为 HTML 的链接。 “地图”元素需要一个“矩形”或这些坐标。这就是为什么我需要酒吧每个部分的协调员的原因。 不得不提一下,我对 Chart 控件还不是很熟悉。

图形是测试生成的。

[已解决] 我得到了解决方案:

            var stackedColumns = new List<Tuple<string,string,Rectangle>>();
            for (int p = 0; p < chart.Series.Select(sm => sm.Points.Count).Max(); p++)
            {                               
                var totalPoints = 0;
                foreach (var series in chart.Series)
                {
                    var width = int.Parse(series.GetCustomProperty("PixelPointWidth"));
                    var x = (int)area.AxisX.ValueToPixelPosition(p + 1) - (width / 2);
                    int y = (int)area.AxisY.ValueToPixelPosition(totalPoints);
                    totalPoints += series.Points.Count > p ? (int)series.Points[p].YValues[0] : 0;
                    int y_total = (int)area.AxisY.ValueToPixelPosition(totalPoints);
                    var rect = new Rectangle(x, y_total, width, Math.Abs(y - y_total));
                    stackedColumns.Add(new Tuple<string, string, Rectangle>(series.Name, series.Points.ElementAtOrDefault(p)?.AxisLabel, rect));                        
                }
            }

此解决方法适用于stackedcolumn,点从x 轴=0 开始。 只需手动设置 PixelPointWidth 属性以获得正确的宽度。我还没有找到获得默认条宽的方法..

【问题讨论】:

  • 这是一个简单的问题,但恐怕没有简单的答案。我已经测试了您的技巧并对其进行了优化,但是对于 最后一个系列的数据点的宽度,它总是 偏离 1-2 个像素。 - 你能告诉我们你需要矩形做什么吗?也许我的优化解决方案会做..

标签: c# winforms charts


【解决方案1】:

这非常棘手,我真的希望我知道如何从某些图表功能中获得界限!

您的代码 sn-p 实际上是解决方法的良好开端。我同意它有问题:

  • 很丑
  • 它并不总是有效
  • 性能很差

让我们一一解决这些问题:

  • 是的,它很丑陋,但这就是解决方法。我的解决方案更丑;-)
  • 我发现有两件事不起作用:

    • 您不能在Pre/PostPaint 事件期间调用HitTest,否则会发生可怕的事情,例如某些Series 丢失、SO 异常或其他崩溃..
    • 最后一个Series宽度的结果关闭 1-2个像素。
  • 即使是小图表,测试图表中每个像素的性能也会很糟糕,但是当你放大图表时会变得越来越差。不过,这相对容易预防..:

我们正在搜索的是每个DataPoint 的每个Series 的边界矩形。

矩形由左右或宽度加上顶部和底部或高度定义。

我们可以通过使用轴函数ValueToPixelPosition 输入每个点的y 值和0 来获得topbottom 的精确值。这既简单又便宜。

除此之外,我们仍然需要找到点的leftright边缘。为此,我们需要沿着零线进行测试。 (所有点都将从那里开始或结束!)

这大大减少了测试次数。

我决定分别对每个系列进行测试,每次都重新盯着0。为了获得更好的性能,您可以一次完成所有操作。

这是一个函数,它为给定的Series 返回一个List&lt;Rectangle&gt;

List<Rectangle> GetColumnSeriesRectangles(Series s, Chart chart, ChartArea ca)
{
    ca.RecalculateAxesScale();

    List<Rectangle> rex = new List<Rectangle>();
    int loff = s == chart.Series.Last() ? 2 : 0; ;
    int y0 = (int)ca.AxisY.ValueToPixelPosition(0);
    int left = -1;
    int right = -1;
    foreach (var dp in s.Points)
    {
        left = -1;
        int delta = 0;
        int off = dp.YValues[0] > 0 ? delta : -delta;
        for (int x = 0; x < chart.Width; x++)
        {
            var hitt = chart.HitTest(x, y0 +off );

            if (hitt.ChartElementType == ChartElementType.DataPoint &&
                ((DataPoint)hitt.Object) == dp)
            {
                if (left < 0) left = x;
                right = x;
            }
            else if (left > 0 && right > left) break;
        }
        int y =  (int)ca.AxisY.ValueToPixelPosition(dp.YValues[0]);
        rex.Add(new Rectangle(left, Math.Min(y0, y), 
                              right - left + 1 - loff, Math.Abs(y - y0)));
        left = -1;
    }
    return rex;
}

一些注意事项:

  • 我首先执行RecalculateAxesScale,因为在计算当前布局之前我们无法进行 Hittest。

  • 我使用辅助变量loff 来保存最后一个Series 中宽度的偏移量。

  • 我从最后一个 x 坐标开始搜索,因为这些点都应该按顺序排列。如果不是因为您使用了有趣的 x 值或插入的点,您可能需要从 0 开始。

  • 我使用y0 作为命中测试 y 和点基数的零值的基线。

  • 我使用一点数学来确定正负 y 值的界限。

这是一个结构,用于为所有 Series 保存这些矩形以及收集它们的代码:

Dictionary<string, List<Rectangle>> ChartColumnRectangles = null;

Dictionary<string, List<Rectangle>> GetChartColumnRectangles(Chart chart, ChartArea ca)
{
    Dictionary<string, List<Rectangle>> allrex = new Dictionary<string, List<Rectangle>>();
    foreach (var s in chart.Series)
    {
        allrex.Add(s.Name, GetColumnSeriesRectangles(s, chart, ca));
    }
    return allrex;
}

每当我们添加点或调整图表大小时,我们都需要重新计算矩形;每当轴视图发生变化时。 AxisViewChangedClientSizeChangedResize 的通用代码以及您添加或删除点的任何位置都可能如下所示:

Chart chart= sender as Chart;
GetChartColumnRectangles(chart, chart.ChartAreas[0]);

让我们用 Paint 事件测试结果:

private void chart1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    chart1.ApplyPaletteColors();
    foreach (var kv in ChartColumnRectangles)
    {
        {
            foreach (var r in kv.Value)
                g.DrawRectangle(Pens.Black, r);
        }
    }
}

它在行动:

【讨论】:

    【解决方案2】:

    好吧,我一直在走这条路,对我来说最大的问题是“PixelPointWidth”的自定义属性就是这样 - 它是自定义的。除非您设置它,否则您无法检索它。我需要项目的宽度 - 必须自己进行调整/计算。请记住,许多图表都可以平移/缩放,因此一旦沿着这条路径走下去,您就需要重新计算它并将其设置为图表预绘制事件。

    这是我制作的一个粗略的小函数(比需要的更冗长 - 用于教育目的并且没有错误处理:)):

            private int CalculateChartPixelPointWidth(Chart chart, ChartArea chartArea, Series series)
        {
            // Get right side - takes some goofy stuff - as the pixel location isn't available
            var areaRightX = Math.Round(GetChartAreaRightPositionX(chart, chartArea));
            var xValue = series.Points[0].XValue;
            var xPixelValue = chartArea.AxisX.ValueToPixelPosition(xValue);
    
            var seriesLeftX = chart.Location.X + xPixelValue;
            var viewPointWidth = Math.Round((areaRightX - seriesLeftX - (series.Points.Count * 2)) / series.Points.Count, 0);
    
            return Convert.ToInt32(viewPointWidth);
        }
    

    还有这个:

            private double GetChartAreaRightPositionX(Chart chart, ChartArea area)
        {
            var xLoc = chart.Location.X;
            return xLoc + (area.Position.Width + area.Position.X) / 100 * chart.Size.Width;
        }
    

    我计算这个的原因是因为我需要在普通图表项目对象之上绘制一些图形叠加层(我自己的渲染用于我自己的目的)。

    在图表的“prepaint”事件中,我需要计算与当前图表视图匹配的“PixelPointWidth”(可能被平移/缩放)。然后我使用该值将图表自定义属性设置为匹配。 . .这样正常的图表实体和 MINE 正确对齐/缩放(确保我们在正确的“x”轴位置):

    在我的 prepaint 事件中,我执行以下操作 - 就在绘制我的图形实体之前:

                    // Pretty close scwag . . .
                    var viewPointWidth = CalculateChartPixelPointWidth(e.Chart, e.Chart.ChartAreas[0], e.Chart.Series[0]);
    
                    // Set the custom property and use the same point width for my own entities .  .
                    chart1.Series[0].SetCustomProperty("PixelPointWidth", viewPointWidth.ToString("D"));
                    // . . .  now draw my entities below . . .
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-17
      • 1970-01-01
      • 2018-11-09
      • 2014-03-13
      • 2017-04-21
      • 2016-10-02
      • 2011-09-02
      • 1970-01-01
      相关资源
      最近更新 更多