Соотношение сдерживать аспект в WindowsForms DataVisualization графике


Используя управление диаграммами из System.Windows.Forms.DataVisualization.Charting.Chart, я создаю точечную диаграмму.

Как я могу ограничить его так, чтобы масштаб оси X был таким же, как масштаб оси Y?

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

Я искал повсюду. в документации и в браузере свойств, но я не могу ничего найти или придумать какие-либо способы сделать это в событии resize.

1 4

1 ответ:

Это хороший вопрос, но, к сожалению, нет такого простого решения, как блокировка двух Axes или установка одного значения..

Давайте начнем с рассмотрения соответствующих игроков:
  • Контроль Chart имеет внутреннюю Size, называемую ClientSize, которая является Chart.Size минус границы. Оба размера измеряются в пикселях.

  • Внутри может быть один или несколько ChartAreas. У каждого есть Position, который имеет тип ElementPosition.

  • Внутри каждого ChartArea находится область, которая используется для фактического рисования точек; она называется InnerPlotPosition.

Свойство InnerPlotPosition определяет прямоугольник внутри диаграммы элемент area, используемый для построения данных; он исключает деления, метки осей и так далее.

Координаты, используемые для этого свойства (от 0,0 до 100,100), связаны с объект ChartArea, а не к вся карта целиком.

Свойство InnerPlotPosition можно использовать для выравнивания нескольких диаграмм области. Однако, если одна область диаграммы имеет деления и метки осей и другой - нет,их осевые линии не могут быть выровнены.

  • и ChartArea.Position, и ChartArea.InnerPlotPosition содержат не только местоположение , но также размеробластей; все значения находятся в процентах внешней области, т. е. ChartArea.InnerPlotPosition относительно ChartArea.Position и ChartArea.Position относительно the Chart.ClientSize. Все проценты идут от 0-100.
Таким образом, ChartAreaвключает в себя Labels и Legends, а также Axes и TickMarks.. Мы хотим найти способ сделать квадрат InnerPlotArea, то есть иметь одинаковую ширину и высоту в пикселях. Проценты не годятся! Давайте начнем с нескольких простых вычислений; если это те данные, которые у нас есть..:
    // we'll work with one ChartArea only..:
    ChartArea ca = chart1.ChartAreas[0];
    ElementPosition cap = ca.Position;
    ElementPosition ipp = ca.InnerPlotPosition;

.. тогда это размеры пикселей двух областей:

    // chartarea pixel size:
    Size CaSize = new Size( (int)( cap.Width * chart1.ClientSize.Width / 100f), 
                            (int)( cap.Height * chart1.ClientSize.Height / 100f));

    // InnerPlotArea pixel size:
   Size IppSize = new Size((int)(ipp.Width * CaSize.Width / 100f),
                            (int)(ipp.Height * CaSize.Height / 100f));

В идеале мы хотели бы, чтобы InnerPlotArea чтобы быть квадратным; так как мы не можем очень хорошо позволить меньшей стороне расти (иначе диаграмма будет перерисовываться), нам нужно уменьшить большую. Таким образом, новый размер пикселя InnerPlotArea равен

int ippNewSide = Math.Min(IppSize.Width, IppSize.Height);

Что дальше? Поскольку Chart.Size только что был установлен, мы не хотим с ним связываться. Также не стоит возиться с ChartArea: ему все еще нужно пространство для хранения Legend и т. д..

Таким образом, мы меняем размер InnerPlotArea..:

Сначала создайте переменную уровня класса для хранения исходных значений InnerPlotPosition:

   ElementPosition ipp0 = null;
Он понадобится нам, чтобы сохранить исходные проценты, то есть маржу, чтобы использовать их при расчете новых. Когда мы адаптируем график, то текущие уже будут изменены / искажены..

Затем мы создаем функцию, чтобы сделать квадрат InnerPlotArea, который оборачивает все это:

void makeSquare(Chart chart)
{
    ChartArea ca = chart.ChartAreas[0];

    // store the original value:
    if (ipp0 == null) ipp0 = ca.InnerPlotPosition;

    // get the current chart area :
    ElementPosition cap = ca.Position;

    // get both area sizes in pixels:
    Size CaSize = new Size( (int)( cap.Width * chart1.ClientSize.Width / 100f), 
                            (int)( cap.Height * chart1.ClientSize.Height / 100f));

    Size IppSize = new Size((int)(ipp0.Width * CaSize.Width / 100f),
                            (int)(ipp0.Height * CaSize.Height / 100f));

    // we need to use the smaller side:
    int ippNewSide = Math.Min(IppSize.Width, IppSize.Height);

    // calculate the scaling factors
    float px = ipp0.Width / IppSize.Width * ippNewSide;
    float py = ipp0.Height / IppSize.Height * ippNewSide;

    // use one or the other:
    if (IppSize.Width  < IppSize.Height)
        ca.InnerPlotPosition = new ElementPosition(ipp0.X, ipp0.Y, ipp0.Width, py);
    else 
        ca.InnerPlotPosition = new ElementPosition(ipp0.X, ipp0.Y, px, ipp0.Height);

}

Вы можете вызвать функцию после или во время изменения размера.

private void chart1_Resize(object sender, EventArgs e)
{
    makeSquare(chart1);
}

Здесь действует функция:

Оригинал размер: оригинал

Немного сжал: чешуйчатый

И снова стал квадратным.: площадь

Обратите внимание, как зеленый ChartArea резервирует достаточно места для Labels и Legend и как автоматическое масштабирование для осей все еще работает.. Но надписи по оси X теперь не помещаются в один ряд. Также обратите внимание, как ChartArea.BackColor на самом деле - это цвет InnerPlotArea только!

Обратите внимание, что вам, возможно, придется обновить переменную ipp0, чтобы отразить измененные проценты, после внесения изменений в макет ChartArea, таких как увеличение, перемещение или удаление Legends или изменение размера или угла Labels и т. д..

Конечно, вы можете изменить функцию, чтобы передать в любом другом соотношении, чтобы сохранить вместо сохранения площади участка квадрат..