Search code examples
c#zedgraph

Zooming with mouse wheel breaks panning (scrolling) of real time data in ZedGraph


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

  1. 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
  2. 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();
}

Solution

  • 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;
    }