Соотношение сдерживать аспект в WindowsForms DataVisualization графике
Используя управление диаграммами из System.Windows.Forms.DataVisualization.Charting.Chart
, я создаю точечную диаграмму.
Как я могу ограничить его так, чтобы масштаб оси X был таким же, как масштаб оси Y?
Просто установить сам элемент управления квадратным недостаточно, потому что он имеет внутренние поля для рисования и маркировки осей, которые не равны. Я мог бы выбрать определенный размер и настроить его, чтобы он был квадратным, но он должен быть как квадратным, так и изменяемым.Я искал повсюду. в документации и в браузере свойств, но я не могу ничего найти или придумать какие-либо способы сделать это в событии resize.
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
относительно theChart.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
и т. д..Конечно, вы можете изменить функцию, чтобы передать в любом другом соотношении, чтобы сохранить вместо сохранения площади участка квадрат..