How to do aligned scrolling through two chart areas without using AlignToChartArea? - c#

I have two ChartArea objects in a Chart (System.Windows.Forms.DataVisualization.Charting is what I'm using).
One is a Point graph, and the other is a RangeBar graph. The horizontal axis on the RangeBar graph is actually the Y axis, so I cannot just use something like this:
Chart1.ChartAreas["Chart Area 2"].AlignWithChartArea = "Default";
I've figured out how to zoom both charts and keep them aligned, but when I try to scroll both charts by clicking on the scrollbar on one of the horizontal axes, I can't quite get it to line up. They almost line up, but they're off by maybe a second or so (the horizontal axis in both graphs is time).
Here's what I have:
private void theChart_AxisViewChanged(object sender, ViewEventArgs e)
{
if (e.ChartArea == theChart.ChartAreas["MyPointChartArea"])
{
theChart.ChartAreas["MyRangeBarChartArea"].AxisY.ScaleView.Position = e.NewPosition;
theChart.ChartAreas["MyRangeBarChartArea"].AxisY.ScaleView.Size = e.NewSize;
theChart.ChartAreas["MyRangeBarChartArea"].AxisY.ScaleView.SizeType = e.NewSizeType;
}
if (e.ChartArea == theChart.ChartAreas["MyRangeBarChartArea"])
{
theChart.ChartAreas["MyPointChartArea"].AxisX.ScaleView.Position = e.NewPosition;
theChart.ChartAreas["MyPointChartArea"].AxisX.ScaleView.Size = e.NewSize;
theChart.ChartAreas["MyPointChartArea"].AxisX.ScaleView.SizeType = e.NewSizeType;
}
}
What else do I need to do to get the charts to line up? The physical extent of the charts is the same. It's just the data that are slightly misaligned.
Thanks for any help.

Related

Overlapping panels with "Dock" resize functionality

I've been struggling for a couple of days with this issue, which seems like it would have an obvious solution.
How could I place a panel, which maintains position and resizes as if Docked, in the middle of the form, over other panels populating a TableLayoutPanel without messing Column/Row spans and misplacing other containers and controls?
I would like to avoid developing custom functionality and make due with the basic visual studio toolbox (extensions are welcome).
In an effort to better explain what I'm hoping to achieve, I've provided a link to an image of the goal.
Blue is the dynamically resizable, centered pop-up panel.
Green is an Image/BackgroundImage starting on cell [1,1] of the TableLayoutPanel.
Orange is a drop-down panel for a side menu, also starting on cell [1,1].
So... I found a solution.
I guess I needed to admit that I have to write the resizing function myself.
I'm new here, so I don't know if answering oneself question is canon, but here goes.
For the Orange panel, given it's Anchored Top,Left:
private double panel1WidthRatio, panel1HeightRatio; // Global variables for maintaining ratio.
private void ParentForm_ResizeBegin(object sender, EventArgs e)
{
double p1w = this.side_panel.Size.Width; // Grab the panels' dimensions
double p1h = this.side_panel.Size.Height; // as soon as the user begins to resize
double fw = this.Size.Width; // in order to store
double fh = this.Size.Height; // the Panel to Form
panel1WidthRatio = p1w / fw; // dimension ratios.
panel1HeightRatio = p1h / fh;
}
private void ParentForm_SizeChanged(object sender, EventArgs e)
{
double formWidth = this.Size.Width; // As soon as a new size is set
double formHeight = this.Size.Height; // resize the panel using the earlier ratio.
this.side_panel.Size = new Size((int)(formWidth * panel1WidthRatio), (int)(formHeight * panel1HeightRatio));
}
For the Blue panel, given no Anchors, simply replace:
this.side_panel.Size = new Size((int)(formWidth * panel1WidthRatio), (int)(formHeight * panel1HeightRatio));
with:
this.middle_panel.Size = new Size((int)(formWidth * panel2WidthRatio), (int)(formHeight * panel2HeightRatio));
this.middle_panel.Location = new System.Drawing.Point(this.Size.Width/4-9, this.Size.Height/4-24);
The constants (9,24) take into account the Form's frame and borders, so as to correctly reposition the panel in the center.

Use of GetValueAtPosition with WPF ToolKit Chart

I'm trying to get the X-coordinate position of the mouse on a Chart. Looking at some other code on this site, I have
private void Chart_MouseMove(object sender, MouseEventArgs e)
{
var chart = (Chart)sender;
var xAxisRange = (IRangeAxis)chart.Axes[0];
var mousePositionInPixels = e.GetPosition(chart);
double mouseXPositionInChartUnits = (double)xAxisRange.GetValueAtPosition(new UnitValue(mousePositionInPixels.X, Unit.Pixels));
TextBoxR0.Text = mouseXPositionInChartUnits.ToString(); //temp display textBox for debugging
RaisePropertyChanged();
}
The problem is that the values are shifted to the left by a constant. The X-value for the origin is 0, but GetValueAtPosition is returning, say 15, and all x values returned are 15 greater than they should be. If I expand the chart horizontally, the offset changes, becoming smaller. If I contract the chart horizontally, the offset becomes larger. I've done some extensive searching for an answer, and have looked for some member of Chart that may give me a left margin offset, but with no success.
I know this answer is late, but I had the same problem. My solution was to get the mouse position relative to the series instead of the chart. Its not perfect (it’s off by a pixel or 2 at a resolution of 1366 x 720), but when rounded to 2 places it’s good enough for me.
In my xaml I gave the series a name (“Series”, as unoriginal as that is). I also have a TextBlock that's bound to the string Pos, so I can see the position of the cursor. I've bound the ItemsSource of the series to List<KeyValuePair<Double, Double>> Points
Lastly, don’t forget that the y axis is flipped relative to screen coordinates (the y axis increases as you move up, but screen coordinates increase as you move down), so that’s why there's a 1.0 – yAxisRange in there.
Xaml:
<chartingToolkit:Chart Name="columnChart" MouseMove="Chart_MouseMove">
<chartingToolkit:LineSeries Name="Series" ItemsSource="{Binding Points}" DependentValuePath="Value" IndependentValuePath="Key" />
</chartingToolkit:Chart>
Code:
private void Chart_MouseMove(object sender, MouseEventArgs e)
{
var chart = (Chart)sender;
var mousePositionInPixels = e.GetPosition(Series);
var xAxisRange = (IRangeAxis)chart.ActualAxes[0];
double mouseXPositionInChartUnits = (double)xAxisRange.GetValueAtPosition(new UnitValue(mousePositionInPixels.X, Unit.Pixels));
var yAxisRange = (IRangeAxis)chart.ActualAxes[1];
double mouseYPositionInChartUnits = 1.0 - (double)yAxisRange.GetValueAtPosition(new UnitValue(mousePositionInPixels.Y, Unit.Pixels));
Pos = $"({Math.Round(mouseXPositionInChartUnits, 2)}, {Math.Round(mouseYPositionInChartUnits, 2)})";
}

WPF Path's Geometry Transform performance gets worse with LineGeometries' length increase

I'm currently trying to create a little plot interactive editor, using WPF.
On maximized window the plot dragging with mouse is not responsive enough because of the plot grid.
I got a path for my plot grid lying inside a Canvas control (render transform just shifts it to the bottom of the canvas)
<Path Name="VisualGrid" RenderTransform="{StaticResource PlotTechnicalAdjust}" Style="{DynamicResource ResourceKey=GridStyle}" Panel.ZIndex="1"/>
Here is how grid is created; _curState has actual camera "viewport" metadata
if (_curState.Changes.ScaleStepXChanged)
{
foreach (TextBlock item in _xLabels)
{
DeleteLabel(item);
}
_xLabels.Clear();
double i = _curState.LeftEdgeLine;
_gridGeom.Children[(int)GridGeomIndexes.VerticalLines] = new GeometryGroup { Transform = _verticalLinesShift};
var verticalLines =(GeometryGroup)_gridGeom.Children[(int)GridGeomIndexes.VerticalLines];
while (i <= _curState.RightEdgeLine * (1.001))
{
verticalLines.Children.Add(new LineGeometry(new Point(i * _plotParameters.PixelsPerOneX, 0),
new Point(i * _plotParameters.PixelsPerOneX,
-_wnd.ContainerGeneral.Height)));
_xLabels.Add(CreateLabel(i, Axis.X));
i += _curState.CurrentScaleStepX;
}
_curState.Changes.ScaleStepXChanged = false;
}
if (_curState.Changes.ScaleStepYChanged)
{
foreach (TextBlock item in _yLabels)
{
DeleteLabel(item);
}
_yLabels.Clear();
double i = _curState.BottomEdgeLine;
_gridGeom.Children[(int)GridGeomIndexes.HorizontalLines] = new GeometryGroup { Transform = _horizontalLinesShift};
var horizontalLines = (GeometryGroup)_gridGeom.Children[(int)GridGeomIndexes.HorizontalLines];
while (i <= _curState.TopEdgeLine * (1.001))
{
horizontalLines.Children.Add(new LineGeometry(new Point(0, -i * _plotParameters.PixelsPerOneY),
new Point(_wnd.ContainerGeneral.Width,
-i * _plotParameters.PixelsPerOneY)));
_yLabels.Add(CreateLabel(i, Axis.Y));
i += _curState.CurrentScaleStepY;
}
_curState.Changes.ScaleStepYChanged = false;
}
Where Transforms are composition of TranslateTransform and ScaleTransform (for vertical lines I only use X components and only Y for horizontal lines).
After beeing created those GeometryGroups are only edited if a new line apears into camera or an existing line exits viewable space. Grid is only recreated when axis graduations have to be changed after zooming.
I have a dragging option implemented like this:
private Point _cursorOldPos = new Point();
private void OnDragPlotMouseMove(object sender, MouseEventArgs e)
{
if (e.Handled)
return;
Point cursorNewPos = e.GetPosition(ContainerGeneral);
_plotView.TranslateShiftX.X += cursorNewPos.X - _cursorOldPos.X;
_plotView.TranslateShiftY.Y += cursorNewPos.Y - _cursorOldPos.Y;
_cursorOldPos = cursorNewPos;
e.Handled = true;
}
This works perfectly smooth with a small window (1200x400 units) for a large amount of points (like 100+).
But for a large window (fullscreen 1920x1080) it happens pretty jittery even without any data-point controls on canvas.
The strange moment is that lags don't appear when I order my GridGenerator to keep around 100+ lines for small window and drag performance suffers when I got less than 50 lines on maximezed. It makes me think that it might somehow depend not on a number of elements inside a geometry, but on their linear size.
I suppose I should mention that OnSizeChanged I adjust the ContainerGeneral canvas' height and width and simply re-create the grid.
Checked the number of lines stored in runtime to make sure I don't have any extras. Tried using Image with DrawingVisual instead of Path. Nothing helped.
Appearances for clearer understanding
It was all about stroke dashes and WPF's unhealthy desire to count them all while getting hit test bounds for DrawingContext.
The related topic is Why does use of pens with dash patterns cause huge (!) performance degredation in WPF custom 2D drawing?

C# Chart Control serie is covered by custom label

I'm using C# chart control to draw a nyquist plot. Now i want data points appear on the curve each time the user moves the mouse on it. So i used hit test method in GetToolTipText event.
private void BodePlot_GetToolTipText(object sender, ToolTipEventArgs e)
{
HitTestResult result = BodePlot.HitTest(e.X, e.Y);
selectDataPoint = null;
if (result.ChartElementType == ChartElementType.DataPoint)
{
selectDataPoint = (DataPoint)result.Object;
e.Text = selectDataPoint.ToString();
}
{
The problem is only a part of the curve shows values, others don't. When i use e.Text = result.Object.ToString(); to get the object on which the mouse is pointing to, here what i found :
Instead of showing the data points, the text on tooltip show custom label. So i guess the reason is that the curve is covered by the labels of x and y axis.
The only solution that i found is disabling the x and y axis, with that everything works fine. But i want to keep those axes, so how can i make those labels hide under the curve.
Your analysis is likely correct. The way to go around this would be to provide HitTest() with the optional third argument which define the desired element type.
public HitTestResult HitTest (
int x,
int y,
ChartElementType requestedElement
)
This should return underlying data points even if other elements are overlapping them.

WPF Render Transform Behaving Weird

I am experiencing a weird problem with a render transform in WPF. The project I'm working on needs to display a clicked user point over an image. When the user clicks a point, a custom control is placed at the location of their click. The image should then be able to be scaled around any point using the mouse wheel, and the custom control should be translated (not scaled) to the correct location.
To do this, I follow the MouseWheel event as follows:
private void MapPositioner_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point location = Mouse.GetPosition(MainWindow.Instance.imageMap);
MainWindow.Instance.imageMap.RenderTransform = null;
ScaleTransform st = new ScaleTransform(scale + (e.Delta < 0 ? -0.2 : 0.2), scale += (e.Delta < 0 ? -0.2 : 0.2));
st.CenterX = location.X;
st.CenterY = location.Y;
TransformGroup tg = new TransformGroup();
tg.Children.Add(st);
//tg.Children.Add(tt);
MainWindow.Instance.imageMap.RenderTransform = tg;
if (scale <= 1)
{
MainWindow.Instance.imageMap.RenderTransform = null;
}
if (TransformationChanged != null)
TransformationChanged();
}
Then, I implemented an event handler in the custom control for the TransformationChanged event seen at the end of the above code block as follows:
private void Instance_TransformationChanged()
{
//check image coords
//
if (MainWindow.Instance.imageMap.RenderTransform != null)
{
if (MainWindow.Instance.imageMap.RenderTransform != Transform.Identity)
{
Transform st = MainWindow.Instance.imageMap.RenderTransform;
Point image = MainWindow.VideoOverlayCanvas.TransformToVisual(MainWindow.Instance.MapImage).Transform(loc2);
Point trans = st.Transform(image);
Point final = MainWindow.Instance.MapImage.TransformToVisual(MainWindow.VideoOverlayCanvas).Transform(trans);
// selected = anchor2;
// final = ClipToOverlay(final);
// selected = null;
connector.X2 = final.X;
connector.Y2 = final.Y;
Canvas.SetLeft(anchor2, final.X);
Canvas.SetTop(anchor2, final.Y);
}
}
else
{
connector.X2 = loc2.X;
connector.Y2 = loc2.Y;
Canvas.SetLeft(anchor2, loc2.X);
Canvas.SetTop(anchor2, loc2.Y);
}
}
This way, I can ensure that the custom control's position is updated only after the new transform is set. Note that since I am applying the transform to the point, there is no scaling done to the control, the effect is that it is translated to the point it should. This works fine as long as the user is only scaling around one point. If they change that point, it doesnt work.
Here are some images that show the problem:
User clicks a point
user zooms out, what happened here?
after zooming out (all the way out in this case) it looks ok
I've been messing with this for about two days now, so I apologize if my code looks messy. I know this is a pretty obscure question so any help would be appreciated.
Thanks,
Max
If anyone is looking for an answer to this, because of deadlines, I had to write a workaround by having the user pan with the right mouse button and zoom with the mouse wheel. This way zooming always happens around the center of the image, so the controls are always lined up. I'm still looking for answers to the original question though if anyone can figure it out
Thanks,
Max
I'm not sure what's wrong with your transform, but have you considered an alternate approach? For example, you might want to add a transparent canvas set to stay at the same size as the image, z-order above the image (explicitly set or just put the Canvas element just after the image element). Then you can just use Canvas.SetLeft and Canvas.SetTop to place the user control where the user clicked, and to move it around. A lot easier than using a transform.

Categories

Resources