I have one line series connected to the left vertical axis and the other connected to the right vertical axis. How can I align Ticks
and Grids
on both axes?
As you can see, the grid lines for vertical axes are on different positions. I thought that if I set the same number of ticks on both sides, it could align grids. I use this code:
private void AlignAxes()
{
if (tChart1.Axes.Left.CalcIncrement != 0)
{
var ticksNum = (tChart1.Axes.Left.Maximum - tChart1.Axes.Left.Minimum) /
tChart1.Axes.Left.CalcIncrement;
tChart1.Axes.Right.Increment =
(tChart1.Axes.Right.Maximum - tChart1.Axes.Right.Minimum) / ticksNum;
}
}
But it didn't work as expected:
I would like to have something like this:
But obviously with correctly calculated and aligned values.
Explanation:
I tried almost everything imaginable and found a solution (as good as I was able to get). The result is good enough at least for me.
Every calculation I tried was not 100% working. The Axis.Labels.Items
has no items if the labels are created automatically. Finally I found that there is private ArrayList
with name labelPos
and there are rectangles where labels should go. So I have to use System.Reflection
to get this list. From locations of this rectangles then calculates values on the other y axis and insert them as custom labels.
Code:
private bool _redrawAgain = false;
private void AlignAxes()
{
if (_redrawAgain)
{
_redrawAgain = false;
}
else
{
_redrawAgain = true;
var tmpl = new AxisLabels(tChart1.Axes.Left);
var privateField = tmpl.GetType().GetField("labelPos",
BindingFlags.NonPublic | BindingFlags.Instance);
var labelPos = (ArrayList)privateField.GetValue(tChart1.Axes.Left.Labels);
tChart1.Axes.Right.Labels.Items.Clear();
for (var i = 0; i < labelPos.Count; i++)
{
var rect = (Rectangle)labelPos[i];
tChart1.Axes.Right.Labels.Items.Add(
tChart1.Axes.Right.CalcPosPoint(rect.Y + (rect.Height / 2)));
}
tChart1.Axes.Right.Grid.DrawEvery = tChart1.Axes.Left.Grid.DrawEvery;
tChart1.Refresh();
}
}
I call AlignAxes();
from the TChart.AfterDraw
event so I have to refresh the chart once again, but I was not able to get this working without refreshing from any chart event I tried.
This code also works when the axis are switched at runtime and even if there is nothing on left axis, because it just clears custom labels from right axis and let the library to care about labels.
Result:
The only thing I was not able to solve is the MinimumOffset
and MaximumOffset
, but I think that it is broken in the TeeChart library. I set both offsets to 10 on both axes and they are shown properly when the chart is shown, but after zoom or scroll the label on the right axis disappears with the offset in mind and on the left axis disappears as if there is the offset is set to 0. You can see this strange behaviour on the image on both y axes near the x axis - there is no label on the right axis on the line where there is label on left axis.
Update:
I also want to have axis title so I had to update the code like this:
private void AlignAxes(Graphics3D g)
{
if (_redrawAgain)
{
_redrawAgain = false;
}
else
{
_redrawAgain = true;
var tmpl = new AxisLabels(tChart1.Axes.Left);
var privateField = tmpl.GetType().GetField("labelPos",
BindingFlags.NonPublic | BindingFlags.Instance);
var labels = (ArrayList)privateField.GetValue(tChart1.Axes.Left.Labels);
tChart1.Axes.Right.Labels.Items.Clear();
var gratestWidth = 0;
for (var i = 0; i < labels.Count; i++)
{
var rect = (Rectangle)labels[i];
tChart1.Axes.Right.Labels.Items.Add(
tChart1.Axes.Right.CalcPosPoint(rect.Y + (rect.Height / 2)));
var width = g.MeasureString(tChart1.Axes.Right.Labels.Font,
tChart1.Axes.Right.Labels.Items
[tChart1.Axes.Right.Labels.Items.Count - 1].
Value.ToString(
tChart1.Axes.Right.Labels.ValueFormat)).
ToSize().Width;
if (width > gratestWidth)
{
gratestWidth = width;
}
}
if (labels.Count > 0)
{
tChart1.Axes.Right.Labels.CustomSize = gratestWidth + 10;
}
else
{
tChart1.Axes.Right.Labels.CustomSize = 0;
}
tChart1.Axes.Right.Grid.DrawEvery = tChart1.Axes.Left.Grid.DrawEvery;
tChart1.Refresh();
}
}
I had to calculate the width of the labels by myself because otherwise there will be another chart refresh.