【问题标题】:Winforms pie chart legend text length affects label and chartarea sizeWinforms 饼图图例文本长度影响标签和图表区域大小
【发布时间】:2020-05-29 13:04:27
【问题描述】:

我设置了以下 ChartArea 注释设置:

private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
        {
            if (e.ChartElement is ChartArea)
            {
                var ta = new TextAnnotation();
                ta.IsMultiline = true;
                ta.Text = "Results of Calculation\n%";
                ta.Width = e.Position.Width;
                ta.Height = e.Position.Height;
                ta.X = e.Position.X;
                ta.Y = e.Position.Y;
                ta.Font = new Font("Candara", e.Position.Height / 10, FontStyle.Regular);

                chart1.Annotations.Add(ta);
            }
        }

与此有关的一些问题,以及与我发布的其他问题有关的Legend

My other Pie Chart Legend/ChartArea question

有了这个PrePaint 设置,我不确定我的位置是否适合TextAnnotation。我正在使用e.Position,但它不是“完全”集中在饼图区域的甜甜圈中间。我希望它完美地居中。不知道在这里使用什么其他属性。

第二个问题是,当图例文本长度发生变化时,它会“推动”并使ChartArea 本身变小,因此饼图变小。我希望它是相反的,ChartArea 饼图 保持相同的大小,但 Legend 被推到一边。

这可能吗?

以下是饼图的位置设置:

谢谢

【问题讨论】:

    标签: winforms charts annotations pie-chart legend-properties


    【解决方案1】:

    很抱歉,上次我帮不上忙。我测试了 TextAnnotation 的居中,实际上当 InnerPlotPosition 设置为 auto 时它会出现问题。此外,在link 找到的答案会在每个 PrePaint 处创建一个新的 TextAnnotation 实例,导致 TextAnnotation 重叠和居中文本模糊。

    我找不到避免调整甜甜圈大小的方法(我不确定这是否可能,在这一点上......我会等待其他一些答案)但也许这可以解决解决方法。

    首先我创建了一个字典来存储居中的 TextAnnotation 引用(关键是图形名称,以防你有多个),然后在 PrePaint 事件中我得到图形中使用的 TextAnnotation 的正确引用并更新那个的坐标。

    其次,我手动设置了InnerPlotPosition,这似乎解决了TextAnnotation的居中问题。当然,您需要为 InnerPlot 指定坐标和大小,就像我对线条所做的那样:

    chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(0, 0, 60.65f, 94.99f);
    

    最后,我手动设置图例的位置和大小,并使用扩展方法 WrapAt 在图例项文本中的每个 _maxLegendTextBeforeWrap 设置一个“换行符”。没办法让它随着图例区域的宽度动态变化,只能手动设置。

    下面是最终效果的 GIF。不知道这是否适合您作为解决方案(太多的调整和代码,以我的口味),但无论如何。也许这可以引发一些关于如何解决的新想法。

    为此,我创建了这些全局变量:

            /// <summary>
            /// Saves the currently doughnut centered annotations per graph.
            /// </summary>
            private IDictionary<string, TextAnnotation> _annotationsByGraph;
            /// <summary>
            /// Number of characters 
            /// </summary>
            private int _maxLegendTextBeforeWrap = 10;
            /// <summary>
            /// Legend area width.
            /// </summary>
            private int _legendWidth = 20;
            /// <summary>
            /// Legend area height.
            /// </summary>
            private int _legendHeight = 90;
    

    这是 Load 事件的处理程序:

    private void ChartTest_Load(object sender, EventArgs e)
    {
        // ** Start of test data **
        chart1.Series["Series1"].Points.AddXY("A", 33);
        chart1.Series["Series1"].Points[0].LegendText = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        chart1.Series["Series1"].Points.AddXY("B", 33);
        chart1.Series["Series1"].Points[1].LegendText = "BBBBBBBBBBBBBBBBBBBB";
        chart1.Series["Series1"].Points.AddXY("C", 34);
        chart1.Series["Series1"].Points[2].LegendText = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
        // ** End of test data **
    
        // Creates a new instance of the dictionary storing the references to the annotations.
        _annotationsByGraph = new Dictionary<string, TextAnnotation>();
        // Createa a new instance of an annotation for the chart1 graph.
        _annotationsByGraph.Add(chart1.Name, new TextAnnotation());
        // Manually setting the position of the chart area prevents the imperfect positioning of the
        // TextAnnotation at the center of the doughnut.
        chart1.ChartAreas[0].Position.Auto = true;
        // Manually set the position of the InnerPlotPosition area prevents the imperfect positioning of the
        // TextAnnotation at the center of the doughnut.
        chart1.ChartAreas[0].InnerPlotPosition.Auto = false;
        chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(0, 0, 60.65f, 94.99f);
        // Minimum size for the legend font.
        chart1.Legends[0].AutoFitMinFontSize = 5;
        // Set the legend style as column.
        chart1.Legends[0].LegendStyle = LegendStyle.Column;
        // Splits the legend texts with the space char every _maxLegendTextBeforeWrap characters.
        chart1.Series["Series1"].Points.ToList().ForEach(p => p.LegendText = p.LegendText.WrapAt(_maxLegendTextBeforeWrap));
    }
    

    这是 PrePaint 事件的处理程序:

        private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
        {
            if (e.ChartElement is ChartArea)
            {
                // Get the reference to the corresponding text annotation for this chart.
                // We need this, otherwise we are creating and painting a new instance of a TextAnnotation
                // at every PrePaint, with the resulting blurrying effect caused by the overlapping of the text.
                var ta = _annotationsByGraph[e.Chart.Name];
                // Check if the annotation has already been added to the chart.
                if (!e.Chart.Annotations.Contains(ta))
                    e.Chart.Annotations.Add(ta);
                // Set the properties of the centered TextAnnotation.
                ta.IsMultiline = true;
                ta.Text = "Results of Calculation\nx%";
                ta.Font = new Font("Candara", e.Position.Height / 10, FontStyle.Regular);
                ta.Width = e.Position.Width;
                ta.Height = e.Position.Height;
                ta.X = e.Position.X;
                ta.Y = e.Position.Y;
                // Move the legend manually to the right of the doughnut.
                e.Chart.Legends[0].Position = new ElementPosition(e.Position.X + e.Position.Width, e.Position.Y, _legendWidth, _legendHeight);
            }
        }
    

    这就是按钮的作用:

            private void BtnChangeLegendItemLength_Click(object sender, EventArgs e)
            {
               if (chart1.Series["Series1"].Points[1].LegendText.StartsWith("DD"))
                  chart1.Series["Series1"].Points[1].LegendText = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".WrapAt(_maxLegendTextBeforeWrap);
               else
                  chart1.Series["Series1"].Points[1].LegendText = "DDDDDD".WrapAt(_maxLegendTextBeforeWrap);
            }
    

    这是扩展方法定义:

    internal static class ExtensionMethods
    {
        public static string WrapAt(this string legendText, int maxLengthBeforeWrap)
        {
            if (legendText.Length <= maxLengthBeforeWrap)
                return legendText;
            // Integer division to get how many times we have to insert a space.
            var times = legendText.Length / maxLengthBeforeWrap;
            // Counter of added spaces.
            var spacesAdded = 0;
            // Iterate for each space char needed.
            for (var i = 1; i <= times; i++)
            {
                // Insert a space char every maxLengthBeforeWrap positions.
                legendText = legendText.Insert(maxLengthBeforeWrap * i + spacesAdded, new string(' ', 1));
                spacesAdded++;
            }
            return legendText;
        }
    }
    

    【讨论】:

    • 感谢您非常详细的回复!我很感激。目前,由于这些问题,我想我会尝试使用 WPF 和 XAML 而不是 WinForms,它可能会证明是一个更好的解决方案。但我认为您的回答非常有帮助。
    猜你喜欢
    • 2020-08-24
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多