SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and now iOS Charting & Android Chart Components

Welcome to the SciChart Community Forums!

Please use the forums below to ask questions about SciChart. Take a moment to read our Question asking guidelines on how to ask a good question and our support policy

We also have a tag=SciChart on Stackoverflow.com where you can earn rep for your questions!

Answered
1
0

Hello,

I have a SciChart with multiple y-axes. Thanks to the ZoomPanModifier the user can pan the whole chart by dragging (holding the left mouse button down and moving the mouse). Good! But it should also be possible for the user to pan a certain axis (meaning : changing the min and max values of the axis) and that doesn’t seem to be possible with the built-in ChartModifiers.

So then I thought, no problem, I make my own ChartModifier to handle this (I already implemented two other modifiers for other mouse interactions before, so I get the hang of this :-)). But I don’t get this to work correctly.

My first attempt was to derive from ChartModifierBase and then handle events like OnModifierMouseDown and OnModifierMouseUp. Problem there is that OnModifierMouseUp doesn’t get called when the mouse is not over the chart surface anymore. That is a problem because the user can release the mouse button when the mouse is over another part of the WPF window or even outside of the application window and somewhere else on the screen.

My second attempt was to inherit from ZoomPanModifierBase because the ZoomPanModifier handles these ‘mouseups’ correctly. I try by overriding the Pan(System.Windows.Point currentPoint, System.Windows.Point lastPoint, System.Windows.Point startPoint) method but this method only gets called when the panning starts on the chart area itself, not on one of the axes, so it doesn’t seem to be a solution either.

 public class AxisPanModifier : ZoomPanModifierBase
{
    public override void Pan(System.Windows.Point currentPoint, System.Windows.Point lastPoint, System.Windows.Point startPoint) {
        // Does not get called when the dragging starts on an axis.
    }
}

Can someone give me a tip on how to proceed or does someone already have a solution for this ? Thanks for any help in advance. 🙂

Best greetings,
Stefaan

Images
  • You must to post comments
Best Answer
1
0

Hi Stefaan,

It is possible to pan a single axis by dragging the axis. Please see the XAxisDragModifier and YAxisDragModifier. The property you need to set to enable panning on an axis is *AxisDragModifier.DragMode, which has values of Pan and Scale.

If you wanted to go one further you could create your own ChartModifier (as you have done) to pan a single axis. Please see our tutorial on Creating a Custom ZoomPanModifier. If you take this code (found in the first tutorial) and add an AxisId DependencyProperty you could make it generic to filter on a specific axis by using the ChartModifierBase.GetYAxis(string id) and GetXAxis methods.

Hope this helps!

Andrew

  • srillaert
    Hello Andrew,thanks a lot for the reply. It helps me but I am still missing a piece in the puzzle. Let me explain : The YAxisDragModifier works for one y-axis by setting an AxisId DependencyProperty. That is fine, but in my chart there is a dynamically changing collection of y-axes (2 in the screenshot but it can be any number of y-axes, i forgot to add this information to the question). I could dynamically add and remove YAxisDragModifiers I guess but then the code would get a bit convoluted.So ideally I would go for the second option, creating a custom ZoomPanModifier that would handle all the y-axes and the single x-axis. But there my problem is (as outlined in the question) that the mouse events OnModifierMouseMove and OnModifierMouseUp are not triggered when the mouse moves out of the chart area. In a way the SimpleZoomPanModifier of the tutorial is too simple and is missing some key event handling that is needed for a good implementation (when the mouse moves to another part of the WPF window or outside of the application window). It is possible to write a ChartModifier that handles these events well because that is actually what modifiers like ZoomPanModifier and YAxisDragModifier are doing but I don't know how...Best greetings, Stefaan
  • Andrew
    Yes, it is too simple (deliberately so!), but it's not too hard to fix this. Simply call ModifierSurface.CaptureMouse() in MouseDown after testing the mouse was down on the main chart area, and ModifierSurface.ReleaseMouseCapture() in MouseUp. This is what we do in our own ZoomPanModifier implementation.
  • srillaert
    Thanks a lot Andrew, ModifierSurface.CaptureMouse() and ModifierSurface.ReleaseMouseCapture() were indeed the last puzzle pieces. I still have to work a bit on my own code here but I am confident now that I can come to a good working solution for the panning of the (dynamic number of) YAxes. Thanks again for the help and a great product !
  • You must to post comments
0
0

Hello,

here my code if other people later want to have some similar dragging behavior. Works with a flexible collection of x-axes and y-axis (although I only tested with one x-axis). Please feel free to use the code.

/// <summary>
/// ChartModifier for panning through dragging the mouse, allows for panning a specific axis.
/// </summary>
/// <remarks>
/// See : https://www.scichart.com/questions/question/panning-an-axis-by-dragging-the-mouse
/// </remarks>
public class PanModifier : ChartModifierBase
{
    private class PanningState
    {
        public Point LastPoint;
        public IEnumerable<IAxis> XAxesToScroll;
        public IEnumerable<IAxis> YAxesToScroll;
    }

    private PanningState _state;

    private PanningState CreateInitialPanningState(ModifierMouseArgs e) {
        foreach (var xAxis in XAxes) {
            if (HitTestableContainsMousePoint(xAxis, e)) {
                // Dragging started on an x-axis, only pan that x-axis.
                return new PanningState {
                    LastPoint = e.MousePoint,
                    XAxesToScroll = new IAxis[] { xAxis },
                    YAxesToScroll = new IAxis[0]
                };
            }
        }
        foreach (var yAxis in YAxes) {
            if (HitTestableContainsMousePoint(yAxis, e)) {
                // Dragging started on an y-axis, only pan that y-axis.
                return new PanningState {
                    LastPoint = e.MousePoint,
                    XAxesToScroll = new IAxis[0],
                    YAxesToScroll = new IAxis[] { yAxis }
                };
            }
        }
        // Dragging started on the main chart area, pan all the axes.
        return new PanningState {
            LastPoint = e.MousePoint,
            XAxesToScroll = XAxes,
            YAxesToScroll = YAxes
        };
    }

    private bool HitTestableContainsMousePoint(IHitTestable hitTestable, ModifierMouseArgs e) {
        Rect bounds = hitTestable.GetBoundsRelativeTo(RootGrid);
        return bounds.Contains(e.MousePoint);
    }

    public override void OnModifierMouseDown(ModifierMouseArgs e) {
        base.OnModifierMouseDown(e);
        ModifierSurface.CaptureMouse();
        _state = CreateInitialPanningState(e);
        e.Handled = true;
    }

    public override void OnModifierMouseMove(ModifierMouseArgs e) {
        base.OnModifierMouseMove(e);
        if (_state == null) return;

        var currentPoint = e.MousePoint;
        var xDelta = currentPoint.X - _state.LastPoint.X;
        var yDelta = _state.LastPoint.Y - currentPoint.Y;

        using (ParentSurface.SuspendUpdates()) {
            foreach (var xAxis in _state.XAxesToScroll) {
                xAxis.Scroll(XAxis.IsHorizontalAxis ? xDelta : -yDelta, ClipMode.None);
            }

            foreach (var yAxis in _state.YAxesToScroll) {
                yAxis.Scroll(YAxis.IsHorizontalAxis ? -xDelta : yDelta, ClipMode.None);
            }
        }

        _state.LastPoint = currentPoint;
    }

    public override void OnModifierMouseUp(ModifierMouseArgs e) {
        base.OnModifierMouseUp(e);
        ModifierSurface.ReleaseMouseCapture();
        _state = null;
    }
}

Best greetings,
Stefaan

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.