I need to access the DataPoint via X coordinate, but I can't seem to find any solutions for it. HitTest() is out, since it also needs Y coordinate (cannot hit anything if Y is 0). I know I could select the DataPoints via Linq and the X coordinate, but I was wondering if there are better solutions for this?
More specifically I'm trying to show Candlestick data (Open, Low, High, Close and DateTime) with a TextAnnotation, which follows the CursorX (it's anchored above my X-axis, which is located at the bottom of my chart), and my CursorX is intervalled so it is always in the center of a candle.
What I have so far, is a MouseMove function, which updates my CursorX, but lacks the data access and label update:
PointF _mouse = new PointF();
TextAnnotation _mouseLabel;
void UpdateMouseCursor(object sender, MouseEventArgs e)
{
if( _mouseLabel == null )
return;
_mouse.X = e.Location.X;
_mouse.Y = e.Location.Y;
var cursorX = chart1.ChartAreas[0].CursorX;
cursorX.SetCursorPixelPosition( _mouse, true );
_mouseLabel.X = cursorX.Position;
}
CursorX and _mouseLabel setup:
public void SetupMouse(double interval, DateTimeIntervalType intervalType)
{
chart1.ChartAreas[0].CursorX.IntervalType = intervalType;
chart1.ChartAreas[0].CursorX.Interval = interval;
chart1.ChartAreas[0].CursorX.LineColor = Color.FromArgb(128, 128,128,128);
if( _mouseLabel == null )
{
_mouseLabel = new TextAnnotation();
_mouseLabel.Text = "WOOHOO!";
_mouseLabel.AnchorY = 85;
_mouseLabel.AxisX = chart1.ChartAreas[0].AxisX;
chart1.Annotations.Add( _mouseLabel );
}
}
The visuals:
Related
Windows Forms ZedGraph control in WPF application. The data points are auto-generated and attached to the chart every N seconds. When new data point is added to the chart I shift (pan) chart one point to the left, so there is always no more than last 50 points visible in the viewport. Overall, it works pretty good, except for two things.
Issues
If user tries to zoom in or out, the viewport stops following the last data point and chart goes outside of the screen, so panning stops working
I would like to pan or shift chart using mouse event, without scrolling, but when I press right mouse button and try to move it to the left, it tries to zoom chart instead of panning
protected void CreateChart(ZedGraphControl control)
{
_rand = new Random();
var curve = control.GraphPane.AddJapaneseCandleStick("Demo", new StockPointList());
curve.Stick.IsAutoSize = true;
curve.Stick.Color = Color.Blue;
control.AutoScroll = true; // Always shift to the last data point when new data comes in
control.IsEnableHPan = true; // I assume this should allow me to move chart to the left using mouse
control.IsEnableVPan = true;
control.IsEnableHZoom = true;
control.IsEnableVZoom = true;
control.IsShowPointValues = true;
control.IsShowHScrollBar = false;
control.IsShowVScrollBar = false;
control.IsAutoScrollRange = true; // Always shift to the last data point when new data comes in
control.IsZoomOnMouseCenter = false;
control.GraphPane.XAxis.Type = AxisType.DateAsOrdinal;
control.AxisChange();
control.Invalidate();
var aTimer = new Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTime);
aTimer.Interval = 100;
aTimer.Enabled = true;
}
protected XDate _xDate = new XDate(2006, 2, 1);
protected double _open = 50.0;
protected Random _rand = new Random();
// Auto generate data points
protected void OnTime(object source, ElapsedEventArgs e)
{
var control = FormCharts;
var x = _xDate.XLDate;
var close = _open + _rand.NextDouble() * 10.0 - 5.0;
var hi = Math.Max(_open, close) + _rand.NextDouble() * 5.0;
var low = Math.Min(_open, close) - _rand.NextDouble() * 5.0;
var pt = new StockPt(x, hi, low, _open, close, 100000);
_open = close;
_xDate.AddDays(1.0);
if (XDate.XLDateToDayOfWeek(_xDate.XLDate) == 6)
{
_xDate.AddDays(2.0);
}
(control.GraphPane.CurveList[0].Points as StockPointList).Add(pt);
control.GraphPane.XAxis.Scale.Min = control.GraphPane.XAxis.Scale.Max - 50; // Hide all points except last 50, after mouse zooming this line stops working
//control.GraphPane.XAxis.Scale.Max = control.GraphPane.XAxis.Scale.Max + 1;
control.AxisChange();
control.Invalidate();
}
Kind of solved it.
First issue with broken auto-scroll after zooming
It happens because zooming sets these parameters to FALSE.
area.XAxis.Scale.MinAuto = false;
area.XAxis.Scale.MaxAuto = false;
To fix it, either set it back to TRUE every time new data point comes. Another way to fix it, is to keep them always as FALSE and move chart manually
protected void MoveChart(GraphPane pane, int pointsToMove, int pointsToShow)
{
pane.XAxis.Scale.Max = pane.XAxis.Scale.Max - pointsToMove;
pane.XAxis.Scale.Min = pane.XAxis.Scale.Max - Math.Abs(pointsToShow);
}
...
// Example : shift one point to the left and show only 50 last points
MoveChart(control.MasterPane.PaneList["Quotes"], -1, 50);
Second issue, implementing custom panning without scrollbar using mouse events.
protected int _mouseX = -1;
protected int _mouseY = -1;
...
control.MouseUpEvent += OnMouseUp;
control.MouseDownEvent += OnMouseDown;
control.MouseMoveEvent += OnMouseMove;
...
// Example : remember X and Y on mouse down and move chart until mouse up event
protected bool OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
_mouseX = -1; // unset X on mouse up
_mouseY = -1;
return true;
}
protected bool OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
_mouseX = e.X; // remember last X on mouse down
_mouseY = e.Y;
return true;
}
protected bool OnMouseMove(ZedGraphControl sender, System.Windows.Forms.MouseEventArgs e)
{
if (_mouseX >= 0) // if X was saved after mouse down
{
foreach (var pane in sender.MasterPane.PaneList) // move synced chart panels
{
MoveChart(pane, _mouseX > e.X ? -1 : 1, 50); // if mouse move is increasing X, move chart to the right, and vice versa
}
_mouseX = e.X; // update X to new position
_mouseY = e.Y;
}
return true;
}
I'm trying to figure out how to auto scroll a scrollview when a user drags an item to the edge of the screen. I'm really not getting the behavious I expect out of this code. Does anyone have an example or a technique that works?
I have this in my:
onDrag(View v, DragEvent e)
{
case DragAction.Location:
var currentPosition = (int)e.GetX();
var point = GetTouchPositionFromDragEvent(v, e);
System.Diagnostics.Debug.WriteLine($"DragAction.Location from {v.GetType()} => {currentPosition}");
System.Diagnostics.Debug.WriteLine($"DragAction.GLOBAL from {v.GetType()} => {point.X}");
if (point.X > App.AppScreenWidth - 50)
{
_hostScrollView.ScrollToAsync(_hostScrollView.ScrollX + 30, 0, true);
}
if (point.X < 50)
{
_hostScrollView.ScrollToAsync(_hostScrollView.ScrollX - 30, 0, true);
}
}
public static Point GetTouchPositionFromDragEvent(View item, DragEvent e)
{
Rect rItem = new Rect();
item.GetGlobalVisibleRect(rItem);
return new Point(rItem.Left + (int)Math.Round(e.GetX()), rItem.Top + (int)Math.Round(e.GetY()));
}
This also has the knock on effect of only scrolling in one direction strangely and also requires the user to keep moving the item in order to fire the events which leads me to think this is entirely the wrong place to be even trying to do this scrolling.
Any pointers or nudges in the right direction would be really appreciated.
In your Activity
public class MainActivity : AppCompatActivity, View.IOnDragListener,View.IOnScrollChangeListener
private int mScrollDistance;
ScrollView _hostScrollView= FindViewById<ScrollView>(Resource.Id.xxx);
_hostScrollView.SetOnScrollChangeListener(this);
public bool OnDrag(View v, DragEvent e)
{
var action = e.Action;
var scrollView = v as ScrollView;
switch (action)
{
case DragAction.Started:
break;
case DragAction.Location:
int y = Java.Lang.Math.Round(e.GetY());
int translatedY = y - mScrollDistance;
int threshold = 50;
// make a scrolling up due the y has passed the threshold
if (translatedY < threshold)
{
// make a scroll up by 30 px
scrollView.ScrollBy(0, -30);
}
// make a autoscrolling down due y has passed the 500 px border
if (translatedY + threshold > 500)
{
// make a scroll down by 30 px
scrollView.ScrollBy(0, 30);
}
break;
}
return true;
}
public void OnScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
{
var scrollView = v as ScrollView;
mScrollDistance = scrollView.ScrollY;
}
You could modify the logic as you want if I misunderstand your requirement .
I am trying to display the X and Y coordinates of the chart data in a small display. Everything works well but the data shown isn't accurate.
Here is the code below:
var results = chart1.HitTest(e.X, e.Y, false, ChartElementType.PlottingArea);
foreach (var result in results)
{
if (result.ChartElementType == ChartElementType.PlottingArea)
{
yValue = chart1.ChartAreas[0].AxisY2.PixelPositionToValue(e.Y);
xValue = chart1.ChartAreas[0].AxisX2.PixelPositionToValue(e.X);
}
}
if (OverlapcheckBox1.Checked)
{
int val = Convert.ToInt16(yValue / 24);
yValue = yValue - 24 * val;
}
if (Cursor1checkBox.Checked && ClickMouse)
{
V1textBox1.Text = string.Concat(string.Concat(yValue).ToString());
}
if (Cursor2checkBox.Checked && ClickMouse)
{
V2textBox2.Text = string.Concat(string.Concat(yValue).ToString());
}
The image shows cursor at 10 but the value in V1 is 9.88
And an image:
Unless your mouse has amazing accuracy you would never see an exact 10.000. You could round it off:
private void Chart1_MouseClick(object sender, MouseEventArgs e) {
double yValue = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);
yValue = Math.Round(yValue, 0);
}
Or perhaps you wish to find DataPoints near the cursor click position?
private void Chart1_MouseClick(object sender, MouseEventArgs e) {
HitTestResult result = chart1.HitTest(e.X, e.Y);
if (result.ChartElementType == ChartElementType.DataPoint) {
DataPoint point = (DataPoint)result.Object;
double yValue = point.YValues[0];
}
}
I have a FlowLayoutPanel which can be scrolled by the user on the verticle axis. i have the following event handler which is used to see where the user has scrolled to and provides the position number within blocks of 405px:
private void ChangedParentFlowPanel_Scroll(object sender, ScrollEventArgs e)
{
int NewPos = e.NewValue;
int range = (NewPos - 1) / 405 + 1;
CurrentIndex_Changed = range;
tCounter.Text = CurrentIndex_Changed.ToString();
}
That works just fine and does exactly what i need when the user scrolls using the verticle bar. The problem i have is i need to update tCounter with the same value but this time when the user scrolls using the mouse wheel. I'Ve tried the following but this only ever seems to provide the Y axis value for the location of the mouse when it scrolls and not the location of the scroll itself:
private void ChangedParentFlowPanel_Wheel(object sender, MouseEventArgs e)
{
int NewPos = e.Location.Y;
MessageBox.Show(NewPos.ToString());
int range = (NewPos - 1) / 405 + 1;
CurrentIndex_Changed = range;
tCounter.Text = CurrentIndex_Changed.ToString();
}
The question is...how can i get the scroll position of the scroll in ChangedParentFlowPanel when a mousewheel is used?
Thanks to the comment by Hans Pasant here's the answer:
private void ChangedParentFlowPanel_Wheel(object sender, MouseEventArgs e)
{
int NewPos = Math.Abs(ChangedParentFlowPanel.AutoScrollPosition.Y);
int range = (NewPos - 1) / 405 + 1;
CurrentIndex_Changed = range;
tCounter.Text = CurrentIndex_Changed.ToString();
}
Math.Abs because the returned scroll Y value is minus and i need a positive value.
I have still have a problem with C# Chart (in System.Windows.Forms.DataVisualization.Charting namespace).
When i zoom on Y axis, sometimes, interval line aren't redrawn.
Look at the picture below, interval lines are missing under 0.Missing interval line
Usually, there is no problem and 11 interval lines are drawn.
private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
{
if ((e != null) && (sender != null))
{
// on ne récupère que les évènement concernant l'axe Y. L'axe X se gère tout seul car il est unique
if (e.Axis.AxisName.ToString() == "Y")
{
// AxisY.ScaleView.ViewMinimum is the minimum Y axis value displayed. It contains the offset to apply to label, grid and interval on Y axis
e.ChartArea.AxisY.IntervalOffset = (e.ChartArea.AxisY.Interval - e.ChartArea.AxisY.ScaleView.ViewMinimum) % e.ChartArea.AxisY.Interval;
// To update Y axis label, major grid and interval.
e.ChartArea.AxisY.Interval = (e.ChartArea.AxisY.ScaleView.ViewMaximum - e.ChartArea.AxisY.ScaleView.ViewMinimum) / 11;
}
}
}
Above is my axisViewChanged event.
The Y axis is declared as below
chartArea1.AxisY.Interval = (sens.dMaximumValue - sens.dMinimumValue)/11.0;
chartArea1.AxisY.IntervalOffsetType = DateTimeIntervalType.Number;
chartArea1.AxisY.IntervalType = DateTimeIntervalType.Number;
chartArea1.AxisY.LabelStyle.Format = ".000";
chartArea1.AxisY.MajorGrid.LineColor = Color.White; // Color.DarkViolet;
chartArea1.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dash;
chartArea1.AxisY.MajorTickMark.Enabled = false;
chartArea1.AxisY.Maximum = sens.dMaximumValue*1.1;
chartArea1.AxisY.Minimum = sens.dMinimumValue*1.1;
chartArea1.AxisY.ScaleView.MinSize = 0.01D;
chartArea1.AxisY.ScaleView.SmallScrollMinSize = 0.001D;
chartArea1.AxisY.ScrollBar.BackColor = Color.White;
chartArea1.AxisY.ScrollBar.ButtonColor = Color.LightGray;
As you can see, it is declared as Number, which is correct i think.
When this problem oocurs, if i click on the scrollbar, the 11 interval are correctly redrawn.
Has something solved this problem ?