Set static space between custom axes - c#

I have to have static space (lets say 20 px) between axes.
If I have more axes on the left, it is possible to set their StartPosition and EndPosition in pixels or in percents.
For pixels setting - is there some way, how I can get the height of the space, where are the axes (red line in the picture)?
For percents setting - is there some way, how I can automatically convert 20 px to percents? I could calculate that by myself if I know the height, but I don't know how to get it - see 1.
I am able to get the height of the whole panel, where the chart is, but I don't know where to get the actual height of the space for left axes.

tChart1.Chart.ChartRect gives you the Rectangle of the "drawing zone". In your case, where you have to get that size before the axes calculations, you can use GetAxesChartRect event and use e.AxesChartRect as follows:
private void testChartRect()
{
for (int i = 0; i < 4; i++)
{
Line line = new Line(tChart1.Chart);
tChart1.Series.Add(line);
line.Chart = tChart1.Chart;
line.FillSampleValues();
Axis axis = new Axis();
tChart1.Axes.Custom.Add(axis);
line.CustomVertAxis = axis;
axis.AxisPen.Color = line.Color;
axis.Labels.Font.Color = line.Color;
}
tChart1.Aspect.View3D = false;
tChart1.Panel.MarginLeft = 10;
//tChart1.AfterDraw += TChart1_AfterDraw1;
tChart1.GetAxesChartRect += TChart1_GetAxesChartRect;
}
private void TChart1_GetAxesChartRect(object sender, GetAxesChartRectEventArgs e)
{
Rectangle chartRect = e.AxesChartRect;
int axisLength = (chartRect.Bottom - chartRect.Top) / tChart1.Axes.Custom.Count;
int margin = 20;
for (int i = 0; i < tChart1.Axes.Custom.Count; i++)
{
Axis axis = tChart1.Axes.Custom[i];
axis.StartEndPositionUnits = PositionUnits.Pixels;
axis.StartPosition = i * axisLength;
axis.EndPosition = (i + 1) * axisLength - (i != (tChart1.Axes.Custom.Count - 1) ? margin : 0);
}
}
private void TChart1_AfterDraw1(object sender, Graphics3D g)
{
tChart1.Graphics3D.Brush.Color = Color.Red;
tChart1.Graphics3D.Brush.Transparency = 80;
tChart1.Graphics3D.Rectangle(tChart1.Chart.ChartRect);
}

Related

Windows forms C# how to make the grid lines of a chart square?

I have a chart in windows forms and I want the gridlines to be squared. The gridline is anchored to bottom, top, left and right so it resizes with the screen. How do I make the grid lines always square and make the whole chart resize with the screen?
I have tried setting the width and height to be the same, but it doesn't work since the series names of the chart are on the right.
EDIT 1:
Here is the full uncensored code:
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = max;
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisX.Maximum = max;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "0";
chart1.ChartAreas[0].AxisY.LabelStyle.Format = "0";
chart1.ChartAreas[0].AxisX.Interval = 1;
chart1.ChartAreas[0].AxisY.Interval = 1;
chart1.ChartAreas[0].AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
chart1.ChartAreas[0].RecalculateAxesScale();
for (int i = 0; i < points.ToArray().Length; i++)
dt.Rows.Add(pointsArr[i, 0], pointsArr[i, 1]);
chart1.DataSource = dt;
chart1.Series["תחום הפתרונות האפשריים"].BorderWidth = 0;
float[] OptimalPoint = CalculateOptimalPt(convertEq(z), ListArToAr(points));
if (OptimalPoint[0] == 0)
{
for (int i = 0; i < 2; i++)
{
DataPoint dp = new DataPoint();
dp.SetValueXY(i, OptimalPoint[1]);
if (i > 0) dp.Color = Color.Transparent;
chart1.Series["פיתרון אופטימלי"].Points.Add(dp);
}
}
else
chart1.Series["פיתרון אופטימלי"].Points.AddXY(OptimalPoint[0], OptimalPoint[1]);
chart1.Series["פיתרון אופטימלי"].Points[0].MarkerSize = 10;
chart1.Series["תחום הפתרונות האפשריים"].XValueMember = "X_Value";
chart1.Series["תחום הפתרונות האפשריים"].YValueMembers = "Y_Value";
chart1.Series["תחום הפתרונות האפשריים"].ChartType = SeriesChartType.Area;
panel1.Visible = false;
panel2.Visible = true;
You can do it by anchoring the chart only to the Top and Left and calculating and setting the Width and Height yourself when Form size changes.
To do so we get fundamental data of the chart in the form constructor.
private readonly Size _innerMargin = new Size(183, 55); // Estimated
private readonly Size _outerMargin;
private readonly float _aspectRatio;
public Form1()
{
InitializeComponent();
_outerMargin = Size - chart1.Size;
Size innerSize = chart1.Size - _innerMargin;
_aspectRatio = (float)innerSize.Width / innerSize.Height;
}
_innerMargin is the estimated total difference between the chart size and the plot area with the gridlines. I actually got it from a screenshot and measured it in a graphics application.
_outerMargin is the difference of the form size and the chart control size.
This calculation of the initial _aspectRatio assumes the grid lines build perfect squares when the form opens. Instead, you could set this aspect ratio from the known number of squares in X and Y:
_aspectRatio = 16f / 16f; // From your example image.
In the Form_Resize event handler, we then set the new size of the chart. Depending on whether the current aspect ratio (calculated from the theoretical new maximum plot area size) is less than or greater than the original aspect ratio, the height or the width of the chart determines the maximum chart size. The other dimension must be calculated so that the aspect ratio of plot area remains the same.
private void Form1_Resize(object sender, EventArgs e)
{
Size maxChartSize = Size - _outerMargin;
Size innerSize = maxChartSize - _innerMargin;
double currentAspectRatio = (float)innerSize.Width / innerSize.Height;
if (currentAspectRatio < _aspectRatio) {
int chartWidth = Width - _outerMargin.Width;
chart1.Width = chartWidth;
chart1.Height = (int)((chartWidth - _innerMargin.Width) / _aspectRatio + _innerMargin.Height);
} else {
int chartHeight = Height - _outerMargin.Height;
chart1.Height = chartHeight;
chart1.Width = (int)((chartHeight - _innerMargin.Height) * _aspectRatio + _innerMargin.Width);
}
}

Setting fixed major grid marks independent of data range

This is my first project in c# and I'm trying to create plots from data.
I'm struggling with drawing minor and major grid lines and labels on a logarithmic scale.
I've set the scale to logarithmic, set the base to 10 and both major and minor intervals to 1, and it works great, however, the interval starts with the minimum value on scale, so for example if data starts at 30M (I'm dealing with frequencies) the next major tick is at 300M and 3G, which is not as it should be.
Is there a way to set major grid to 1, 10, 100 etc, independent of what data is displayed? i've tried changing intervals, base and offset but have not achieved much.
area.AxisX.IsLogarithmic = true;
area.AxisX.LogarithmBase = 10;
area.AxisX.Interval = 1;
//area.AxisX.IntervalOffset = 10000;
area.AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
area.AxisX.MajorGrid.Enabled = true;
area.AxisX.MajorTickMark.Enabled = true;
area.AxisX.MinorGrid.Enabled = true;
area.AxisX.MinorGrid.Interval = 1;
area.AxisX.MinorTickMark.Enabled = true;
area.AxisX.MinorTickMark.Interval = 1;
area.AxisX.Minimum = minMaxXY[0]; // in this example 30 M
area.AxisX.Maximum = minMaxXY[1]; // in this example 1 G
here's the link to the current grid
https://ibb.co/3WkxLfc
Thank you for your time and answers!
Thanks to TaW replay I managed to get my program working.
Here is my solution using customLabels location to draw the grid lines.
private void Chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
if (e.Chart.ChartAreas.Count > 0) // I don't yet truly understand when this event occurs,
// so I got plenty of null references.
{
Graphics g = e.ChartGraphics.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Color minorGridColor = Color.Gainsboro;
ChartArea area = e.Chart.ChartAreas[0];
double aymin = area.AxisY.Minimum;
double aymax = area.AxisY.Maximum;
int y0 = (int)area.AxisY.ValueToPixelPosition(aymin);
int y1 = (int)area.AxisY.ValueToPixelPosition(aymax);
foreach (var label in chart1.ChartAreas[0].AxisX.CustomLabels)
{
double xposition = area.AxisX.ValueToPixelPosition(Math.Pow(10, label.FromPosition + 0.1));
if (xposition > area.AxisX.ValueToPixelPosition(minMaxXY[0]) && xposition < area.AxisX.ValueToPixelPosition(minMaxXY[1]))
//this prevents drawing of lines outside of the chart area
{
int x = (int)xposition;
using (Pen dashed_pen = new Pen(Color.FromArgb(10, 0, 0, 0), 1))
{
dashed_pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawLine(dashed_pen, x, y0, x, y1);
}
}
}
}
}
I also found the CustomLabel.GridTicks Property, but for some reason it did not work.

WinForms: highlighting X and Y axes

I'm making a program to graph parabolas, and I want to make the X and Y axes (the ones from (0,0)) a different color. I haven't found any options to do so, and the only solution I've found is to make a large grid and set its increment to half the graph's size. Is there an alternative?
I used the default chart control. I'd expect something like:
You can set Crossing for axis to move the axis to center of chart. Also you can set LineWidth for axis to make it thicker. Also you can set ArrowStyle to have an arrow at the end of axis.
For example, to have a chart like this:
Use such code:
private void Form1_Load(object sender, EventArgs e)
{
//Set Chart Margins
this.chart1.ChartAreas[0].Position.Auto = false;
this.chart1.ChartAreas[0].Position.X = 10;
this.chart1.ChartAreas[0].Position.Y = 10;
this.chart1.ChartAreas[0].Position.Width = 80;
this.chart1.ChartAreas[0].Position.Height = 80;
//Configure X Axis
this.chart1.ChartAreas[0].AxisX.Crossing = 0;
this.chart1.ChartAreas[0].AxisX.Interval = 1;
this.chart1.ChartAreas[0].AxisX.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisX.LineWidth = 2;
this.chart1.ChartAreas[0].AxisX.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Configure Y Axis
this.chart1.ChartAreas[0].AxisY.Crossing = 0;
this.chart1.ChartAreas[0].AxisY.Interval = 5;
this.chart1.ChartAreas[0].AxisY.LineWidth = 2;
this.chart1.ChartAreas[0].AxisY.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisY.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Set Chart Type
this.chart1.Series[0].ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline;
//Set Data
var p = new List<PointF>();
for (int i = -5; i <= 5; i++)
{
p.Add(new PointF(i, i * Math.Abs(i)));
}
this.chart1.DataSource = p;
this.chart1.Series[0].XValueMember = "X";
this.chart1.Series[0].YValueMembers = "Y";
this.chart1.Series[0].IsVisibleInLegend = false;
}

how to position an object using variables

I'm trying to create 81 picture boxes and have them automatically positioned a certain distance apart from one another but they don't seem to be placing in any logical order. I have to initialize the X point to -1700 for them to even appear on the screen. The following code gets the first 15 where I want them but then they start stacking on top of one another instead of continuing the pattern. This is the result of about an hour of tinkering but initially the logic looked fine. I even had a message box that would display the current X,Y that was being set and it was correct it just would not place them at those coordinates.
int X = -1700;
int Y = 0;
for (int i = 0; i < 81; i++)
{
this.Controls.Add(championThumbNailsArray[i]);
championThumbNailsArray[i].Height = 80;
championThumbNailsArray[i].Width = 80;
championThumbNailsArray[i].Location = new Point(X, Y);
// MessageBox.Show(Convert.ToString(X) + "," + Convert.ToString(Y));
championThumbNailsArray[i].ImageLocation = akali.grabPicture();
//championThumbNailsArray[i].ImageLocation = championsArray[i].grabPicture();
if (X <= 425)
X = X + 85;
else
{
X = -1700;
Y = Y + 85;
}
}
Instead of manually placing elements use a FlowLayoutPanel. Add the controls to the panel and let it do the arrangement for you.
This code works as you are expecting
private void Form1_Load(object sender, EventArgs e)
{
int x = 0;
int y = 0;
for (int i = 0; i < 81; i++)
{
PictureBox p = new PictureBox();
p.BorderStyle = BorderStyle.Fixed3D;
p.Height = 80;
p.Width = 80;
p.Location = new Point(x, y);
x += 85;
if (x > 425)
{
x = 0;
y += 85;
}
this.Controls.Add(p);
}
}
But I would go with something like #Ed said, a FlowLayout control

Trying to make a zoom effect

I am trying to make a zoom effect on a picturebox with mouse wheel. Everything is OK except that when I use mouse middle button to zoom in or zoom out, it is ok, but it does not zoom in or zoom out the point which the mouse cursor is on. When I zoom in the point, I want it always slide. Please help me add a code snippet to make it work.
Here is my code:
int i = 5;
int index = 10;
private double[] zoomfactor = { .25, .33, .50, .66, .80, 1, 1.25, 1.5, 2.0, 2.5, 3.0 };
private void Zoom(int i)
{
double new_Zoom = zoomfactor[i];
imgBox.Width = Convert.ToInt32(imgBox.Image.Width * new_Zoom);
imgBox.Height = Convert.ToInt32(imgBox.Image.Height * new_Zoom);
}
private void On_wheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
i = i + e.Delta / 120;
if (i < 0)
{
i = 0;
}
else
{
if (i <= index)
i = i;
else
i = index;
}
Zoom(i);
}
You need to adjust the picture box location based on the mouse position relative to the form.
Here is a rough but working example of how you might do this:
var i = 5;
var zoomfactor = new[] {.25, .33, .50, .66, .80, 1, 1.25, 1.5, 2.0, 2.5, 3.0};
var origin = new Point(100, 100);
var image = Image.FromFile(#"c:\example.png");
var imgBox = new PictureBox {
Location = origin,
Size = image.Size,
Image = image,
SizeMode = PictureBoxSizeMode.StretchImage
};
var form = new Form {
Size = new Size(800, 600),
Controls = {imgBox}
};
form.MouseWheel += (sender, e) => {
i += e.Delta/120;
if (i < 0) {
i = 0;
}
if (i >= zoomfactor.Length) {
i = zoomfactor.Length - 1;
}
var newZoom = zoomfactor[i];
imgBox.Width = (int) (imgBox.Image.Width*newZoom);
imgBox.Height = (int) (imgBox.Image.Height*newZoom);
imgBox.Left = (int) (e.X - newZoom*(e.X - origin.X));
imgBox.Top = (int) (e.Y - newZoom*(e.Y - origin.Y));
};
form.ShowDialog();
You are not taking the mouse coordinates into account.
The MouseEventArgs class tells you where the mouse is (X, Y and Location properties), so you need to adjust accordingly.

Categories