Winforms p ie длина текста легенды диаграммы влияет на размер метки и области диаграммы - PullRequest
1 голос
/ 29 мая 2020

У меня установлены следующие настройки аннотации 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 в отношении моего другого опубликованного вопроса:

Другой мой вопрос P ie Chart Legend / ChartArea

С этой настройкой PrePaint я не уверен, что моя позиция верна для TextAnnotation. Я использую e.Position, но он выходит не «точно» по центру бублика области диаграммы p ie. Я бы хотел, чтобы он был идеально отцентрован. Не уверен, какое еще свойство здесь использовать.

Вторая проблема заключается в том, что при изменении длины текста легенды он «подталкивает» и делает сам ChartArea меньше, поэтому диаграмма p ie становится меньше . Я бы хотел, чтобы все было наоборот, где ChartArea p ie диаграмма остается того же размера, а Legend отодвигается.

Возможно ли это ?

Ниже приведена настройка положения диаграммы p ie: ChartArea settings

Спасибо

1 Ответ

1 голос
/ 05 июня 2020

Мне жаль, что я больше ничем не помог, в прошлый раз. Я протестировал центрирование TextAnnotation, и на самом деле у него есть проблемы, когда для InnerPlotPosition установлено значение auto. Более того, ответ, найденный по адресу ссылка , создает новый экземпляр TextAnnotation в каждом PrePaint, вызывая перекрытие TextAnnotations и размытие центрированного текста.

Я не мог найти способ чтобы избежать изменения размера пончика (я даже не уверен, что это возможно, на данный момент ... Я подожду других ответов), но, возможно, это может сработать как обходной путь.

Сначала я создал словарь для хранения центрированных ссылок TextAnnotations (ключ - это имя графика, если у вас их несколько), затем в событии PrePaint я получаю правильную ссылку на TextAnnotation, используемую в графике, и обновляю координаты этого .

Во-вторых, я установил InnerPlotPosition вручную, это, кажется, решает проблему центрирования TextAnnotation. Конечно, вам нужно указать координаты и размер для InnerPlot, как я сделал со строкой:

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

Наконец, я вручную установил положение и размер легенды и с помощью метода расширения WrapAt I установите «разрыв строки» каждый _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;
    }
}

enter image description here

...